Demoting collection support, using latest couchrest, ensuring reduce always included when needed

This commit is contained in:
Sam Lown 2011-03-13 19:34:25 +01:00
parent d1baf99324
commit 9e1f4282f6
8 changed files with 135 additions and 113 deletions

View file

@ -3,7 +3,7 @@ PATH
specs: specs:
couchrest_model (1.1.0.beta) couchrest_model (1.1.0.beta)
activemodel (~> 3.0.0) activemodel (~> 3.0.0)
couchrest (~> 1.0.1) couchrest (~> 1.0.2)
mime-types (~> 1.15) mime-types (~> 1.15)
railties (~> 3.0.0) railties (~> 3.0.0)
tzinfo (~> 0.3.22) tzinfo (~> 0.3.22)
@ -28,10 +28,10 @@ GEM
i18n (~> 0.4) i18n (~> 0.4)
activesupport (3.0.4) activesupport (3.0.4)
builder (2.1.2) builder (2.1.2)
couchrest (1.0.1) couchrest (1.0.2)
json (>= 1.4.6) json (~> 1.5.1)
mime-types (>= 1.15) mime-types (~> 1.15)
rest-client (>= 1.5.1) rest-client (~> 1.6.1)
diff-lcs (1.1.2) diff-lcs (1.1.2)
erubis (2.6.6) erubis (2.6.6)
abstract (>= 1.0.0) abstract (>= 1.0.0)
@ -67,7 +67,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
activemodel (~> 3.0.0) activemodel (~> 3.0.0)
couchrest (~> 1.0.1) couchrest (~> 1.0.2)
couchrest_model! couchrest_model!
mime-types (~> 1.15) mime-types (~> 1.15)
rack-test (>= 0.5.7) rack-test (>= 0.5.7)

View file

@ -23,7 +23,7 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"] s.require_paths = ["lib"]
s.add_dependency(%q<couchrest>, "~> 1.0.1") s.add_dependency(%q<couchrest>, "~> 1.0.2")
s.add_dependency(%q<mime-types>, "~> 1.15") s.add_dependency(%q<mime-types>, "~> 1.15")
s.add_dependency(%q<activemodel>, "~> 3.0.0") s.add_dependency(%q<activemodel>, "~> 3.0.0")
s.add_dependency(%q<tzinfo>, "~> 0.3.22") s.add_dependency(%q<tzinfo>, "~> 0.3.22")

View file

@ -6,9 +6,10 @@
* Minor enhancements: * Minor enhancements:
* A yield parameter in an anonymous casted model property block is no longer required (@samlown) * A yield parameter in an anonymous casted model property block is no longer required (@samlown)
* Narrow the rescued exception to avoid catching class evaluation errors that has nothing to to with the association (thanks Simone Carletti) * Narrow the rescued exception to avoid catching class evaluation errors that has nothing to to with the association (thanks Simone Carletti)
* Fix validate uniqueness test that was never executed (thanks Simone Carletti) * Fix validate uniqueness test that was never executed (thanks Simone Carletti)
* Adds a #reload method to reload document attributes (thanks Simone Carletti) * Adds a #reload method to reload document attributes (thanks Simone Carletti)
* CollectionProxy no longer provided by default with simple views (pending deprication)
== CouchRest Model 1.0.0 == CouchRest Model 1.0.0

View file

@ -85,9 +85,14 @@ module CouchRest
end end
# returns stored defaults if there is a view named this in the design doc # returns stored defaults if there is a view named this in the design doc
def has_view?(view) def has_view?(name)
view = view.to_s design_doc && design_doc.has_view?(name)
design_doc && design_doc['views'] && design_doc['views'][view] end
# Check if the view can be reduced by checking to see if it has a
# reduce function.
def can_reduce_view?(name)
design_doc && design_doc.can_reduce_view?(name)
end end
# Dispatches to any named view. # Dispatches to any named view.
@ -127,12 +132,9 @@ module CouchRest
if raw || (opts.has_key?(:include_docs) && opts[:include_docs] == false) if raw || (opts.has_key?(:include_docs) && opts[:include_docs] == false)
fetch_view(db, name, opts, &block) fetch_view(db, name, opts, &block)
else else
if block.nil? opts = opts.merge(:include_docs => true)
collection_proxy_for(design_doc, name, opts.merge({:database => db, :include_docs => true})) view = fetch_view db, name, opts, &block
else view['rows'].collect{|r| build_from_database(r['doc'])} if view['rows']
view = fetch_view db, name, opts.merge({:include_docs => true}), &block
view['rows'].collect{|r|build_from_database(r['doc'])} if view['rows']
end
end end
end end

