From c4b49baecfb439782ca120399447bcaa1c7d9c59 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Fri, 27 Mar 2009 13:42:49 +0000 Subject: [PATCH] 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