From ada5b1660f3e134646e8334b6cc3b7d03e7fbf28 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Thu, 26 Mar 2009 21:21:20 +0000 Subject: [PATCH 1/7] Move design_doc attributes to Mixins::DesignDoc --- lib/couchrest/mixins/design_doc.rb | 3 +++ lib/couchrest/mixins/views.rb | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/couchrest/mixins/design_doc.rb b/lib/couchrest/mixins/design_doc.rb index 4f016ed..510d145 100644 --- a/lib/couchrest/mixins/design_doc.rb +++ b/lib/couchrest/mixins/design_doc.rb @@ -6,6 +6,9 @@ module CouchRest def self.included(base) base.extend(ClassMethods) + base.send(:extlib_inheritable_accessor, :design_doc) + base.send(:extlib_inheritable_accessor, :design_doc_slug_cache) + base.send(:extlib_inheritable_accessor, :design_doc_fresh) end module ClassMethods diff --git a/lib/couchrest/mixins/views.rb b/lib/couchrest/mixins/views.rb index 852afd5..a58ddf0 100644 --- a/lib/couchrest/mixins/views.rb +++ b/lib/couchrest/mixins/views.rb @@ -4,9 +4,6 @@ module CouchRest def self.included(base) base.extend(ClassMethods) - base.send(:extlib_inheritable_accessor, :design_doc) - base.send(:extlib_inheritable_accessor, :design_doc_slug_cache) - base.send(:extlib_inheritable_accessor, :design_doc_fresh) end module ClassMethods From f9278a4ca6d4ea5e29de850f05cca041f0a72979 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Thu, 26 Mar 2009 21:39:36 +0000 Subject: [PATCH 2/7] Typo in comment --- lib/couchrest/mixins/views.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/couchrest/mixins/views.rb b/lib/couchrest/mixins/views.rb index a58ddf0..61dccb3 100644 --- a/lib/couchrest/mixins/views.rb +++ b/lib/couchrest/mixins/views.rb @@ -145,7 +145,7 @@ module CouchRest retryable = true begin design_doc.view(view_name, opts, &block) - # the design doc could have been deleted by a rouge process + # the design doc could have been deleted by a rogue process rescue RestClient::ResourceNotFound => e if retryable refresh_design_doc From ec7848b783637cb44bb4b684e8e104f64bb1d55b Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 27 Mar 2009 11:27:37 +0000 Subject: [PATCH 3/7] Multiple database support for ExtendedDocument. New optional parameters are available to select the database: Mixins::DocumentQueries * get , * all :database => * first :database => Mixins::Views * view , :database => * all_design_doc_versions * cleanup_design_docs! Mixins::DesignDoc * refresh_design_doc now only updates the design_doc _id and removes _rev * call save_design_doc to save and update the design_doc * call save_design_doc_on to save the design doc on a given database without modifying the model's design_doc object Design (core/design.rb) * new method view_on , ... Bug fixes: * design_doc_slug in mixins/design_doc.rb was using an empty document to calculate the slug each time * method_missing in core/extended_document.rb now passes a block through --- lib/couchrest/core/design.rb | 24 ++--- lib/couchrest/mixins/design_doc.rb | 39 ++++++-- lib/couchrest/mixins/document_queries.rb | 4 +- lib/couchrest/mixins/views.rb | 36 ++++--- lib/couchrest/more/extended_document.rb | 4 +- spec/couchrest/core/design_spec.rb | 7 ++ spec/couchrest/more/extended_doc_view_spec.rb | 99 +++++++++++++++++-- 7 files changed, 158 insertions(+), 55 deletions(-) diff --git a/lib/couchrest/core/design.rb b/lib/couchrest/core/design.rb index f9b8e22..e9d1146 100644 --- a/lib/couchrest/core/design.rb +++ b/lib/couchrest/core/design.rb @@ -35,11 +35,17 @@ JAVASCRIPT end # Dispatches to any named view. + # (using the database where this design doc was saved) def view view_name, query={}, &block + view_on database, view_name, query, &block + end + + # Dispatches to any named view in a specific database + def view_on db, view_name, query={}, &block view_name = view_name.to_s view_slug = "#{name}/#{view_name}" defaults = (self['views'][view_name] && self['views'][view_name]["couchrest-defaults"]) || {} - fetch_view(view_slug, defaults.merge(query), &block) + db.view(view_slug, defaults.merge(query), &block) end def name @@ -64,22 +70,6 @@ JAVASCRIPT (self['views'][view]["couchrest-defaults"]||{}) end - # def fetch_view_with_docs name, opts, raw=false, &block - # if raw - # fetch_view name, opts, &block - # else - # begin - # view = fetch_view name, opts.merge({:include_docs => true}), &block - # view['rows'].collect{|r|new(r['doc'])} if view['rows'] - # rescue - # # fallback for old versions of couchdb that don't - # # have include_docs support - # view = fetch_view name, opts, &block - # view['rows'].collect{|r|new(database.get(r['id']))} if view['rows'] - # end - # end - # end - def fetch_view view_name, opts, &block database.view(view_name, opts, &block) end diff --git a/lib/couchrest/mixins/design_doc.rb b/lib/couchrest/mixins/design_doc.rb index 510d145..0251815 100644 --- a/lib/couchrest/mixins/design_doc.rb +++ b/lib/couchrest/mixins/design_doc.rb @@ -19,7 +19,7 @@ module CouchRest def design_doc_slug return design_doc_slug_cache if (design_doc_slug_cache && design_doc_fresh) funcs = [] - design_doc ||= Design.new(default_design_doc) + self.design_doc ||= Design.new(default_design_doc) design_doc['views'].each do |name, view| funcs << "#{name}/#{view['map']}#{view['reduce']}" end @@ -43,21 +43,42 @@ module CouchRest end def refresh_design_doc - did = design_doc_id - saved = database.get(did) rescue nil + design_doc['_id'] = design_doc_id + design_doc.delete('_rev') + #design_doc.database = nil + self.design_doc_fresh = true + end + + # Save the design doc onto the default database, and update the + # design_doc attribute + def save_design_doc + refresh_design_doc unless design_doc_fresh + self.design_doc = update_design_doc(design_doc) + end + + # Save the design doc onto a target database in a thread-safe way, + # not modifying the model's design_doc + def save_design_doc_on(db) + update_design_doc(Design.new(design_doc), db) + end + + private + + # Writes out a design_doc to a given database, returning the + # updated design doc + def update_design_doc(design_doc, db = database) + saved = db.get(design_doc['_id']) rescue nil if saved design_doc['views'].each do |name, view| saved['views'][name] = view end - database.save_doc(saved) - self.design_doc = saved + db.save_doc(saved) + saved else - design_doc['_id'] = did - design_doc.delete('_rev') - design_doc.database = database + design_doc.database = db design_doc.save + design_doc end - self.design_doc_fresh = true end end # module ClassMethods diff --git a/lib/couchrest/mixins/document_queries.rb b/lib/couchrest/mixins/document_queries.rb index 01d97bf..ff05ee2 100644 --- a/lib/couchrest/mixins/document_queries.rb +++ b/lib/couchrest/mixins/document_queries.rb @@ -36,8 +36,8 @@ module CouchRest end # Load a document from the database by id - def get(id) - doc = database.get id + def get(id, db = database) + doc = db.get id new(doc) end diff --git a/lib/couchrest/mixins/views.rb b/lib/couchrest/mixins/views.rb index 61dccb3..f6d8b7f 100644 --- a/lib/couchrest/mixins/views.rb +++ b/lib/couchrest/mixins/views.rb @@ -54,6 +54,10 @@ module CouchRest # themselves. By default Post.by_date will return the # documents included in the generated view. # + # Calling with :database => [instance of CouchRest::Database] will + # send the query to a specific database, otherwise it will go to + # the model's default database (use_database) + # # CouchRest::Database#view options can be applied at view definition # time as defaults, and they will be curried and used at view query # time. Or they can be overridden at query time. @@ -67,7 +71,7 @@ module CouchRest # that model won't be available until generation is complete. This can # take some time with large databases. Strategies are in the works. # - # To understand the capabilities of this view system more compeletly, + # To understand the capabilities of this view system more completely, # it is recommended that you read the RSpec file at # spec/core/model_spec.rb. @@ -97,12 +101,13 @@ module CouchRest refresh_design_doc end query[:raw] = true if query[:reduce] + db = query.delete(:database) || database raw = query.delete(:raw) - fetch_view_with_docs(name, query, raw, &block) + fetch_view_with_docs(db, name, query, raw, &block) end - def all_design_doc_versions - database.documents :startkey => "_design/#{self.to_s}-", + def all_design_doc_versions(db = database) + db.documents :startkey => "_design/#{self.to_s}-", :endkey => "_design/#{self.to_s}-\u9999" end @@ -111,11 +116,11 @@ module CouchRest # and consistently using the latest code, is the way to clear out old design # docs. Running it to early could mean that live code has to regenerate # potentially large indexes. - def cleanup_design_docs! - ddocs = all_design_doc_versions + def cleanup_design_docs!(db = database) + ddocs = all_design_doc_versions(db) ddocs["rows"].each do |row| if (row['id'] != design_doc_id) - database.delete_doc({ + db.delete_doc({ "_id" => row['id'], "_rev" => row['value']['rev'] }) @@ -125,30 +130,31 @@ module CouchRest private - def fetch_view_with_docs(name, opts, raw=false, &block) + def fetch_view_with_docs(db, name, opts, raw=false, &block) if raw || (opts.has_key?(:include_docs) && opts[:include_docs] == false) - fetch_view(name, opts, &block) + fetch_view(db, name, opts, &block) else begin - view = fetch_view name, opts.merge({:include_docs => true}), &block + view = fetch_view db, name, opts.merge({:include_docs => true}), &block view['rows'].collect{|r|new(r['doc'])} if view['rows'] rescue # fallback for old versions of couchdb that don't # have include_docs support - view = fetch_view(name, opts, &block) + view = fetch_view(db, name, opts, &block) view['rows'].collect{|r|new(database.get(r['id']))} if view['rows'] end end end - def fetch_view view_name, opts, &block + def fetch_view(db, view_name, opts, &block) + raise "A view needs a database to operate on (specify :database option, or use_database in the #{self.class} class)" unless db retryable = true begin - design_doc.view(view_name, opts, &block) - # the design doc could have been deleted by a rogue process + design_doc.view_on(db, view_name, opts, &block) + # the design doc may not have been saved yet on this database rescue RestClient::ResourceNotFound => e if retryable - refresh_design_doc + save_design_doc_on(db) retryable = false retry else diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index a103b62..62c26f8 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -77,10 +77,10 @@ module CouchRest end # Temp solution to make the view_by methods available - def self.method_missing(m, *args) + def self.method_missing(m, *args, &block) if has_view?(m) query = args.shift || {} - view(m, query, *args) + view(m, query, *args, &block) else super end diff --git a/spec/couchrest/core/design_spec.rb b/spec/couchrest/core/design_spec.rb index af1fb01..186e013 100644 --- a/spec/couchrest/core/design_spec.rb +++ b/spec/couchrest/core/design_spec.rb @@ -57,6 +57,13 @@ describe CouchRest::Design do res = @des.view :by_name res["rows"][0]["key"].should == "x" end + it "should be queryable on specified database" do + @des.name = "mydesign" + @des.save + @des.database = nil + res = @des.view_on @db, :by_name + res["rows"][0]["key"].should == "x" + end end describe "from a saved document" do diff --git a/spec/couchrest/more/extended_doc_view_spec.rb b/spec/couchrest/more/extended_doc_view_spec.rb index 4a3e6ee..171c81f 100644 --- a/spec/couchrest/more/extended_doc_view_spec.rb +++ b/spec/couchrest/more/extended_doc_view_spec.rb @@ -3,6 +3,13 @@ require File.join(FIXTURE_PATH, 'more', 'article') require File.join(FIXTURE_PATH, 'more', 'course') describe "ExtendedDocument views" do + + class Unattached < CouchRest::ExtendedDocument + # Note: no use_database here + property :title + property :questions + view_by :title + end describe "a model with simple views and a default param" do before(:all) do @@ -75,12 +82,18 @@ describe "ExtendedDocument views" do end it "should yield" do courses = [] - rs = Course.by_title # remove me Course.view(:by_title) do |course| courses << course end courses[0]["doc"]["title"].should =='aaa' end + it "should yield with by_key method" do + courses = [] + Course.by_title do |course| + courses << course + end + courses[0]["doc"]["title"].should =='aaa' + end end @@ -103,6 +116,76 @@ describe "ExtendedDocument views" do end end + describe "a model class not tied to a database" do + before(:all) do + reset_test_db! + @db = TEST_SERVER.default_database + %w{aaa bbb ddd eee}.each do |title| + u = Unattached.new(:title => title) + u.database = @db + u.save + @first_id ||= u.id + end + end + it "should barf on all if no database given" do + lambda{Unattached.all}.should raise_error + end + it "should query all" do + rs = Unattached.all :database=>@db + rs.length.should == 4 + end + it "should barf on query if no database given" do + lambda{Unattached.view :by_title}.should raise_error + end + it "should make the design doc upon first query" do + Unattached.by_title :database=>@db + doc = Unattached.design_doc + doc['views']['all']['map'].should include('Unattached') + end + it "should merge query params" do + rs = Unattached.by_title :database=>@db, :startkey=>"bbb", :endkey=>"eee" + rs.length.should == 3 + end + it "should query via view" do + view = Unattached.view :by_title, :database=>@db + designed = Unattached.by_title :database=>@db + view.should == designed + end + it "should yield" do + things = [] + Unattached.view(:by_title, :database=>@db) do |thing| + things << thing + end + things[0]["doc"]["title"].should =='aaa' + end + it "should barf on get if no database given" do + lambda{Unattached.get("aaa")}.should raise_error + end + it "should get from specific database" do + u = Unattached.get(@first_id, @db) + u.title.should == "aaa" + end + it "should barf on first if no database given" do + lambda{Unattached.first}.should raise_error + end + it "should get first" do + u = Unattached.first :database=>@db + u.title.should =~ /\A...\z/ + end + it "should barf on all_design_doc_versions if no database given" do + lambda{Unattached.all_design_doc_versions}.should raise_error + end + it "should clean up design docs left around on specific database" do + Unattached.by_title :database=>@db + Unattached.all_design_doc_versions(@db)["rows"].length.should == 1 + Unattached.view_by :questions + Unattached.by_questions :database=>@db + Unattached.all_design_doc_versions(@db)["rows"].length.should == 2 + Unattached.cleanup_design_docs!(@db) + Unattached.all_design_doc_versions(@db)["rows"].length.should == 1 + end + end + describe "a model with a compound key view" do before(:all) do Article.design_doc_fresh = false @@ -177,14 +260,11 @@ describe "ExtendedDocument views" do newdocs["rows"].length.should == @design_docs["rows"].length end it "should create a new version of the design document on view access" do - old_design_doc = Article.database.documents(:key => @design_docs["rows"].first["key"], :include_docs => true)["rows"][0]["doc"] + ddocs = Article.all_design_doc_versions["rows"].length Article.view_by :updated_at Article.by_updated_at - newdocs = Article.database.documents({:startkey => "_design/", :endkey => "_design/\u9999"}) - - doc = Article.database.documents(:key => @design_docs["rows"].first["key"], :include_docs => true)["rows"][0]["doc"] - doc["_rev"].should_not == old_design_doc["_rev"] - doc["views"].keys.should include("by_updated_at") + Article.all_design_doc_versions["rows"].length.should == ddocs + 1 + Article.design_doc["views"].keys.should include("by_updated_at") end end @@ -196,12 +276,11 @@ describe "ExtendedDocument views" do Article.by_field end it "should clean them up" do - ddocs = Article.all_design_doc_versions Article.view_by :stream Article.by_stream + Article.all_design_doc_versions["rows"].length.should > 1 Article.cleanup_design_docs! - ddocs = Article.all_design_doc_versions - ddocs["rows"].length.should == 1 + Article.all_design_doc_versions["rows"].length.should == 1 end end end From aabf097e88757fd9b04665890a6676d4b317baa9 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 27 Mar 2009 11:44:09 +0000 Subject: [PATCH 4/7] Tidying up spec, remove unnecessary assignments to local variable --- spec/couchrest/core/design_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/couchrest/core/design_spec.rb b/spec/couchrest/core/design_spec.rb index 186e013..8b367c7 100644 --- a/spec/couchrest/core/design_spec.rb +++ b/spec/couchrest/core/design_spec.rb @@ -14,7 +14,7 @@ describe CouchRest::Design do describe "with an unsaved view" do before(:each) do @des = CouchRest::Design.new - method = @des.view_by :name + @des.view_by :name end it "should accept a name" do @des.name = "mytest" @@ -31,7 +31,7 @@ describe CouchRest::Design do describe "saving" do before(:each) do @des = CouchRest::Design.new - method = @des.view_by :name + @des.view_by :name @des.database = reset_test_db! end it "should fail without a name" do @@ -49,7 +49,7 @@ describe CouchRest::Design do @db.bulk_save([{"name" => "x"},{"name" => "y"}]) @des = CouchRest::Design.new @des.database = @db - method = @des.view_by :name + @des.view_by :name end it "should by queryable when it's saved" do @des.name = "mydesign" @@ -99,7 +99,7 @@ describe CouchRest::Design do @db = reset_test_db! @des = CouchRest::Design.new @des.name = "test" - method = @des.view_by :name, :descending => true + @des.view_by :name, :descending => true @des.database = @db @des.save @db.bulk_save([{"name" => "a"},{"name" => "z"}]) @@ -123,7 +123,7 @@ describe CouchRest::Design do @db = reset_test_db! @des = CouchRest::Design.new @des.name = "test" - method = @des.view_by :name, :age + @des.view_by :name, :age @des.database = @db @des.save @db.bulk_save([{"name" => "a", "age" => 2}, From af6ac7df89bd2ee4efa8ff243760f848b6850072 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 27 Mar 2009 11:50:42 +0000 Subject: [PATCH 5/7] Remove obsolete 'move' methods --- lib/couchrest/core/document.rb | 9 --------- lib/couchrest/monkeypatches.rb | 6 ------ 2 files changed, 15 deletions(-) diff --git a/lib/couchrest/core/document.rb b/lib/couchrest/core/document.rb index 1f889b3..e90c5fe 100644 --- a/lib/couchrest/core/document.rb +++ b/lib/couchrest/core/document.rb @@ -65,15 +65,6 @@ module CouchRest result['ok'] end - # moves the document to a new id. If the destination id currently exists, a rev must be provided. - # dest can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc - # hash with a '_rev' key - def move(dest) - raise ArgumentError, "doc.database required to copy" unless database - result = database.move_doc(self, dest) - result['ok'] - end - # Returns the CouchDB uri for the document def uri(append_rev = false) return nil if new_document? diff --git a/lib/couchrest/monkeypatches.rb b/lib/couchrest/monkeypatches.rb index 60e02b8..c7678f6 100644 --- a/lib/couchrest/monkeypatches.rb +++ b/lib/couchrest/monkeypatches.rb @@ -56,12 +56,6 @@ module RestClient :url => url, :headers => headers) end - - def self.move(url, headers={}) - Request.execute(:method => :move, - :url => url, - :headers => headers) - end # class Request # From c4b49baecfb439782ca120399447bcaa1c7d9c59 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 27 Mar 2009 13:42:49 +0000 Subject: [PATCH 6/7] ClassProxy provides class-level methods on a dynamically chosen database. Examples: db = CouchRest::Database.new(...) articles = Article.on(db) articles.all { ... } articles.by_title { ... } u = articles.get("someid") u = articles.new(:title => "I like plankton") u.save # saved on the correct database --- lib/couchrest/mixins/class_proxy.rb | 108 ++++++++++++++++++ .../mixins/extended_document_mixins.rb | 3 +- lib/couchrest/more/extended_document.rb | 1 + spec/couchrest/more/extended_doc_view_spec.rb | 69 +++++++++++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 lib/couchrest/mixins/class_proxy.rb diff --git a/lib/couchrest/mixins/class_proxy.rb b/lib/couchrest/mixins/class_proxy.rb new file mode 100644 index 0000000..17d6adf --- /dev/null +++ b/lib/couchrest/mixins/class_proxy.rb @@ -0,0 +1,108 @@ +module CouchRest + module Mixins + module ClassProxy + + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + + # Return a proxy object which represents a model class on a + # chosen database instance. This allows you to DRY operations + # where a database is chosen dynamically. + # + # ==== Example: + # + # db = CouchRest::Database.new(...) + # articles = Article.on(db) + # + # articles.all { ... } + # articles.by_title { ... } + # + # u = articles.get("someid") + # + # u = articles.new(:title => "I like plankton") + # u.save # saved on the correct database + + def on(database) + Proxy.new(self, database) + end + end + + class Proxy #:nodoc: + def initialize(klass, database) + @klass = klass + @database = database + end + + # ExtendedDocument + + def new(*args) + doc = @klass.new(*args) + doc.database = @database + doc + end + + def method_missing(m, *args, &block) + if has_view?(m) + query = args.shift || {} + view(m, query, *args, &block) + else + super + end + end + + # Mixins::DocumentQueries + + def all(opts = {}, &block) + @klass.all({:database => @database}.merge(opts), &block) + end + + def first(opts = {}) + @klass.first({:database => @database}.merge(opts)) + end + + def get(id) + @klass.get(id, @database) + end + + # Mixins::Views + + def has_view?(view) + @klass.has_view?(view) + end + + def view(name, query={}, &block) + @klass.view(name, {:database => @database}.merge(query), &block) + end + + def all_design_doc_versions + @klass.all_design_doc_versions(@database) + end + + def cleanup_design_docs! + @klass.cleanup_design_docs!(@database) + end + + # Mixins::DesignDoc + + def design_doc + @klass.design_doc + end + + def design_doc_fresh + @klass.design_doc_fresh + end + + def refresh_design_doc + @klass.refresh_design_doc + end + + def save_design_doc + @klass.save_design_doc_on(@database) + end + end + end + end +end diff --git a/lib/couchrest/mixins/extended_document_mixins.rb b/lib/couchrest/mixins/extended_document_mixins.rb index c68eed5..f5aa8f9 100644 --- a/lib/couchrest/mixins/extended_document_mixins.rb +++ b/lib/couchrest/mixins/extended_document_mixins.rb @@ -3,4 +3,5 @@ require File.join(File.dirname(__FILE__), 'document_queries') require File.join(File.dirname(__FILE__), 'views') require File.join(File.dirname(__FILE__), 'design_doc') require File.join(File.dirname(__FILE__), 'validation') -require File.join(File.dirname(__FILE__), 'extended_attachments') \ No newline at end of file +require File.join(File.dirname(__FILE__), 'extended_attachments') +require File.join(File.dirname(__FILE__), 'class_proxy') diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index 62c26f8..c1590f1 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -11,6 +11,7 @@ module CouchRest include CouchRest::Mixins::Views include CouchRest::Mixins::DesignDoc include CouchRest::Mixins::ExtendedAttachments + include CouchRest::Mixins::ClassProxy def self.inherited(subklass) subklass.send(:include, CouchRest::Mixins::Properties) diff --git a/spec/couchrest/more/extended_doc_view_spec.rb b/spec/couchrest/more/extended_doc_view_spec.rb index 171c81f..e4aff6b 100644 --- a/spec/couchrest/more/extended_doc_view_spec.rb +++ b/spec/couchrest/more/extended_doc_view_spec.rb @@ -8,6 +8,7 @@ describe "ExtendedDocument views" do # Note: no use_database here property :title property :questions + property :professor view_by :title end @@ -158,6 +159,13 @@ describe "ExtendedDocument views" do end things[0]["doc"]["title"].should =='aaa' end + it "should yield with by_key method" do + things = [] + Unattached.by_title(:database=>@db) do |thing| + things << thing + end + things[0]["doc"]["title"].should =='aaa' + end it "should barf on get if no database given" do lambda{Unattached.get("aaa")}.should raise_error end @@ -186,6 +194,67 @@ describe "ExtendedDocument views" do end end + describe "class proxy" do + before(:all) do + reset_test_db! + @us = Unattached.on(TEST_SERVER.default_database) + %w{aaa bbb ddd eee}.each do |title| + u = @us.new(:title => title) + u.save + @first_id ||= u.id + end + end + it "should query all" do + rs = @us.all + rs.length.should == 4 + end + it "should make the design doc upon first query" do + @us.by_title + doc = @us.design_doc + doc['views']['all']['map'].should include('Unattached') + end + it "should merge query params" do + rs = @us.by_title :startkey=>"bbb", :endkey=>"eee" + rs.length.should == 3 + end + it "should query via view" do + view = @us.view :by_title + designed = @us.by_title + view.should == designed + end + it "should yield" do + things = [] + @us.view(:by_title) do |thing| + things << thing + end + things[0]["doc"]["title"].should =='aaa' + end + it "should yield with by_key method" do + things = [] + @us.by_title do |thing| + things << thing + end + things[0]["doc"]["title"].should =='aaa' + end + it "should get from specific database" do + u = @us.get(@first_id) + u.title.should == "aaa" + end + it "should get first" do + u = @us.first + u.title.should =~ /\A...\z/ + end + it "should clean up design docs left around on specific database" do + @us.by_title + @us.all_design_doc_versions["rows"].length.should == 1 + Unattached.view_by :professor + @us.by_professor + @us.all_design_doc_versions["rows"].length.should == 2 + @us.cleanup_design_docs! + @us.all_design_doc_versions["rows"].length.should == 1 + end + end + describe "a model with a compound key view" do before(:all) do Article.design_doc_fresh = false From d71a33fc9321b45be51e1ff768753f87cd88103c Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 27 Mar 2009 14:00:28 +0000 Subject: [PATCH 7/7] Update handler for old versions of couchdb --- lib/couchrest/mixins/views.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/couchrest/mixins/views.rb b/lib/couchrest/mixins/views.rb index f6d8b7f..e2aa685 100644 --- a/lib/couchrest/mixins/views.rb +++ b/lib/couchrest/mixins/views.rb @@ -141,7 +141,7 @@ module CouchRest # fallback for old versions of couchdb that don't # have include_docs support view = fetch_view(db, name, opts, &block) - view['rows'].collect{|r|new(database.get(r['id']))} if view['rows'] + view['rows'].collect{|r|new(db.get(r['id']))} if view['rows'] end end end