View file

@ -0,0 +1,89 @@
require File.expand_path("../../spec_helper", __FILE__)
require File.join(FIXTURE_PATH, 'more', 'article')
describe "Collections" do
before(:all) do
reset_test_db!
Article.refresh_design_doc
titles = ["very uniq one", "really interesting", "some fun",
"really awesome", "crazy bob", "this rocks", "super rad"]
titles.each_with_index do |title,i|
a = Article.new(:title => title, :date => Date.today)
a.save
end
titles = ["yesterday very uniq one", "yesterday really interesting", "yesterday some fun",
"yesterday really awesome", "yesterday crazy bob", "yesterday this rocks"]
titles.each_with_index do |title,i|
a = Article.new(:title => title, :date => Date.today - 1)
a.save
end
end
it "should return a proxy that looks like an array of 7 Article objects" do
articles = Article.collection_proxy_for('Article', 'by_date', :descending => true,
:key => Date.today, :include_docs => true)
articles.class.should == Array
articles.size.should == 7
end
it "should provide a class method for paginate" do
articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :descending => true, :key => Date.today, :include_docs => true)
articles.size.should == 3
articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
articles.size.should == 3
articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :page => 3, :descending => true, :key => Date.today, :include_docs => true)
articles.size.should == 1
end
it "should provide a class method for paginated_each" do
options = { :design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :page => 1, :descending => true, :key => Date.today,
:include_docs => true }
Article.paginated_each(options) do |a|
a.should_not be_nil
end
end
it "should provide a class method to get a collection for a view" do
articles = Article.find_all_article_details(:key => Date.today)
articles.class.should == Array
articles.size.should == 7
end
it "should get a subset of articles using paginate" do
articles = Article.collection_proxy_for('Article', 'by_date', :key => Date.today, :include_docs => true)
articles.paginate(:page => 1, :per_page => 3).size.should == 3
articles.paginate(:page => 2, :per_page => 3).size.should == 3
articles.paginate(:page => 3, :per_page => 3).size.should == 1
end
it "should get all articles, a few at a time, using paginated each" do
articles = Article.collection_proxy_for('Article', 'by_date', :key => Date.today, :include_docs => true)
articles.paginated_each(:per_page => 3) do |a|
a.should_not be_nil
end
end
it "should raise an exception if design_doc is not provided" do
lambda{Article.collection_proxy_for(nil, 'by_date')}.should raise_error
lambda{Article.paginate(:view_name => 'by_date')}.should raise_error
end
it "should raise an exception if view_name is not provided" do
lambda{Article.collection_proxy_for('Article', nil)}.should raise_error
lambda{Article.paginate(:design_doc => 'Article')}.should raise_error
end
it "should be able to span multiple keys" do
articles = Article.collection_proxy_for('Article', 'by_date', :startkey => Date.today - 1, :endkey => Date.today, :include_docs => true)
articles.paginate(:page => 1, :per_page => 3).size.should == 3
articles.paginate(:page => 3, :per_page => 3).size.should == 3
articles.paginate(:page => 5, :per_page => 3).size.should == 1
end
it "should pass database parameter to pager" do
proxy = mock(:proxy)
proxy.stub!(:paginate)
::CouchRest::Model::Collection::CollectionProxy.should_receive(:new).with('database', anything(), anything(), anything(), anything()).and_return(proxy)
Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :database => 'database')
end
end

View file

