Browse files

Still working on getting new design docs to work. Simplified some of …

…the logic to allow new views to be created outside a design block
  • Loading branch information...
1 parent 88def18 commit 7f0d1a12b98e36f2823ad078e68484c4aca19de4 @samlown samlown committed Jun 7, 2012
View
1 lib/couchrest/model/base.rb
@@ -9,7 +9,6 @@ class Base < CouchRest::Document
include CouchRest::Model::Persistence
include CouchRest::Model::DocumentQueries
include CouchRest::Model::ExtendedAttachments
- include CouchRest::Model::ClassProxy
include CouchRest::Model::Proxyable
include CouchRest::Model::PropertyProtection
include CouchRest::Model::Associations
View
135 lib/couchrest/model/class_proxy.rb
@@ -1,135 +0,0 @@
-module CouchRest
- module Model
- 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
-
- # Base
-
- 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 || {}
- return view(m, query, *args, &block)
- elsif m.to_s =~ /^find_(by_.+)/
- view_name = $1
- if has_view?(view_name)
- return first_from_view(view_name, *args)
- end
- end
- super
- end
-
- # DocumentQueries
-
- def all(opts = {}, &block)
- docs = @klass.all({:database => @database}.merge(opts), &block)
- docs.each { |doc| doc.database = @database if doc.respond_to?(:database) } if docs
- docs
- end
-
- def count(opts = {}, &block)
- @klass.all({:database => @database, :raw => true, :limit => 0}.merge(opts), &block)['total_rows']
- end
-
- def first(opts = {})
- doc = @klass.first({:database => @database}.merge(opts))
- doc.database = @database if doc && doc.respond_to?(:database)
- doc
- end
-
- def last(opts = {})
- doc = @klass.last({:database => @database}.merge(opts))
- doc.database = @database if doc && doc.respond_to?(:database)
- doc
- end
-
- def get(id)
- doc = @klass.get(id, @database)
- doc.database = @database if doc && doc.respond_to?(:database)
- doc
- end
- alias :find :get
-
- def get!(id)
- doc = @klass.get!(id, @database)
- doc.database = @database if doc && doc.respond_to?(:database)
- doc
- end
- alias :find! :get!
-
- # Views
-
- def has_view?(view)
- @klass.has_view?(view)
- end
-
- def view(name, query={}, &block)
- docs = @klass.view(name, {:database => @database}.merge(query), &block)
- docs.each { |doc| doc.database = @database if doc.respond_to?(:database) } if docs
- docs
- end
-
- def first_from_view(name, *args)
- # add to first hash available, or add to end
- (args.last.is_a?(Hash) ? args.last : (args << {}).last)[:database] = @database
- doc = @klass.first_from_view(name, *args)
- doc.database = @database if doc && doc.respond_to?(:database)
- doc
- end
-
- # DesignDoc
-
- def design_doc
- @klass.design_doc
- end
-
- def refresh_design_doc
- @klass.refresh_design_doc(@database)
- end
-
- def save_design_doc
- @klass.save_design_doc(@database)
- end
-
- end
- end
- end
-end
View
55 lib/couchrest/model/designs.rb
@@ -27,8 +27,9 @@ module ClassMethods
def design(prefix = nil, &block)
mapper = DesignMapper.new(self, prefix)
mapper.instance_eval(&block) if block_given?
- # Create an 'all' view, using the previous settings.
- mapper.view :all if prefix.nil?
+
+ # Create an 'all' view if no prefix and one has not been defined already
+ mapper.view(:all) if prefix.nil? and !mapper.design_doc.has_view?(:all)
end
# Override the default page pagination value:
@@ -54,7 +55,8 @@ def design_docs
end
-
+ # Map method calls defined in a design block to actions
+ # in the Design Document.
class DesignMapper
# Basic mapper attributes
@@ -66,17 +68,10 @@ class DesignMapper
def initialize(model, prefix = nil)
self.model = model
self.prefix = prefix
- self.method = (prefix ? "#{prefix}_" : '') + 'design_doc'
-
- # Create design doc method in model, then call it so we have a copy
- create_design_doc_method
- self.design_doc = model.send(method)
+ self.method = Design.method_name(prefix)
- # Ensure model has up to date list of design docs
- model.design_docs << design_doc unless model.design_docs.include?(design_doc)
-
- # Some defaults
- design_doc.auto_update = model.auto_update_design_doc
+ self.design_doc = (model.respond_to?(method) ?
+ model.send(method) : create_model_design_doc_reader)
end
def disable_auto_update
@@ -90,8 +85,7 @@ def enable_auto_update
# Add the specified view to the design doc the definition was made in
# and create quick access methods in the model.
def view(name, opts = {})
- View.define(model, design_doc, name, opts)
- create_view_method(name)
+ design_doc.create_view(name, opts)
end
# Really simple design function that allows a filter
@@ -101,8 +95,7 @@ def view(name, opts = {})
# No methods are created here, the design is simply updated.
# See the CouchDB API for more information on how to use this.
def filter(name, function)
- filters = (design_doc['filters'] ||= {})
- filters[name.to_s] = function
+ design_doc.create_filter(name, function)
end
# Convenience wrapper to access model's type key option.
@@ -112,24 +105,18 @@ def model_type_key
protected
- def create_design_doc_method
- model.class_eval <<-EOS, __FILE__, __LINE__ + 1
- def self.#{method}
- @_#{method} ||= ::CouchRest::Model::Designs::Design.new(self, #{prefix ? '"'+prefix.to_s+'"' : 'nil'})
- end
- EOS
- end
+ # Create accessor in model and assign a new design doc.
+ # New design doc is returned ready to use.
+ def create_model_design_doc_reader
+ doc = Design.new(model, prefix)
+ model.instance_eval "def #{method}; @#{method}; end"
+ model.instance_variable_set("@#{method}", doc)
+ model.design_docs << doc
+
+ # Set defaults
+ doc.auto_update = model.auto_update_design_doc
- def create_view_method(name, prefix = nil)
- prefix = prefix ? "#{prefix}_" : ''
- model.class_eval <<-EOS, __FILE__, __LINE__ + 1
- def self.#{name}(opts = {})
- #{method}.view('#{name}', opts)
- end
- def self.find_#{name}(*key)
- #{name}.key(*key).first()
- end
- EOS
+ doc
end
end
View
48 lib/couchrest/model/designs/design.rb
@@ -5,8 +5,8 @@ module Designs
class Design < ::CouchRest::Design
- # The model Class that this design belongs to
- attr_accessor :model
+ # The model Class that this design belongs to and method name
+ attr_accessor :model, :method_name
# Can this design save itself to the database?
# If false, the design will be loaded automatically before a view is executed.
@@ -15,18 +15,13 @@ class Design < ::CouchRest::Design
# Instantiate a new design document for this model
def initialize(model, prefix = nil)
- self.model = model
+ self.model = model
+ self.method_name = self.class.method_name(prefix)
suffix = prefix ? "_#{prefix}" : ''
self["_id"] = "_design/#{model.to_s}#{suffix}"
apply_defaults
end
- # Create a new view object.
- # This overrides the normal CouchRest Design view method
- def view(name, opts = {})
- CouchRest::Model::Designs::View.new(self, model, opts, name)
- end
-
def sync(db = nil)
if auto_update
db ||= database
@@ -77,11 +72,37 @@ def uri(db = database)
"#{db.root}/#{self['_id']}"
end
+
+ ######## VIEW HANDLING ########
+
+ # Create a new view object.
+ # This overrides the normal CouchRest Design view method
+ def view(name, opts = {})
+ CouchRest::Model::Designs::View.new(self, model, opts, name)
+ end
+
# Helper method to provide a list of all the views
def view_names
self['views'].keys
end
+ def has_view?(name)
+ view_names.include?(name.to_s)
+ end
+
+ # Add the specified view to the design doc the definition was made in
+ # and create quick access methods in the model.
+ def create_view(name, opts = {})
+ View.define_and_create(self, name, opts)
+ end
+
+ ######## FILTER HANDLING ########
+
+ def create_filter(name, function)
+ filters = (self['filters'] ||= {})
+ filters[name.to_s] = function
+ end
+
protected
def load_from_database(db = database)
@@ -138,6 +159,15 @@ def apply_defaults
)
end
+
+ class << self
+
+ def method_name(prefix = nil)
+ (prefix ? "#{prefix}_" : '') + 'design_doc'
+ end
+
+ end
+
end
end
end
View
27 lib/couchrest/model/designs/view.rb
@@ -20,15 +20,16 @@ class View
# outside CouchRest Model.
def initialize(design_doc, parent, new_query = {}, name = nil)
self.design_doc = design_doc
+ proxy = new_query.delete(:proxy)
if parent.is_a?(Class) && parent < CouchRest::Model::Base
raise "Name must be provided for view to be initialized" if name.nil?
- self.model = parent
+ self.model = (proxy || parent)
self.owner = parent
self.name = name.to_s
# Default options:
self.query = { }
elsif parent.is_a?(self.class)
- self.model = (new_query.delete(:proxy) || parent.model)
+ self.model = (proxy || parent.model)
self.owner = parent.owner
self.name = parent.name
self.query = parent.query.dup
@@ -406,6 +407,12 @@ def execute
# Class Methods
class << self
+
+ def define_and_create(design_doc, name, opts = {})
+ define(design_doc, name, opts)
+ create_model_methods(design_doc, name, opts)
+ end
+
# Simplified view definition. A new view will be added to the
# provided design document using the name and options.
#
@@ -434,9 +441,10 @@ class << self
# like to enable this, set the <tt>:allow_blank</tt> option to false. The default
# is true, empty strings are permited in the indexes.
#
- def define(model, design_doc, name, opts = {})
+ def define(design_doc, name, opts = {})
# Don't create the map or reduce method if auto updates are disabled
if design_doc.auto_update
+ model = design_doc.model
# Is this an all view?
if name.to_s == 'all'
opts[:map] = <<-EOF
@@ -486,6 +494,19 @@ def define(model, design_doc, name, opts = {})
view
end
+
+ def create_model_methods(design_doc, name, opts = {})
+ method = design_doc.method_name
+ design_doc.model.instance_eval <<-EOS, __FILE__, __LINE__ + 1
+ def #{name}(opts = {})
+ #{method}.view('#{name}', opts)
+ end
+ def find_#{name}(*key)
+ #{name}.key(*key).first()
+ end
+ EOS
+ end
+
end
end
View
6 lib/couchrest/model/proxyable.rb
@@ -108,10 +108,10 @@ def create_view_methods
model.design_docs.each do |doc|
doc.view_names.each do |name|
class_eval <<-EOS, __FILE__, __LINE__ + 1
- def self.#{name}(opts = {})
- #{name}({:proxy => self}.merge(opts))
+ def #{name}(opts = {})
+ model.#{name}({:proxy => self}.merge(opts))
end
- def self.find_#{name}(*key)
+ def find_#{name}(*key)
#{name}.key(*key).first()
end
EOS
View
13 lib/couchrest/model/validations/uniqueness.rb
@@ -15,9 +15,10 @@ def setup(model)
attributes.each do |attribute|
opts = merge_view_options(attribute)
- if model.respond_to?(:has_view?) && !model.has_view?(opts[:view_name])
- opts[:keys] << {:allow_nil => true}
- model.view_by(*opts[:keys])
+ unless model.respond_to?(opts[:view_name])
+ model.design do
+ view opts[:view_name], :allow_nil => true
+ end
end
end
end
@@ -33,15 +34,15 @@ def validate_each(document, attribute, value)
# Determine the base of the search
base = opts[:proxy].nil? ? model : document.instance_eval(opts[:proxy])
- if base.respond_to?(:has_view?) && !base.has_view?(opts[:view_name])
+ unless base.respond_to?(opts[:view_name])
raise "View #{document.class.name}.#{opts[:view_name]} does not exist for validation!"
end
- rows = base.view(opts[:view_name], :key => values, :limit => 2, :include_docs => false)['rows']
+ rows = base.send(opts[:view_name], :key => values, :limit => 2, :include_docs => false).rows
return if rows.empty?
unless document.new?
- return if rows.find{|row| row['id'] == document.id}
+ return if rows.find{|row| row.id == document.id}
end
if rows.length > 0
View
1 lib/couchrest_model.rb
@@ -37,7 +37,6 @@
require "couchrest/model/callbacks"
require "couchrest/model/document_queries"
require "couchrest/model/extended_attachments"
-require "couchrest/model/class_proxy"
require "couchrest/model/proxyable"
require "couchrest/model/associations"
require "couchrest/model/configuration"
View
1 spec/fixtures/models/base.rb
@@ -150,6 +150,7 @@ def code
end
property :title
+ design
validates_uniqueness_of :code, :view => 'all'
end
View
169 spec/unit/class_proxy_spec.rb
@@ -1,169 +0,0 @@
-require "spec_helper"
-
-class UnattachedDoc < CouchRest::Model::Base
- # Note: no use_database here
- property :title
- property :questions
- property :professor
- design do
- view :title
- end
-end
-
-
-describe "Proxy Class" do
-
- before(:all) do
- reset_test_db!
- # setup the class default doc to save the design doc
- UnattachedDoc.use_database nil # just to be sure it is really unattached
- @us = UnattachedDoc.on(DB)
- %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 count" do
- @us.count.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('UnattachedDoc')
- 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 query via first_from_view" do
- UnattachedDoc.should_receive(:first_from_view).with('by_title', 'bbb', {:database => DB})
- @us.first_from_view('by_title', 'bbb')
- end
-
- it "should query via first_from_view with complex options" do
- UnattachedDoc.should_receive(:first_from_view).with('by_title', {:key => 'bbb', :database => DB})
- @us.first_from_view('by_title', :key => 'bbb')
- end
-
- it "should query via first_from_view with complex extra options" do
- UnattachedDoc.should_receive(:first_from_view).with('by_title', 'bbb', {:limit => 1, :database => DB})
- @us.first_from_view('by_title', 'bbb', :limit => 1)
- end
-
- it "should allow dynamic view matching for single elements" do
- @us.should_receive(:first_from_view).with('by_title', 'bbb')
- @us.find_by_title('bbb')
- 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.should == @us.all.first
- end
-
- it "should get last" do
- u = @us.last
- u.should == @us.all.last
- end
-
- it "should set database on first retreived document" do
- u = @us.first
- u.database.should === DB
- end
- it "should set database on all retreived documents" do
- @us.all.each do |u|
- u.database.should === DB
- end
- end
- it "should set database on each retreived document" do
- rs = @us.by_title :startkey=>"bbb", :endkey=>"eee"
- rs.length.should == 3
- rs.each do |u|
- u.database.should === DB
- end
- end
- it "should set database on document retreived by id" do
- u = @us.get(@first_id)
- u.database.should === DB
- end
- it "should not attempt to set database on raw results using :all" do
- @us.all(:raw => true).each do |u|
- u.respond_to?(:database).should be_false
- end
- end
- it "should not attempt to set database on raw results using view" do
- @us.by_title(:raw => true).each do |u|
- u.respond_to?(:database).should be_false
- end
- end
-
- describe "#get!" do
- it "raises exception when passed a nil" do
- expect { @us.get!(nil)}.to raise_error(CouchRest::Model::DocumentNotFound)
- end
-
- it "raises exception when passed an empty string " do
- expect { @us.get!("")}.to raise_error(CouchRest::Model::DocumentNotFound)
- end
-
- it "raises exception when document with provided id does not exist" do
- expect { @us.get!("thisisnotreallyadocumentid")}.to raise_error(CouchRest::Model::DocumentNotFound)
- end
- end
-
- describe "#find!" do
- it "raises exception when passed a nil" do
- expect { @us.find!(nil)}.to raise_error(CouchRest::Model::DocumentNotFound)
- end
-
- it "raises exception when passed an empty string " do
- expect { @us.find!("")}.to raise_error(CouchRest::Model::DocumentNotFound)
- end
-
- it "raises exception when document with provided id does not exist" do
- expect { @us.find!("thisisnotreallyadocumentid")}.to raise_error(CouchRest::Model::DocumentNotFound)
- end
- end
-
- # Sam Lown 2010-04-07
- # Removed as unclear why this should happen as before my changes
- # this happend by accident, not explicitly.
- # If requested, this feature should be added as a specific method.
- #
- #it "should clean up design docs left around on specific database" do
- # @us.by_title
- # original_id = @us.model_design_doc['_rev']
- # Unattached.view_by :professor
- # @us.by_professor
- # @us.model_design_doc['_rev'].should_not == original_id
- #end
-end
View
89 spec/unit/designs/design_spec.rb
@@ -20,6 +20,24 @@ class DesignSampleModel < CouchRest::Model::Base
end
end
+ describe "class methods" do
+
+ before :all do
+ @klass = CouchRest::Model::Designs::Design
+ end
+
+ describe ".method_name" do
+ it "should return standard method name" do
+ @klass.method_name.should eql('design_doc')
+ end
+
+ it "should add prefix to standard method name" do
+ @klass.method_name('stats').should eql('stats_design_doc')
+ end
+ end
+
+ end
+
describe "base methods" do
before :each do
@@ -30,8 +48,9 @@ class DesignSampleModel < CouchRest::Model::Base
describe "initialisation without prefix" do
- it "should associate model" do
+ it "should associate model and set method name" do
@obj.model.should eql(@model)
+ @obj.method_name.should eql("design_doc")
end
it "should generate correct id" do
@@ -45,8 +64,10 @@ class DesignSampleModel < CouchRest::Model::Base
describe "initialisation with prefix" do
- it "should associate model" do
+ it "should associate model and set method name" do
+ @obj = CouchRest::Model::Designs::Design.new(@model, 'stats')
@obj.model.should eql(@model)
+ @obj.method_name.should eql("stats_design_doc")
end
it "should generate correct id with prefix" do
@@ -57,15 +78,6 @@ class DesignSampleModel < CouchRest::Model::Base
end
- describe "view method" do
-
- it "should instantiate a new view and pass options" do
- CouchRest::Model::Designs::View.should_receive(:new).with(@obj, @model, {}, 'by_test')
- @obj.view('by_test', {})
- end
-
- end
-
describe "sync method" do
@@ -188,13 +200,68 @@ class DesignSampleModel < CouchRest::Model::Base
end
end
+ describe "#view" do
+ it "should instantiate a new view and pass options" do
+ CouchRest::Model::Designs::View.should_receive(:new).with(@obj, @model, {}, 'by_test')
+ @obj.view('by_test', {})
+ end
+ end
+
describe "#view_names" do
it "should provide a list of all the views available" do
@doc = DesignSampleModel.design_doc
@doc.view_names.should eql(['by_name', 'all'])
end
end
+ describe "#has_view?" do
+ before :each do
+ @doc = DesignSampleModel.design_doc
+ end
+
+ it "should tell us if a view exists" do
+ @doc.has_view?('by_name').should be_true
+ end
+
+ it "should tell us if a view exists as symbol" do
+ @doc.has_view?(:by_name).should be_true
+ end
+
+ it "should tell us if a view does not exist" do
+ @doc.has_view?(:by_foobar).should be_false
+ end
+ end
+
+ describe "#create_view" do
+ before :each do
+ @doc = DesignSampleModel.design_doc
+ @doc['views'] = @doc['views'].clone
+ end
+
+ it "should forward view creation to View model" do
+ CouchRest::Model::Designs::View.should_receive(:define_and_create).with(@doc, 'by_other_name', {})
+ @doc.create_view('by_other_name')
+ end
+
+ it "should forward view creation to View model with opts" do
+ CouchRest::Model::Designs::View.should_receive(:define_and_create).with(@doc, 'by_other_name', {:by => 'name'})
+ @doc.create_view('by_other_name', :by => 'name')
+ end
+ end
+
+
+ describe "#create_filter" do
+ before :each do
+ @doc = DesignSampleModel.design_doc
+ end
+
+ it "should add simple filter" do
+ @doc.create_filter('test', 'foobar')
+ @doc['filters']['test'].should eql('foobar')
+ @doc['filters'] = nil # cleanup
+ end
+ end
+
end
View
75 spec/unit/designs/view_spec.rb
@@ -59,24 +59,62 @@ class DesignViewModel < CouchRest::Model::Base
end
+ describe "with proxy in query for first initialization" do
+ it "should set model to proxy object and remove from query" do
+ proxy = mock("Proxy")
+ @obj = @klass.new(@mod.design_doc, @mod, {:proxy => proxy}, 'test_view')
+ @obj.model.should eql(proxy)
+ end
+ end
+
+ describe "with proxy in query for chained instance" do
+ it "should set the model to proxy object instead of parents model" do
+ proxy = mock("Proxy")
+ @obj = @klass.new(@mod.design_doc, @mod, {}, 'test_view')
+ @obj.model.should eql(@mod)
+ @obj = @obj.proxy(proxy)
+ @obj.model.should eql(proxy)
+ end
+ end
+
+ end
+
+ describe ".define_and_create" do
+ before :each do
+ @design_doc = { }
+ end
+
+ it "should call define and create_model_methods method" do
+ @klass.should_receive(:define).with(@design_doc, 'test', {}).and_return(nil)
+ @klass.should_receive(:create_model_methods).with(@design_doc, 'test', {}).and_return(nil)
+ @klass.define_and_create(@design_doc, 'test')
+ end
+
+ it "should call define and create_model_methods method with opts" do
+ @klass.should_receive(:define).with(@design_doc, 'test', {:foo => :bar}).and_return(nil)
+ @klass.should_receive(:create_model_methods).with(@design_doc, 'test', {:foo => :bar}).and_return(nil)
+ @klass.define_and_create(@design_doc, 'test', {:foo => :bar})
+ end
+
end
describe ".define" do
describe "with no auto update" do
before :each do
@design_doc = { }
+ @design_doc.stub!(:model).and_return(DesignViewModel)
@design_doc.stub!(:auto_update).and_return(false)
end
it "should set map view to true" do
- @klass.define(DesignViewModel, @design_doc, 'test_view')
+ @klass.define(@design_doc, 'test_view')
@design_doc['views']['test_view']['map'].should eql(true)
@design_doc['views']['test_view']['reduce'].should be_false
end
it "should set reduce to true if set" do
- @klass.define(DesignViewModel, @design_doc, 'test_view', :reduce => true)
+ @klass.define(@design_doc, 'test_view', :reduce => true)
@design_doc['views']['test_view']['map'].should eql(true)
@design_doc['views']['test_view']['reduce'].should eql(true)
end
@@ -86,16 +124,17 @@ class DesignViewModel < CouchRest::Model::Base
before :each do
@design_doc = { }
+ @design_doc.stub!(:model).and_return(DesignViewModel)
@design_doc.stub!(:auto_update).and_return(true)
end
it "should add a basic view" do
- @klass.define(DesignViewModel, @design_doc, 'test_view', :map => 'foo')
+ @klass.define(@design_doc, 'test_view', :map => 'foo')
@design_doc['views']['test_view'].should_not be_nil
end
it "should auto generate mapping from name" do
- lambda { @klass.define(DesignViewModel, @design_doc, 'by_title') }.should_not raise_error
+ lambda { @klass.define(@design_doc, 'by_title') }.should_not raise_error
str = @design_doc['views']['by_title']['map']
str.should include("((doc['#{DesignViewModel.model_type_key}'] == 'DesignViewModel') && (doc['title'] != null))")
str.should include("emit(doc['title'], 1);")
@@ -104,16 +143,42 @@ class DesignViewModel < CouchRest::Model::Base
end
it "should auto generate mapping from name with and" do
- @klass.define(DesignViewModel, @design_doc, 'by_title_and_name')
+ @klass.define(@design_doc, 'by_title_and_name')
str = @design_doc['views']['by_title_and_name']['map']
str.should include("(doc['title'] != null) && (doc['name'] != null)")
str.should include("emit([doc['title'], doc['name']], 1);")
str = @design_doc['views']['by_title_and_name']['reduce']
str.should include("return sum(values);")
end
end
+
+ describe ".create_model_methods" do
+ before :each do
+ @model = DesignViewModel
+ @design_doc = { }
+ @design_doc.stub!(:model).and_return(@model)
+ @design_doc.stub!(:method_name).and_return("design_doc")
+ @model.stub!('design_doc').and_return(@design_doc)
+ end
+ it "should create standard view method" do
+ @klass.create_model_methods(@design_doc, 'by_name')
+ @model.should respond_to('by_name')
+ @design_doc.should_receive('view').with('by_name', {})
+ @model.by_name
+ end
+ it "should create find_ view method" do
+ @klass.create_model_methods(@design_doc, 'by_name')
+ @model.should respond_to('find_by_name')
+ view = mock("View")
+ view.should_receive('key').with('fred').and_return(view)
+ view.should_receive('first').and_return(nil)
+ @design_doc.should_receive('view').and_return(view)
+ @model.find_by_name('fred')
+ end
+ end
end
+
describe "instance methods" do
before :each do
View
28 spec/unit/designs_spec.rb
@@ -146,7 +146,7 @@ class DesignModel < CouchRest::Model::Base
end
it "should call create method on view" do
- CouchRest::Model::Designs::View.should_receive(:define).with(DesignModel, @object.design_doc, 'test', {})
+ CouchRest::Model::Designs::View.should_receive(:define).with(@object.design_doc, 'test', {})
@object.view('test')
end
@@ -157,8 +157,7 @@ class DesignModel < CouchRest::Model::Base
end
it "should create a method for view instance" do
- CouchRest::Model::Designs::View.stub!(:define)
- @object.should_receive(:create_view_method).with('test')
+ @object.design_doc.should_receive(:create_view).with('test', {})
@object.view('test')
end
end
@@ -174,29 +173,6 @@ class DesignModel < CouchRest::Model::Base
DesignModel.design_doc['filters'].should_not be_empty
DesignModel.design_doc['filters']['important'].should_not be_blank
end
-
- end
-
- describe "#create_view_method" do
- before :each do
- @object = @klass.new(DesignModel)
- end
-
- it "should create a method that returns view instance" do
- @object.design_doc.should_receive(:view).with('test_view', {}).and_return(nil)
- @object.send(:create_view_method, 'test_view')
- DesignModel.test_view
- end
-
- it "should create a method that returns quick access find_by method" do
- view = mock("View")
- view.stub(:key).and_return(view)
- view.stub(:first).and_return(true)
- @object.design_doc.should_receive(:view).with('by_test_view', {}).and_return(view)
- @object.send(:create_view_method, 'by_test_view')
- lambda { DesignModel.find_by_test_view('test').should be_true }.should_not raise_error
- end
-
end
end
View
4 spec/unit/property_protection_spec.rb
@@ -150,7 +150,9 @@ class WithProtected < CouchRest::Model::Base
use_database TEST_SERVER.default_database
property :name
property :admin, :default => false, :protected => true
- view_by :name
+ design do
+ view :by_name
+ end
end
before(:each) do
View
155 spec/unit/proxyable_spec.rb
@@ -148,141 +148,101 @@ def self.foo; puts 'bar'; end
@klass = CouchRest::Model::Proxyable::ModelProxy
end
- it "should initialize and set variables" do
- @obj = @klass.new(Cat, 'owner', 'owner_name', 'database')
- @obj.model.should eql(Cat)
- @obj.owner.should eql('owner')
- @obj.owner_name.should eql('owner_name')
- @obj.database.should eql('database')
+ before :each do
+ @design_doc = mock('Design')
+ @design_doc.stub!(:view_names).and_return(['all', 'by_name'])
+ @model = mock('Cat')
+ @model.stub!(:design_docs).and_return([@design_doc])
+ @obj = @klass.new(@model, 'owner', 'owner_name', 'database')
end
- describe "instance" do
+ describe "initialization" do
- before :each do
- @obj = @klass.new(Cat, 'owner', 'owner_name', 'database')
+ it "should set base attributes" do
+ @obj.model.should eql(@model)
+ @obj.owner.should eql('owner')
+ @obj.owner_name.should eql('owner_name')
+ @obj.database.should eql('database')
end
- it "should proxy new call" do
- @obj.should_receive(:proxy_block_update).with(:new, 'attrs', 'opts')
- @obj.new('attrs', 'opts')
+ it "should create view methods" do
+ @obj.should respond_to('all')
+ @obj.should respond_to('by_name')
+ @obj.should respond_to('find_all')
+ @obj.should respond_to('find_by_name')
end
- it "should proxy build_from_database" do
- @obj.should_receive(:proxy_block_update).with(:build_from_database, 'attrs', 'opts')
- @obj.build_from_database('attrs', 'opts')
+ it "should create 'all' view method that forward to model's view with proxy" do
+ @model.should_receive(:all).with(:proxy => @obj).and_return(nil)
+ @obj.all
end
- describe "#method_missing" do
- it "should return design view object" do
- m = "by_some_property"
- inst = mock('DesignView')
- inst.stub!(:proxy).and_return(inst)
- @obj.should_receive(:has_view?).with(m).and_return(true)
- Cat.should_receive(:respond_to?).with(m).and_return(true)
- Cat.should_receive(:send).with(m).and_return(inst)
- @obj.method_missing(m).should eql(inst)
- end
+ it "should create 'by_name' view method that forward to model's view with proxy" do
+ @model.should_receive(:by_name).with(:proxy => @obj).and_return(nil)
+ @obj.by_name
+ end
- it "should call view if necessary" do
- m = "by_some_property"
- @obj.should_receive(:has_view?).with(m).and_return(true)
- Cat.should_receive(:respond_to?).with(m).and_return(false)
- @obj.should_receive(:view).with(m, {}).and_return('view')
- @obj.method_missing(m).should eql('view')
- end
+ it "should create 'find_by_name' view that forwards to normal view" do
+ view = mock('view')
+ view.should_receive('key').with('name').and_return(view)
+ view.should_receive('first').and_return(nil)
+ @obj.should_receive(:by_name).and_return(view)
+ @obj.find_by_name('name')
+ end
- it "should provide wrapper for #first_from_view" do
- m = "find_by_some_property"
- view = "by_some_property"
- @obj.should_receive(:has_view?).with(m).and_return(false)
- @obj.should_receive(:has_view?).with(view).and_return(true)
- @obj.should_receive(:first_from_view).with(view).and_return('view')
- @obj.method_missing(m).should eql('view')
- end
+ end
+ describe "instance" do
+
+ it "should proxy new call" do
+ @obj.should_receive(:proxy_block_update).with(:new, 'attrs', 'opts')
+ @obj.new('attrs', 'opts')
end
- it "should proxy #all" do
- Cat.should_receive(:all).with({:database => 'database'})
- @obj.should_receive(:proxy_update_all)
- @obj.all
+ it "should proxy build_from_database" do
+ @obj.should_receive(:proxy_block_update).with(:build_from_database, 'attrs', 'opts')
+ @obj.build_from_database('attrs', 'opts')
end
-
+
it "should proxy #count" do
- Cat.should_receive(:all).with({:database => 'database', :raw => true, :limit => 0}).and_return({'total_rows' => 3})
- @obj.count.should eql(3)
+ view = mock('View')
+ view.should_receive(:count).and_return(nil)
+ @model.should_receive(:all).and_return(view)
+ @obj.count
end
it "should proxy #first" do
- Cat.should_receive(:first).with({:database => 'database'})
- @obj.should_receive(:proxy_update)
+ view = mock('View')
+ view.should_receive(:first).and_return(nil)
+ @model.should_receive(:all).and_return(view)
@obj.first
end
it "should proxy #last" do
- Cat.should_receive(:last).with({:database => 'database'})
- @obj.should_receive(:proxy_update)
+ view = mock('View')
+ view.should_receive(:last).and_return(nil)
+ @model.should_receive(:all).and_return(view)
@obj.last
end
it "should proxy #get" do
- Cat.should_receive(:get).with(32, 'database')
+ @model.should_receive(:get).with(32, 'database')
@obj.should_receive(:proxy_update)
@obj.get(32)
end
it "should proxy #find" do
- Cat.should_receive(:get).with(32, 'database')
+ @model.should_receive(:get).with(32, 'database')
@obj.should_receive(:proxy_update)
@obj.find(32)
end
- it "should proxy #has_view?" do
- Cat.should_receive(:has_view?).with('view').and_return(false)
- @obj.has_view?('view')
- end
-
- it "should proxy #view_by" do
- Cat.should_receive(:view_by).with('name').and_return(false)
- @obj.view_by('name')
- end
-
- it "should proxy #view" do
- Cat.should_receive(:view).with('view', {:database => 'database'})
- @obj.should_receive(:proxy_update_all)
- @obj.view('view')
- end
-
- it "should proxy #first_from_view" do
- Cat.should_receive(:first_from_view).with('view', {:database => 'database'})
- @obj.should_receive(:proxy_update)
- @obj.first_from_view('view')
- end
-
- it "should proxy design_doc" do
- Cat.should_receive(:design_doc)
- @obj.design_doc
- end
-
- describe "#save_design_doc" do
- it "should be proxied without args" do
- Cat.should_receive(:save_design_doc).with('database')
- @obj.save_design_doc
- end
-
- it "should be proxied with database arg" do
- Cat.should_receive(:save_design_doc).with('db')
- @obj.save_design_doc('db')
- end
- end
-
-
### Updating methods
describe "#proxy_update" do
it "should set returned doc fields" do
doc = mock(:Document)
- doc.should_receive(:is_a?).with(Cat).and_return(true)
+ doc.should_receive(:is_a?).with(@model).and_return(true)
doc.should_receive(:database=).with('database')
doc.should_receive(:model_proxy=).with(@obj)
doc.should_receive(:send).with('owner_name=', 'owner')
@@ -291,7 +251,7 @@ def self.foo; puts 'bar'; end
it "should not set anything if matching document not provided" do
doc = mock(:DocumentFoo)
- doc.should_receive(:is_a?).with(Cat).and_return(false)
+ doc.should_receive(:is_a?).with(@model).and_return(false)
doc.should_not_receive(:database=)
doc.should_not_receive(:model_proxy=)
doc.should_not_receive(:owner_name=)
@@ -343,13 +303,12 @@ class ProxyableInvoice < CouchRest::Model::Base
view :by_total
end
end
-
-
@company = ProxyableCompany.create(:slug => 'samco')
end
it "should create the new database" do
- @company.proxyable_invoices.all.should be_empty
+ view = @company.proxyable_invoices.all
+ view.should be_empty
TEST_SERVER.databases.find{|db| db =~ /#{TESTDB}-samco/}.should_not be_nil
end
View
8 spec/unit/subclass_spec.rb
@@ -17,13 +17,17 @@ class DesignBusinessCard < BusinessCard
class OnlineCourse < Course
property :url
- view_by :url
+ design do
+ view :by_url
+ end
end
class Animal < CouchRest::Model::Base
use_database TEST_SERVER.default_database
property :name
- view_by :name
+ design do
+ view :by_name
+ end
end
class Dog < Animal; end
View
26 spec/unit/validations_spec.rb
@@ -10,7 +10,7 @@
end
it "should create a new view if none defined before performing" do
- WithUniqueValidation.has_view?(:by_title).should be_true
+ WithUniqueValidation.design_doc.has_view?(:by_title).should be_true
end
it "should validate a new unique document" do
@@ -49,24 +49,22 @@
it "should not try to create a defined view" do
WithUniqueValidationView.validates_uniqueness_of :title, :view => 'fooobar'
- WithUniqueValidationView.has_view?('fooobar').should be_false
- WithUniqueValidationView.has_view?('by_title').should be_false
+ WithUniqueValidationView.design_doc.has_view?('fooobar').should be_false
+ WithUniqueValidationView.design_doc.has_view?('by_title').should be_false
end
it "should not try to create new view when already defined" do
@obj = @objs[1]
- @obj.class.should_not_receive('view_by')
- @obj.class.should_receive('has_view?').and_return(true)
- @obj.class.should_receive('view').and_return({'rows' => [ ]})
+ @obj.class.design_doc.should_not_receive('create_view')
@obj.valid?
end
end
context "with a proxy parameter" do
it "should create a new view despite proxy" do
- WithUniqueValidationProxy.has_view?(:by_title).should be_true
+ WithUniqueValidationProxy.design_doc.has_view?(:by_title).should be_true
end
it "should be used" do
@@ -77,19 +75,23 @@
it "should allow specific view" do
@obj = WithUniqueValidationProxy.new(:title => 'test 7')
- @obj.class.should_not_receive('view_by')
+ @obj.class.should_not_receive('by_title')
+ view = mock('View')
+ view.stub!(:rows).and_return([])
proxy = mock('Proxy')
+ proxy.should_receive('by_title').and_return(view)
+ proxy.should_receive('respond_to?').with('by_title').and_return(true)
@obj.should_receive('proxy').and_return(proxy)
- proxy.should_receive('has_view?').and_return(true)
- proxy.should_receive('view').and_return({'rows' => [ ]})
@obj.valid?
end
end
context "when proxied" do
it "should lookup the model_proxy" do
+ view = mock('View')
+ view.stub!(:rows).and_return([])
mp = mock(:ModelProxy)
- mp.should_receive(:view).and_return({'rows' => []})
+ mp.should_receive(:by_title).and_return(view)
@obj = WithUniqueValidation.new(:title => 'test 8')
@obj.stub!(:model_proxy).twice.and_return(mp)
@obj.valid?
@@ -103,7 +105,7 @@
end
it "should create the view" do
- @objs.first.class.has_view?('by_parent_id_and_title')
+ @objs.first.class.design_doc.has_view?('by_parent_id_and_title')
end
it "should validate unique document" do

0 comments on commit 7f0d1a1

Please sign in to comment.