Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Continuing modifications to get new design docs working

  • Loading branch information...
commit 88def18b484c05a5da9a1b22ed29816f610edde3 1 parent fa5ef9e
Sam Lown samlown authored
7 lib/couchrest/model/designs.rb
View
@@ -48,6 +48,10 @@ def default_per_page
@_default_per_page || 25
end
+ def design_docs
+ @_design_docs ||= []
+ end
+
end
@@ -68,6 +72,9 @@ def initialize(model, prefix = nil)
create_design_doc_method
self.design_doc = model.send(method)
+ # 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
end
22 lib/couchrest/model/designs/design.rb
View
@@ -37,7 +37,7 @@ def sync(db = nil)
# Load up the last copy. We never overwrite the remote copy
# as it may contain views that are not used or known about by
# our model.
- doc = load_from_database(database)
+ doc = load_from_database(db)
if !doc || doc['couchrest-hash'] != checksum
# We need to save something
@@ -56,6 +56,7 @@ def sync(db = nil)
self
end
+
def checksum
sum = self['couchrest-hash']
if sum && (@_original_hash == to_hash)
@@ -69,6 +70,18 @@ def database
model.database
end
+ # Override the default #uri method for one that accepts
+ # the current database.
+ # This is used by the caching code.
+ def uri(db = database)
+ "#{db.root}/#{self['_id']}"
+ end
+
+ # Helper method to provide a list of all the views
+ def view_names
+ self['views'].keys
+ end
+
protected
def load_from_database(db = database)
@@ -108,13 +121,6 @@ def checksum!
self['couchrest-hash'] = Digest::MD5.hexdigest(flatten.call(base).sort.join(''))
end
- # Override the default #uri method for one that accepts
- # the current database.
- # This is used by the caching code.
- def uri(db)
- "#{db.root}/#{self['_id']}"
- end
-
def cache
Thread.current[:couchrest_design_cache] ||= {}
end
4 lib/couchrest/model/designs/view.rb
View
@@ -310,8 +310,8 @@ def database(value)
# Set the view's proxy that will be used instead of the model
# for any future searches. As soon as this enters the
- # new object's initializer it will be removed and replace
- # the model object.
+ # new view's initializer it will be removed and set as the model
+ # object.
#
# See the Proxyable mixin for more details.
#
74 lib/couchrest/model/proxyable.rb
View
@@ -70,6 +70,8 @@ def initialize(model, owner, owner_name, database)
@owner = owner
@owner_name = owner_name
@database = database
+
+ create_view_methods
end
# Base
@@ -81,39 +83,18 @@ def build_from_database(attrs = {}, options = {}, &block)
proxy_block_update(:build_from_database, attrs, options, &block)
end
- def method_missing(m, *args, &block)
- if has_view?(m)
- if model.respond_to?(m)
- return model.send(m, *args).proxy(self)
- else
- query = args.shift || {}
- return view(m, query, *args, &block)
- end
- 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)
- proxy_update_all(@model.all({:database => @database}.merge(opts), &block))
- end
+ # From DocumentQueries (The old fashioned way)
def count(opts = {})
- @model.count({:database => @database}.merge(opts))
+ all(opts).count
end
def first(opts = {})
- proxy_update(@model.first({:database => @database}.merge(opts)))
+ all(opts).first
end
def last(opts = {})
- proxy_update(@model.last({:database => @database}.merge(opts)))
+ all(opts).last
end
def get(id)
@@ -121,38 +102,23 @@ def get(id)
end
alias :find :get
- # Views
-
- def has_view?(view)
- @model.has_view?(view)
- end
-
- def view_by(*args)
- @model.view_by(*args)
- end
-
- def view(name, query={}, &block)
- proxy_update_all(@model.view(name, {:database => @database}.merge(query), &block))
- 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
- proxy_update(@model.first_from_view(name, *args))
- end
-
- # DesignDoc
- def design_doc
- @model.design_doc
- end
+ protected
- def save_design_doc(db = nil)
- @model.save_design_doc(db || @database)
+ 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))
+ end
+ def self.find_#{name}(*key)
+ #{name}.key(*key).first()
+ end
+ EOS
+ end
+ end
end
-
- protected
-
# Update the document's proxy details, specifically, the fields that
# link back to the original document.
def proxy_update(doc)
4 spec/unit/class_proxy_spec.rb
View
@@ -5,7 +5,9 @@ class UnattachedDoc < CouchRest::Model::Base
property :title
property :questions
property :professor
- view_by :title
+ design do
+ view :title
+ end
end
227 spec/unit/design_doc_spec.rb
View
@@ -1,227 +0,0 @@
-# encoding: utf-8
-require 'spec_helper'
-
-describe CouchRest::Model::DesignDoc do
-
- before :all do
- reset_test_db!
- end
-
- describe "CouchRest Extension" do
-
- it "should have created a checksum! method" do
- ::CouchRest::Design.new.should respond_to(:checksum!)
- end
-
- it "should calculate a consistent checksum for model" do
- WithTemplateAndUniqueID.design_doc.checksum!.should eql('caa2b4c27abb82b4e37421de76d96ffc')
- end
-
- it "should calculate checksum for complex model" do
- Article.design_doc.checksum!.should eql('70dff8caea143bf40fad09adf0701104')
- end
-
- it "should cache the generated checksum value" do
- Article.design_doc.checksum!
- Article.design_doc['couchrest-hash'].should_not be_blank
- end
- end
-
- describe "class methods" do
-
- describe ".design_doc" do
- it "should provide Design document" do
- Article.design_doc.should be_a(::CouchRest::Design)
- end
- end
-
- describe ".design_doc_id" do
- it "should provide a reasonable id" do
- Article.design_doc_id.should eql("_design/Article")
- end
- end
-
- describe ".design_doc_slug" do
- it "should provide slug part of design doc" do
- Article.design_doc_slug.should eql('Article')
- end
- end
-
- describe ".design_doc_uri" do
- it "should provide complete url" do
- Article.design_doc_uri.should eql("#{COUCHHOST}/#{TESTDB}/_design/Article")
- end
- it "should provide complete url for new DB" do
- db = mock("Database")
- db.should_receive(:root).and_return('db')
- Article.design_doc_uri(db).should eql("db/_design/Article")
- end
- end
-
- describe ".stored_design_doc" do
- it "should load a stored design from the database" do
- Article.by_date
- Article.stored_design_doc['_rev'].should_not be_blank
- end
- it "should return nil if not already stored" do
- WithDefaultValues.stored_design_doc.should be_nil
- end
- end
-
- describe ".save_design_doc" do
- it "should call up the design updater" do
- Article.should_receive(:update_design_doc).with('db', false)
- Article.save_design_doc('db')
- end
- end
-
- describe ".save_design_doc!" do
- it "should call save_design_doc with force" do
- Article.should_receive(:save_design_doc).with('db', true)
- Article.save_design_doc!('db')
- end
- end
-
- end
-
- describe "basics" do
-
- before :all do
- reset_test_db!
- end
-
- it "should have been instantiated with views" do
- d = Article.design_doc
- d['views']['all']['map'].should include('Article')
- end
-
- it "should not have been saved yet" do
- lambda { Article.database.get(Article.design_doc.id) }.should raise_error(RestClient::ResourceNotFound)
- end
-
- describe "after requesting a view" do
- before :each do
- Article.all
- end
- it "should have saved the design doc after view request" do
- Article.database.get(Article.design_doc.id).should_not be_nil
- end
- end
-
- describe "model with simple views" do
- before(:all) do
- Article.all.map{|a| a.destroy(true)}
- Article.database.bulk_delete
- written_at = Time.now - 24 * 3600 * 7
- @titles = ["this and that", "also interesting", "more fun", "some junk"]
- @titles.each do |title|
- a = Article.new(:title => title)
- a.date = written_at
- a.save
- written_at += 24 * 3600
- end
- end
-
- it "will send request for the saved design doc on view request" do
- reset_test_db!
- Article.should_receive(:stored_design_doc).and_return(nil)
- Article.by_date
- end
-
- it "should have generated a design doc" do
- Article.design_doc["views"]["by_date"].should_not be_nil
- end
- it "should save the design doc when view requested" do
- Article.by_date
- doc = Article.database.get Article.design_doc.id
- doc['views']['by_date'].should_not be_nil
- end
- it "should save design doc if a view changed" do
- Article.by_date
- orig = Article.stored_design_doc
- design = Article.design_doc
- view = design['views']['by_date']['map']
- design['views']['by_date']['map'] = view + ' ' # little bit of white space
- Article.by_date
- Article.stored_design_doc['_rev'].should_not eql(orig['_rev'])
- orig['views']['by_date']['map'].should_not eql(Article.design_doc['views']['by_date']['map'])
- end
- it "should not save design doc if not changed" do
- Article.by_date
- orig = Article.stored_design_doc['_rev']
- Article.by_date
- Article.stored_design_doc['_rev'].should eql(orig)
- end
- it "should recreate the design doc if database deleted" do
- Article.database.recreate!
- lambda { Article.by_date }.should_not raise_error(RestClient::ResourceNotFound)
- end
- end
-
- describe "when auto_update_design_doc false" do
- # We really do need a new class for each of example. If we try
- # to use the same class the examples interact with each in ways
- # that can hide failures because the design document gets cached
- # at the class level.
- let(:model_class) {
- model_class = Article.dup
- model_class.class_eval do
- self.auto_update_design_doc = false
- design do
- view :by_title
- end
- end
- doc = DB.get("_design/Article")
- DB.delete_doc doc if doc
- doc = CouchRest::Document.new("_id" => "_design/Article")
- doc["language"] = "javascript"
- doc["views"] = {"all" => {"map" => "function(doc) { if (doc['type'] == 'Article') { emit(doc['_id'],1); } }"},
- "by_title" => {"map" =>
- "function(doc) {
- if ((doc['type'] == 'Article') && (doc['title'] != null)) {
- emit(doc['title'], null);
- }", "reduce" => "function(k,v,r) { return sum(v); }"}}
- DB.save_doc doc
- model_class
- }
-
- it "will not update stored design doc if view changed" do
- model_class.by_title # Perform the view
- orig = model_class.stored_design_doc
- design = model_class.design_doc
- view = design['views']['by_title']['map']
- design['views']['by_title']['map'] = view + ' '
- model_class.by_name
- model_class.stored_design_doc['_rev'].should eql(orig['_rev'])
- end
-
- it "will update stored design if forced" do
- model_class.by_title
- orig = model_class.stored_design_doc
- design = model_class.design_doc
- view = design['views']['by_title']['map']
- design['views']['by_title']['map'] = view + ' '
- model_class.save_design_doc!
- model_class.stored_design_doc['_rev'].should_not eql(orig['_rev'])
- end
-
- it "is able to use predefined views" do
- lambda { model_class.by_title.key("special").all }.should_not raise_error
- end
- end
- end
-
- describe "lazily refreshing the design document" do
- before(:all) do
- @db = reset_test_db!
- WithTemplateAndUniqueID.new('slug' => '1').save
- end
- it "should not save the design doc twice" do
- WithTemplateAndUniqueID.all
- rev = WithTemplateAndUniqueID.design_doc['_rev']
- WithTemplateAndUniqueID.design_doc['_rev'].should eql(rev)
- end
- end
-
-
-end
28 spec/unit/designs/design_spec.rb
View
@@ -9,6 +9,7 @@
end
class DesignSampleModel < CouchRest::Model::Base
+ use_database DB
property :name
property :surname
design do
@@ -77,11 +78,16 @@ class DesignSampleModel < CouchRest::Model::Base
describe "with real model" do
before :all do
+ reset_test_db!
@mod = DesignSampleModel
@doc = @mod.design_doc
@db = @mod.database
end
+ it "should not have been saved up until sync called" do
+ lambda { @mod.database.get(@doc['_id']) }.should raise_error(RestClient::ResourceNotFound)
+ end
+
it "should save a non existant design" do
begin
doc = @db.get(@doc['_id'])
@@ -129,6 +135,13 @@ class DesignSampleModel < CouchRest::Model::Base
@doc['views'].should_not have_key('test')
end
+ it "should be re-created if database destroyed" do
+ @doc.sync # saved
+ reset_test_db!
+ @db.should_receive(:save_doc).with(@doc)
+ @doc.sync
+ end
+
end
end
@@ -167,6 +180,21 @@ class DesignSampleModel < CouchRest::Model::Base
end
end
+
+ describe "#uri" do
+ it "should provide complete url" do
+ @doc = DesignSampleModel.design_doc
+ @doc.uri.should eql("#{DesignSampleModel.database.root}/_design/DesignSampleModel")
+ 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
+
end
214 spec/unit/designs_spec.rb
View
@@ -1,6 +1,7 @@
require "spec_helper"
class DesignModel < CouchRest::Model::Base
+ use_database DB
end
describe CouchRest::Model::Designs do
@@ -62,7 +63,7 @@ class DesignModel < CouchRest::Model::Base
@klass = CouchRest::Model::Designs::DesignMapper
end
- describe 'initialize with prefix' do
+ describe 'initialize without prefix' do
before :all do
@object = @klass.new(DesignModel)
@@ -74,8 +75,8 @@ class DesignModel < CouchRest::Model::Base
@object.send(:method).should eql('design_doc')
end
- it "should create an all method" do
- @object.model.should respond_to('all')
+ it "should add design doc to list" do
+ @object.model.design_docs.should include(@object.model.design_doc)
end
it "should create a design doc method" do
@@ -100,8 +101,12 @@ class DesignModel < CouchRest::Model::Base
@object.send(:method).should eql('stats_design_doc')
end
+ it "should add design doc to list" do
+ @object.model.design_docs.should include(@object.model.stats_design_doc)
+ end
+
it "should not create an all method" do
- @object.model.should respond_to('all')
+ @object.model.should_not respond_to('all')
end
it "should create a design doc method" do
@@ -141,18 +146,18 @@ class DesignModel < CouchRest::Model::Base
end
it "should call create method on view" do
- CouchRest::Model::Designs::View.should_receive(:create).with(DesignModel, @object.design_doc, 'test', {})
+ CouchRest::Model::Designs::View.should_receive(:define).with(DesignModel, @object.design_doc, 'test', {})
@object.view('test')
end
it "should create a method on parent model" do
- CouchRest::Model::Designs::View.stub!(:create)
+ CouchRest::Model::Designs::View.stub!(:define)
@object.view('test_view')
DesignModel.should respond_to(:test_view)
end
it "should create a method for view instance" do
- CouchRest::Model::Designs::View.stub!(:create)
+ CouchRest::Model::Designs::View.stub!(:define)
@object.should_receive(:create_view_method).with('test')
@object.view('test')
end
@@ -178,7 +183,7 @@ class DesignModel < CouchRest::Model::Base
end
it "should create a method that returns view instance" do
- @object.design_doc.should_receive(:view).with({}, 'test_view').and_return(nil)
+ @object.design_doc.should_receive(:view).with('test_view', {}).and_return(nil)
@object.send(:create_view_method, 'test_view')
DesignModel.test_view
end
@@ -187,7 +192,7 @@ class DesignModel < CouchRest::Model::Base
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.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
@@ -196,4 +201,195 @@ class DesignModel < CouchRest::Model::Base
end
+
+ class DesignsNoAutoUpdate < CouchRest::Model::Base
+ use_database DB
+ property :title, String
+ design do
+ disable_auto_update
+ view :by_title_fail, :by => ['title']
+ view :by_title, :reduce => true
+ end
+ end
+
+ describe "Scenario testing" do
+
+ describe "with auto update disabled" do
+
+ before :all do
+ reset_test_db!
+ @mod = DesignsNoAutoUpdate
+ end
+
+ before(:all) do
+ id = @mod.to_s
+ doc = CouchRest::Document.new("_id" => "_design/#{id}")
+ doc["language"] = "javascript"
+ doc["views"] = {"all" => {"map" => "function(doc) { if (doc['type'] == '#{id}') { emit(doc['_id'],1); } }"},
+ "by_title" => {"map" =>
+ "function(doc) {
+ if ((doc['type'] == '#{id}') && (doc['title'] != null)) {
+ emit(doc['title'], 1);
+ }
+ }", "reduce" => "function(k,v,r) { return sum(v); }"}}
+ DB.save_doc doc
+ end
+
+ it "will fail if reduce is not specific in view" do
+ @mod.create(:title => 'This is a test')
+ lambda { @mod.by_title_fail.first }.should raise_error(RestClient::ResourceNotFound)
+ end
+
+ it "will perform view request" do
+ @mod.create(:title => 'This is a test')
+ @mod.by_title.first.title.should eql("This is a test")
+ end
+
+ end
+
+ describe "using views" do
+
+ describe "to find a single item" do
+
+ before(:all) do
+ reset_test_db!
+ %w{aaa bbb ddd eee}.each do |title|
+ Course.new(:title => title, :active => (title == 'bbb')).save
+ end
+ end
+
+ it "should return single matched record with find helper" do
+ course = Course.find_by_title('bbb')
+ course.should_not be_nil
+ course.title.should eql('bbb') # Ensure really is a Course!
+ end
+
+ it "should return nil if not found" do
+ course = Course.find_by_title('fff')
+ course.should be_nil
+ end
+
+ it "should peform search on view with two properties" do
+ course = Course.find_by_title_and_active(['bbb', true])
+ course.should_not be_nil
+ course.title.should eql('bbb') # Ensure really is a Course!
+ end
+
+ it "should return nil if not found" do
+ course = Course.find_by_title_and_active(['bbb', false])
+ course.should be_nil
+ end
+
+ it "should raise exception if view not present" do
+ lambda { Course.find_by_foobar('123') }.should raise_error(NoMethodError)
+ end
+
+ end
+
+ describe "a model class with database provided manually" do
+
+ class Unattached < CouchRest::Model::Base
+ property :title
+ property :questions
+ property :professor
+ design do
+ view :by_title
+ end
+
+ # Force the database to always be nil
+ def self.database
+ nil
+ end
+ end
+
+ before(:all) do
+ reset_test_db!
+ @db = DB
+ %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.first}.should raise_error
+ end
+ it "should query all" do
+ rs = Unattached.all.database(@db).all
+ rs.length.should == 4
+ end
+ it "should barf on query if no database given" do
+ lambda{Unattached.by_title.all}.should raise_error /Database must be defined/
+ 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 return nil on get if no database given" do
+ Unattached.get("aaa").should be_nil
+ 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.all.database(@db).first
+ u.title.should =~ /\A...\z/
+ end
+ it "should get last" do
+ u = Unattached.all.database(@db).last
+ u.title.should == "aaa"
+ end
+
+ end
+
+ describe "a model with a compound key view" do
+ before(:all) do
+ reset_test_db!
+ written_at = Time.now - 24 * 3600 * 7
+ @titles = ["uniq one", "even more interesting", "less fun", "not junk"]
+ @user_ids = ["quentin", "aaron"]
+ @titles.each_with_index do |title,i|
+ u = i % 2
+ a = Article.new(:title => title, :user_id => @user_ids[u])
+ a.date = written_at
+ a.save
+ written_at += 24 * 3600
+ end
+ end
+ it "should create the design doc" do
+ Article.by_user_id_and_date rescue nil
+ doc = Article.design_doc
+ doc['views']['by_date'].should_not be_nil
+ end
+ it "should sort correctly" do
+ articles = Article.by_user_id_and_date.all
+ articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin',
+ 'quentin']
+ articles[1].title.should == 'not junk'
+ end
+ it "should be queryable with couchrest options" do
+ articles = Article.by_user_id_and_date(:limit => 1, :startkey => 'quentin').all
+ articles.length.should == 1
+ articles[0].title.should == "even more interesting"
+ end
+ end
+
+
+ end
+
+ end
+
end
367 spec/unit/view_spec.rb
View
@@ -1,367 +0,0 @@
-require "spec_helper"
-
-describe CouchRest::Model::Views do
-
- class Unattached < CouchRest::Model::Base
- property :title
- property :questions
- property :professor
- view_by :title
-
- # Force the database to always be nil
- def self.database
- nil
- end
- end
-
- describe "ClassMethods" do
- # NOTE! Add more unit tests!
-
- describe "#view" do
-
- it "should not alter original query" do
- options = { :database => DB }
- view = Article.view('by_date', options)
- options[:database].should eql(DB)
- 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
- before(:all) do
- Article.all.map{|a| a.destroy(true)}
- Article.database.bulk_delete
- written_at = Time.now - 24 * 3600 * 7
- @titles = ["this and that", "also interesting", "more fun", "some junk"]
- @titles.each do |title|
- a = Article.new(:title => title)
- a.date = written_at
- a.save
- written_at += 24 * 3600
- end
- end
- it "should return the matching raw view result" do
- view = Article.by_date :raw => true
- view['rows'].length.should == 4
- end
- it "should not include non-Articles" do
- Article.database.save_doc({"date" => 1})
- view = Article.by_date :raw => true
- view['rows'].length.should == 4
- end
- it "should return the matching objects (with default argument :descending => true)" do
- articles = Article.by_date
- articles.collect{|a|a.title}.should == @titles.reverse
- end
- it "should allow you to override default args" do
- articles = Article.by_date :descending => false
- articles.collect{|a|a.title}.should == @titles
- end
- it "should allow you to create a new view on the fly" do
- lambda{Article.by_title}.should raise_error
- Article.view_by :title
- lambda{Article.by_title}.should_not raise_error
- end
- end
-
- describe "another model with a simple view" do
- before(:all) do
- reset_test_db!
- %w{aaa bbb ddd eee}.each do |title|
- Course.new(:title => title).save
- end
- end
- it "should make the design doc upon first query" do
- Course.by_title
- doc = Course.design_doc
- doc['views']['all']['map'].should include('Course')
- end
- it "should can query via view" do
- # register methods with method-missing, for local dispatch. method
- # missing lookup table, no heuristics.
- view = Course.view :by_title
- designed = Course.by_title
- view.should == designed
- end
- it "should get them" do
- rs = Course.by_title
- rs.length.should == 4
- end
- it "should yield" do
- courses = []
- 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
-
- describe "find a single item using a view" do
- before(:all) do
- reset_test_db!
- %w{aaa bbb ddd eee}.each do |title|
- Course.new(:title => title, :active => (title == 'bbb')).save
- end
- end
-
- it "should return single matched record with find helper" do
- course = Course.find_by_title('bbb')
- course.should_not be_nil
- course.title.should eql('bbb') # Ensure really is a Course!
- end
-
- it "should return nil if not found" do
- course = Course.find_by_title('fff')
- course.should be_nil
- end
-
- it "should peform search on view with two properties" do
- course = Course.find_by_title_and_active(['bbb', true])
- course.should_not be_nil
- course.title.should eql('bbb') # Ensure really is a Course!
- end
-
- it "should return nil if not found" do
- course = Course.find_by_title_and_active(['bbb', false])
- course.should be_nil
- end
-
- it "should raise exception if view not present" do
- lambda { Course.find_by_foobar('123') }.should raise_error(NoMethodError)
- end
-
- it "should perform a search directly with specific key" do
- course = Course.first_from_view('by_title', 'bbb')
- course.title.should eql('bbb')
- end
-
- it "should perform a search directly with specific key with options" do
- course = Course.first_from_view('by_title', 'bbb', :reverse => true)
- course.title.should eql('bbb')
- end
-
- it "should perform a search directly with range" do
- course = Course.first_from_view('by_title', :startkey => 'bbb', :endkey => 'eee')
- 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 "#method_missing for find_by methods" do
- before(:all) { reset_test_db! }
-
- specify { Course.should respond_to :find_by_title_and_active }
- specify { Course.should respond_to :by_title }
-
- specify "#method should work in ruby 1.9, but not 1.8" do
- if RUBY_VERSION >= "1.9"
- Course.method(:find_by_title_and_active).should be_a Method
- else
- expect { Course.method(:find_by_title_and_active) }.to raise_error(NameError)
- end
- end
- end
-
- describe "a ducktype view" do
- before(:all) do
- reset_test_db!
- @id = DB.save_doc({:dept => true})['id']
- end
- it "should setup" do
- duck = Course.get(@id) # from a different db
- duck["dept"].should == true
- end
- it "should make the design doc" do
- @as = Course.by_dept
- @doc = Course.design_doc
- @doc["views"]["by_dept"]["map"].should_not include("couchrest")
- end
- it "should not look for class" do
- @as = Course.by_dept
- @as[0]['_id'].should == @id
- end
- end
-
- describe "a model class with database provided manually" do
- before(:all) do
- reset_test_db!
- @db = DB
- %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 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 return nil on get if no database given" do
- Unattached.get("aaa").should be_nil
- 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 get last" do
- u = Unattached.last :database=>@db
- u.title.should == "aaa"
- end
-
- end
-
- describe "a model with a compound key view" do
- before(:all) do
- Article.by_user_id_and_date.each{|a| a.destroy(true)}
- Article.database.bulk_delete
- written_at = Time.now - 24 * 3600 * 7
- @titles = ["uniq one", "even more interesting", "less fun", "not junk"]
- @user_ids = ["quentin", "aaron"]
- @titles.each_with_index do |title,i|
- u = i % 2
- a = Article.new(:title => title, :user_id => @user_ids[u])
- a.date = written_at
- a.save
- written_at += 24 * 3600
- end
- end
- it "should create the design doc" do
- Article.by_user_id_and_date rescue nil
- doc = Article.design_doc
- doc['views']['by_date'].should_not be_nil
- end
- it "should sort correctly" do
- articles = Article.by_user_id_and_date
- articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin',
- 'quentin']
- articles[1].title.should == 'not junk'
- end
- it "should be queryable with couchrest options" do
- articles = Article.by_user_id_and_date :limit => 1, :startkey => 'quentin'
- articles.length.should == 1
- articles[0].title.should == "even more interesting"
- end
- end
-
- describe "with a custom view" do
- before(:all) do
- @titles = ["very uniq one", "even less interesting", "some fun",
- "really junk", "crazy bob"]
- @tags = ["cool", "lame"]
- @titles.each_with_index do |title,i|
- u = i % 2
- a = Article.new(:title => title, :tags => [@tags[u]])
- a.save
- end
- end
- it "should be available raw" do
- view = Article.by_tags :raw => true
- view['rows'].length.should == 5
- end
-
- it "should be default to :reduce => false" do
- ars = Article.by_tags
- ars.first.tags.first.should == 'cool'
- end
-
- it "should be raw when reduce is true" do
- view = Article.by_tags :reduce => true, :group => true
- view['rows'].find{|r|r['key'] == 'cool'}['value'].should == 3
- end
- end
-
- # TODO: moved to Design, delete
- describe "adding a view" do
- before(:each) do
- reset_test_db!
- Article.by_date
- @original_doc_rev = Article.stored_design_doc['_rev']
- @design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
- end
- it "should not create a design doc on view definition" do
- Article.view_by :created_at
- newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
- newdocs["rows"].length.should == @design_docs["rows"].length
- end
- it "should create a new version of the design document on view access" do
- Article.view_by :updated_at
- Article.by_updated_at
- @original_doc_rev.should_not == Article.stored_design_doc['_rev']
- Article.design_doc["views"].keys.should include("by_updated_at")
- end
- end
-
-end
Please sign in to comment.
Something went wrong with that request. Please try again.