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
This commit is contained in:
Brian Candler 2009-03-27 13:42:49 +00:00
parent af6ac7df89
commit c4b49baecf
4 changed files with 180 additions and 1 deletions

View file

@ -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

View file

@ -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__), 'views')
require File.join(File.dirname(__FILE__), 'design_doc') require File.join(File.dirname(__FILE__), 'design_doc')
require File.join(File.dirname(__FILE__), 'validation') require File.join(File.dirname(__FILE__), 'validation')
require File.join(File.dirname(__FILE__), 'extended_attachments') require File.join(File.dirname(__FILE__), 'extended_attachments')
require File.join(File.dirname(__FILE__), 'class_proxy')

View file

@ -11,6 +11,7 @@ module CouchRest
include CouchRest::Mixins::Views include CouchRest::Mixins::Views
include CouchRest::Mixins::DesignDoc include CouchRest::Mixins::DesignDoc
include CouchRest::Mixins::ExtendedAttachments include CouchRest::Mixins::ExtendedAttachments
include CouchRest::Mixins::ClassProxy
def self.inherited(subklass) def self.inherited(subklass)
subklass.send(:include, CouchRest::Mixins::Properties) subklass.send(:include, CouchRest::Mixins::Properties)

View file

@ -8,6 +8,7 @@ describe "ExtendedDocument views" do
# Note: no use_database here # Note: no use_database here
property :title property :title
property :questions property :questions
property :professor
view_by :title view_by :title
end end
@ -158,6 +159,13 @@ describe "ExtendedDocument views" do
end end
things[0]["doc"]["title"].should =='aaa' things[0]["doc"]["title"].should =='aaa'
end 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 it "should barf on get if no database given" do
lambda{Unattached.get("aaa")}.should raise_error lambda{Unattached.get("aaa")}.should raise_error
end end
@ -186,6 +194,67 @@ describe "ExtendedDocument views" do
end end
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 describe "a model with a compound key view" do
before(:all) do before(:all) do
Article.design_doc_fresh = false Article.design_doc_fresh = false