@ -27,6 +27,20 @@ describe "Model views" do
end end
end end
describe "#has_view?" do
it "should check the design doc" do
Article.design_doc.should_receive(:has_view?).with(:test).and_return(true)
Article.has_view?(:test).should be_true
end
end
describe "#can_reduce_view?" do
it "should check if view has a reduce method" do
Article.design_doc.should_receive(:can_reduce_view?).with(:test).and_return(true)
Article.can_reduce_view?(:test).should be_true
end
end
end end
describe "a model with simple views and a default param" do describe "a model with simple views and a default param" do
@ -184,6 +198,11 @@ describe "Model views" do
course.title.should eql('bbb') course.title.should eql('bbb')
end end
it "should perform a search for first when reduce method present" do
course = Course.first_from_view('by_active')
course.should_not be_nil
end
end end
describe "a ducktype view" do describe "a ducktype view" do
@ -375,95 +394,4 @@ describe "Model views" do
end end
end end
describe "with a collection" do
before(:all) do
reset_test_db!
titles = ["very uniq one", "really interesting", "some fun",
"really awesome", "crazy bob", "this rocks", "super rad"]
titles.each_with_index do |title,i|
a = Article.new(:title => title, :date => Date.today)
a.save
end
titles = ["yesterday very uniq one", "yesterday really interesting", "yesterday some fun",
"yesterday really awesome", "yesterday crazy bob", "yesterday this rocks"]
titles.each_with_index do |title,i|
a = Article.new(:title => title, :date => Date.today - 1)
a.save
end
end
require 'date'
it "should return a proxy that looks like an array of 7 Article objects" do
articles = Article.by_date :key => Date.today
articles.class.should == Array
articles.size.should == 7
end
it "should get a subset of articles using paginate" do
articles = Article.by_date :key => Date.today
articles.paginate(:page => 1, :per_page => 3).size.should == 3
articles.paginate(:page => 2, :per_page => 3).size.should == 3
articles.paginate(:page => 3, :per_page => 3).size.should == 1
end
it "should get all articles, a few at a time, using paginated each" do
articles = Article.by_date :key => Date.today
articles.paginated_each(:per_page => 3) do |a|
a.should_not be_nil
end
end
it "should provide a class method to access the collection directly" do
articles = Article.collection_proxy_for('Article', 'by_date', :descending => true,
:key => Date.today, :include_docs => true)
articles.class.should == Array
articles.size.should == 7
end
it "should provide a class method for paginate" do
articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :descending => true, :key => Date.today, :include_docs => true)
articles.size.should == 3
articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
articles.size.should == 3
articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :page => 3, :descending => true, :key => Date.today, :include_docs => true)
articles.size.should == 1
end
it "should provide a class method for paginated_each" do
options = { :design_doc => 'Article', :view_name => 'by_date',
:per_page => 3, :page => 1, :descending => true, :key => Date.today,
:include_docs => true }
Article.paginated_each(options) do |a|
a.should_not be_nil
end
end
it "should provide a class method to get a collection for a view" do
articles = Article.find_all_article_details(:key => Date.today)
articles.class.should == Array
articles.size.should == 7
end
it "should raise an exception if design_doc is not provided" do
lambda{Article.collection_proxy_for(nil, 'by_date')}.should raise_error
lambda{Article.paginate(:view_name => 'by_date')}.should raise_error
end
it "should raise an exception if view_name is not provided" do
lambda{Article.collection_proxy_for('Article', nil)}.should raise_error
lambda{Article.paginate(:design_doc => 'Article')}.should raise_error
end
it "should be able to span multiple keys" do
articles = Article.by_date :startkey => Date.today, :endkey => Date.today - 1
articles.paginate(:page => 1, :per_page => 3).size.should == 3
articles.paginate(:page => 2, :per_page => 3).size.should == 3
articles.paginate(:page => 3, :per_page => 3).size.should == 3
articles.paginate(:page => 4, :per_page => 3).size.should == 3
articles.paginate(:page => 5, :per_page => 3).size.should == 1
end
it "should pass database parameter to pager" do
proxy = mock(:proxy)
proxy.stub!(:paginate)
::CouchRest::Model::Collection::CollectionProxy.should_receive(:new).with('database', anything(), anything(), anything(), anything()).and_return(proxy)
Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :database => 'database')
end
end
end end

View file

@ -13,7 +13,7 @@ class Course < CouchRest::Model::Base
property :hours, Integer property :hours, Integer
property :profit, BigDecimal property :profit, BigDecimal
property :started_on, :type => Date property :started_on, :type => Date
property :updated_at, :type => DateTime property :updated_at, DateTime
property :active, :type => TrueClass property :active, :type => TrueClass
property :very_active, :type => TrueClass property :very_active, :type => TrueClass
property :klass, :type => Class property :klass, :type => Class
@ -22,4 +22,6 @@ class Course < CouchRest::Model::Base
view_by :title, :active view_by :title, :active
view_by :dept, :ducktype => true view_by :dept, :ducktype => true
view_by :active, :map => "function(d) { if (d['#{model_type_key}'] == 'Course' && d['active']) { emit(d['updated_at'], 1); }}", :reduce => "function(k,v,r) { return sum(v); }"
end end