From 9e1f4282f669a892d0a5d662b6f36b79ed3275d0 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sun, 13 Mar 2011 19:34:25 +0100 Subject: [PATCH] Demoting collection support, using latest couchrest, ensuring reduce always included when needed --- Gemfile.lock | 12 ++-- couchrest_model.gemspec | 2 +- history.txt | 7 +- lib/couchrest/model/views.rb | 20 +++--- spec/couchrest/collection_spec.rb | 89 ++++++++++++++++++++++++ spec/couchrest/view_spec.rb | 110 ++++++------------------------ spec/fixtures/more/article.rb | 2 +- spec/fixtures/more/course.rb | 6 +- 8 files changed, 135 insertions(+), 113 deletions(-) create mode 100644 spec/couchrest/collection_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 4fe2a62..29e8b1c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ PATH specs: couchrest_model (1.1.0.beta) activemodel (~> 3.0.0) - couchrest (~> 1.0.1) + couchrest (~> 1.0.2) mime-types (~> 1.15) railties (~> 3.0.0) tzinfo (~> 0.3.22) @@ -28,10 +28,10 @@ GEM i18n (~> 0.4) activesupport (3.0.4) builder (2.1.2) - couchrest (1.0.1) - json (>= 1.4.6) - mime-types (>= 1.15) - rest-client (>= 1.5.1) + couchrest (1.0.2) + json (~> 1.5.1) + mime-types (~> 1.15) + rest-client (~> 1.6.1) diff-lcs (1.1.2) erubis (2.6.6) abstract (>= 1.0.0) @@ -67,7 +67,7 @@ PLATFORMS DEPENDENCIES activemodel (~> 3.0.0) - couchrest (~> 1.0.1) + couchrest (~> 1.0.2) couchrest_model! mime-types (~> 1.15) rack-test (>= 0.5.7) diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index 6a25519..76a1fba 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_dependency(%q, "~> 1.0.1") + s.add_dependency(%q, "~> 1.0.2") s.add_dependency(%q, "~> 1.15") s.add_dependency(%q, "~> 3.0.0") s.add_dependency(%q, "~> 0.3.22") diff --git a/history.txt b/history.txt index 6d91715..1cb3302 100644 --- a/history.txt +++ b/history.txt @@ -6,9 +6,10 @@ * Minor enhancements: * 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) - * Fix validate uniqueness test that was never executed (thanks Simone Carletti) - * Adds a #reload method to reload document attributes (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) + * 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 diff --git a/lib/couchrest/model/views.rb b/lib/couchrest/model/views.rb index a7bdd92..6157102 100644 --- a/lib/couchrest/model/views.rb +++ b/lib/couchrest/model/views.rb @@ -85,9 +85,14 @@ module CouchRest end # returns stored defaults if there is a view named this in the design doc - def has_view?(view) - view = view.to_s - design_doc && design_doc['views'] && design_doc['views'][view] + def has_view?(name) + design_doc && design_doc.has_view?(name) + 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 # Dispatches to any named view. @@ -127,12 +132,9 @@ module CouchRest if raw || (opts.has_key?(:include_docs) && opts[:include_docs] == false) fetch_view(db, name, opts, &block) else - if block.nil? - collection_proxy_for(design_doc, name, opts.merge({:database => db, :include_docs => true})) - else - view = fetch_view db, name, opts.merge({:include_docs => true}), &block - view['rows'].collect{|r|build_from_database(r['doc'])} if view['rows'] - end + opts = opts.merge(:include_docs => true) + view = fetch_view db, name, opts, &block + view['rows'].collect{|r| build_from_database(r['doc'])} if view['rows'] end end diff --git a/spec/couchrest/collection_spec.rb b/spec/couchrest/collection_spec.rb new file mode 100644 index 0000000..89822e3 --- /dev/null +++ b/spec/couchrest/collection_spec.rb @@ -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 diff --git a/spec/couchrest/view_spec.rb b/spec/couchrest/view_spec.rb index 59ab062..c276b56 100644 --- a/spec/couchrest/view_spec.rb +++ b/spec/couchrest/view_spec.rb @@ -27,6 +27,20 @@ describe "Model views" do 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 describe "a model with simple views and a default param" do @@ -184,6 +198,11 @@ describe "Model views" do course.title.should eql('bbb') 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 describe "a ducktype view" do @@ -374,96 +393,5 @@ describe "Model views" do Article.design_doc["views"].keys.should include("by_updated_at") 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 diff --git a/spec/fixtures/more/article.rb b/spec/fixtures/more/article.rb index ef8ce2a..9e370e3 100644 --- a/spec/fixtures/more/article.rb +++ b/spec/fixtures/more/article.rb @@ -18,7 +18,7 @@ class Article < CouchRest::Model::Base :reduce => "function(keys, values, rereduce) { return sum(values); - }" + }" property :date, Date property :slug, :read_only => true diff --git a/spec/fixtures/more/course.rb b/spec/fixtures/more/course.rb index 23e52cd..d06f6a0 100644 --- a/spec/fixtures/more/course.rb +++ b/spec/fixtures/more/course.rb @@ -13,13 +13,15 @@ class Course < CouchRest::Model::Base property :hours, Integer property :profit, BigDecimal property :started_on, :type => Date - property :updated_at, :type => DateTime + property :updated_at, DateTime property :active, :type => TrueClass property :very_active, :type => TrueClass property :klass, :type => Class - + view_by :title view_by :title, :active 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