From c3b7cc316d301227c203c1fe9573c58c8add674d Mon Sep 17 00:00:00 2001 From: Lucas Renan Date: Sun, 15 May 2011 14:41:13 -0300 Subject: [PATCH 01/34] adding config generator --- README.md | 4 ++++ .../config/config_generator.rb | 18 ++++++++++++++++ .../config/templates/couchdb.yml | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 lib/rails/generators/couchrest_model/config/config_generator.rb create mode 100644 lib/rails/generators/couchrest_model/config/templates/couchdb.yml diff --git a/README.md b/README.md index 6c6fcdb..1e2b69b 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,10 @@ The example config above for example would use a database called "project_test". ## Generators +### Configuration + + $ rails generate couchrest_model:config + ### Model $ rails generate model person --orm=couchrest_model diff --git a/lib/rails/generators/couchrest_model/config/config_generator.rb b/lib/rails/generators/couchrest_model/config/config_generator.rb new file mode 100644 index 0000000..b78b4d5 --- /dev/null +++ b/lib/rails/generators/couchrest_model/config/config_generator.rb @@ -0,0 +1,18 @@ +require 'rails/generators/couchrest_model' + +module CouchrestModel + module Generators + class ConfigGenerator < Rails::Generators::Base + source_root File.expand_path('../templates', __FILE__) + + def app_name + Rails::Application.subclasses.first.parent.to_s.underscore + end + + def copy_configuration_file + template 'couchdb.yml', File.join('config', "couchdb.yml") + end + + end + end +end \ No newline at end of file diff --git a/lib/rails/generators/couchrest_model/config/templates/couchdb.yml b/lib/rails/generators/couchrest_model/config/templates/couchdb.yml new file mode 100644 index 0000000..2216a90 --- /dev/null +++ b/lib/rails/generators/couchrest_model/config/templates/couchdb.yml @@ -0,0 +1,21 @@ +development: &development + protocol: 'http' + host: localhost + port: 5984 + prefix: <%= app_name %> + suffix: development + username: + password: + +test: + <<: *development + suffix: test + +production: + protocol: 'https' + host: localhost + port: 5984 + prefix: <%= app_name %> + suffix: production + username: root + password: 123 \ No newline at end of file From 9b8564d9a8953b0420c7f139e8b404a05b0ee790 Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Mon, 23 May 2011 11:02:08 -0600 Subject: [PATCH 02/34] Implement #get! and #find! class methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcos Tapajós --- lib/couchrest/model/class_proxy.rb | 9 +++++++- lib/couchrest/model/document_queries.rb | 5 ++++- lib/couchrest/model/errors.rb | 2 ++ spec/couchrest/class_proxy_spec.rb | 29 +++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/model/class_proxy.rb b/lib/couchrest/model/class_proxy.rb index 3f988b1..16887a3 100644 --- a/lib/couchrest/model/class_proxy.rb +++ b/lib/couchrest/model/class_proxy.rb @@ -87,7 +87,14 @@ module CouchRest 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) diff --git a/lib/couchrest/model/document_queries.rb b/lib/couchrest/model/document_queries.rb index af1083d..839674b 100644 --- a/lib/couchrest/model/document_queries.rb +++ b/lib/couchrest/model/document_queries.rb @@ -86,9 +86,12 @@ module CouchRest # id:: Document ID # db:: optional option to pass a custom database to use def get!(id, db = database) - raise "Missing or empty document ID" if id.to_s.empty? + raise CouchRest::Model::DocumentNotFound if id.blank? + doc = db.get id build_from_database(doc) + rescue RestClient::ResourceNotFound + raise CouchRest::Model::DocumentNotFound end alias :find! :get! diff --git a/lib/couchrest/model/errors.rb b/lib/couchrest/model/errors.rb index 7f14fca..45d8b2f 100644 --- a/lib/couchrest/model/errors.rb +++ b/lib/couchrest/model/errors.rb @@ -19,5 +19,7 @@ module CouchRest end end end + + class DocumentNotFound < Errors::CouchRestModelError; end end end diff --git a/spec/couchrest/class_proxy_spec.rb b/spec/couchrest/class_proxy_spec.rb index f274ca9..1572d8d 100644 --- a/spec/couchrest/class_proxy_spec.rb +++ b/spec/couchrest/class_proxy_spec.rb @@ -123,6 +123,35 @@ describe "Proxy Class" do 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. From 4e19fdfddaf462529d0f43bf807060f53dc448b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Mon, 30 May 2011 22:00:56 -0300 Subject: [PATCH 03/34] History update --- history.md | 1 + 1 file changed, 1 insertion(+) diff --git a/history.md b/history.md index 24d0bd3..02a0a00 100644 --- a/history.md +++ b/history.md @@ -5,6 +5,7 @@ * New Features * Properties with a nil value are now no longer sent to the database. * Now possible to build new objects via CastedArray#build + * Implement #get! and #find! class methods * Minor fixes * #as_json now correctly uses ActiveSupports methods. From 2b9a5193b43b60694637a1f71d9d976150c15caa Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Tue, 31 May 2011 16:03:08 -0600 Subject: [PATCH 04/34] add init block support to #create and #create! --- lib/couchrest/model/persistence.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/couchrest/model/persistence.rb b/lib/couchrest/model/persistence.rb index e77b663..21f6455 100644 --- a/lib/couchrest/model/persistence.rb +++ b/lib/couchrest/model/persistence.rb @@ -118,8 +118,8 @@ module CouchRest # # ==== Returns # returns the reloaded document - def create(attributes = {}) - instance = new(attributes) + def create(attributes = {}, &block) + instance = new(attributes, &block) instance.create instance end @@ -128,8 +128,8 @@ module CouchRest # # ==== Returns # returns the reloaded document or raises an exception - def create!(attributes = {}) - instance = new(attributes) + def create!(attributes = {}, &block) + instance = new(attributes, &block) instance.create! instance end From 53bc7637e208cdee416129c8de90b2eacb85d619 Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Tue, 31 May 2011 16:31:37 -0600 Subject: [PATCH 05/34] tests for #create and #create! init blocks --- lib/couchrest/model/persistence.rb | 4 ++-- spec/couchrest/persistence_spec.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/model/persistence.rb b/lib/couchrest/model/persistence.rb index 21f6455..05d199b 100644 --- a/lib/couchrest/model/persistence.rb +++ b/lib/couchrest/model/persistence.rb @@ -21,8 +21,8 @@ module CouchRest # Creates the document in the db. Raises an exception # if the document is not created properly. - def create! - self.class.fail_validate!(self) unless self.create + def create!(options = {}) + self.class.fail_validate!(self) unless self.create(options) end # Trigger the callbacks (before, after, around) diff --git a/spec/couchrest/persistence_spec.rb b/spec/couchrest/persistence_spec.rb index 86ded62..6e04f30 100644 --- a/spec/couchrest/persistence_spec.rb +++ b/spec/couchrest/persistence_spec.rb @@ -81,6 +81,18 @@ describe "Model Persistence" do article.should_not be_new end + it "yields new instance to block before saving (#create)" do + article = Article.create{|a| a.title = 'my create init block test'} + article.title.should == 'my create init block test' + article.should_not be_new + end + + it "yields new instance to block before saving (#create!)" do + article = Article.create{|a| a.title = 'my create bang init block test'} + article.title.should == 'my create bang init block test' + article.should_not be_new + end + it "should trigger the create callbacks" do doc = WithCallBacks.create(:name => 'my other test') doc.run_before_create.should be_true From 36157a01d37d49ddca7f2ec0dc03288622b9fdab Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sun, 5 Jun 2011 11:21:01 +0200 Subject: [PATCH 06/34] Destroy method now freezes instead of removing ids --- history.md | 2 ++ lib/couchrest/model/persistence.rb | 10 ++++++---- spec/couchrest/persistence_spec.rb | 28 +++++++++++++++++++--------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/history.md b/history.md index a211ca7..695379a 100644 --- a/history.md +++ b/history.md @@ -11,6 +11,8 @@ * DesignDoc cache refreshed if a database is deleted. * Fixing dirty tracking on collection_of association. * Uniqueness Validation views created on initialization, not on demand! + * #destroy freezes object instead of removing _id and _rev, better for callbacks (pointer by karmi) + * #destroyed? method now available ## 1.1.0.beta5 - 2011-04-30 diff --git a/lib/couchrest/model/persistence.rb b/lib/couchrest/model/persistence.rb index e77b663..977bbe2 100644 --- a/lib/couchrest/model/persistence.rb +++ b/lib/couchrest/model/persistence.rb @@ -54,19 +54,21 @@ module CouchRest end # Deletes the document from the database. Runs the :destroy callbacks. - # Removes the _id and _rev fields, preparing the - # document to be saved to a new _id if required. def destroy _run_destroy_callbacks do result = database.delete_doc(self) if result['ok'] - self.delete('_rev') - self.delete('_id') + @_destroyed = true + self.freeze end result['ok'] end end + def destroyed? + !!@_destroyed + end + # Update the document's attributes and save. For example: # # doc.update_attributes :name => "Fred" diff --git a/spec/couchrest/persistence_spec.rb b/spec/couchrest/persistence_spec.rb index 86ded62..4c0014c 100644 --- a/spec/couchrest/persistence_spec.rb +++ b/spec/couchrest/persistence_spec.rb @@ -5,6 +5,7 @@ require File.join(FIXTURE_PATH, 'more', 'cat') require File.join(FIXTURE_PATH, 'more', 'article') require File.join(FIXTURE_PATH, 'more', 'course') require File.join(FIXTURE_PATH, 'more', 'card') +require File.join(FIXTURE_PATH, 'more', 'event') describe "Model Persistence" do @@ -242,25 +243,34 @@ describe "Model Persistence" do @templated.id.should == 'very-important' end end - + describe "destroying an instance" do before(:each) do - @dobj = Basic.new + @dobj = Event.new @dobj.save.should be_true end it "should return true" do result = @dobj.destroy result.should be_true end - it "should be resavable" do - @dobj.destroy - @dobj.rev.should be_nil - @dobj.id.should be_nil - @dobj.save.should be_true - end it "should make it go away" do @dobj.destroy - lambda{Basic.get!(@dobj.id)}.should raise_error + lambda{Basic.get!(@dobj.id)}.should raise_error(RestClient::ResourceNotFound) + end + it "should freeze the object" do + @dobj.destroy + # In Ruby 1.9.2 this raises RuntimeError, in 1.8.7 TypeError, D'OH! + lambda { @dobj.subject = "Test" }.should raise_error(StandardError) + end + it "trying to save after should fail" do + @dobj.destroy + lambda { @dobj.save }.should raise_error(StandardError) + lambda{Basic.get!(@dobj.id)}.should raise_error(RestClient::ResourceNotFound) + end + it "should make destroyed? true" do + @dobj.destroyed?.should be_false + @dobj.destroy + @dobj.destroyed?.should be_true end end From de1cab12711e5d1bfd00365a1903bc3a277f8f39 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sun, 5 Jun 2011 11:26:50 +0200 Subject: [PATCH 07/34] Updating destroy specs for DocumentNotFound message --- spec/couchrest/persistence_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/couchrest/persistence_spec.rb b/spec/couchrest/persistence_spec.rb index cd2ebed..8def959 100644 --- a/spec/couchrest/persistence_spec.rb +++ b/spec/couchrest/persistence_spec.rb @@ -267,7 +267,7 @@ describe "Model Persistence" do end it "should make it go away" do @dobj.destroy - lambda{Basic.get!(@dobj.id)}.should raise_error(RestClient::ResourceNotFound) + lambda{Basic.get!(@dobj.id)}.should raise_error(CouchRest::Model::DocumentNotFound) end it "should freeze the object" do @dobj.destroy @@ -277,7 +277,7 @@ describe "Model Persistence" do it "trying to save after should fail" do @dobj.destroy lambda { @dobj.save }.should raise_error(StandardError) - lambda{Basic.get!(@dobj.id)}.should raise_error(RestClient::ResourceNotFound) + lambda{Basic.get!(@dobj.id)}.should raise_error(CouchRest::Model::DocumentNotFound) end it "should make destroyed? true" do @dobj.destroyed?.should be_false From f5be6c7679207ee2669705455bb6d5cd7c512802 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Mon, 6 Jun 2011 01:53:36 +0200 Subject: [PATCH 08/34] Fixing problem with reloading models and dirty tracking --- lib/couchrest/model/persistence.rb | 2 +- spec/couchrest/dirty_spec.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/couchrest/model/persistence.rb b/lib/couchrest/model/persistence.rb index 885f726..c63b8f4 100644 --- a/lib/couchrest/model/persistence.rb +++ b/lib/couchrest/model/persistence.rb @@ -87,7 +87,7 @@ module CouchRest # # Returns self. def reload - merge!(self.class.get(id)) + prepare_all_attributes(database.get(id), :directly_set_attributes => true) self end diff --git a/spec/couchrest/dirty_spec.rb b/spec/couchrest/dirty_spec.rb index 12c4f18..8cbd7be 100644 --- a/spec/couchrest/dirty_spec.rb +++ b/spec/couchrest/dirty_spec.rb @@ -241,6 +241,14 @@ describe "Dirty" do end end + it "should report changes if an array is popped after reload" do + should_change_array do |array, obj| + obj.reload + obj.keywords.pop + end + end + + it "should report no changes if an empty array is popped" do should_not_change_array do |array, obj| array.clear From 6e025bb256cf0d0ea5b1f6a8c24633d6bff5cc7f Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Mon, 6 Jun 2011 01:55:08 +0200 Subject: [PATCH 09/34] Updating history --- history.md | 1 + 1 file changed, 1 insertion(+) diff --git a/history.md b/history.md index 0841c44..b2df9f5 100644 --- a/history.md +++ b/history.md @@ -17,6 +17,7 @@ * Uniqueness Validation views created on initialization, not on demand! * #destroy freezes object instead of removing _id and _rev, better for callbacks (pointer by karmi) * #destroyed? method now available + * #reload no longer uses Hash#merge! which was causing issues with dirty tracking on casted models. (pointer by kostia) ## 1.1.0.beta5 - 2011-04-30 From 5cee1734da7725a088a7428347e2439c05ee8253 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kahanskyi Date: Mon, 6 Jun 2011 12:51:02 +0200 Subject: [PATCH 10/34] Should always include docs when paginating by Model##paginate Fixes https://github.com/couchrest/couchrest_model/issues/81 --- lib/couchrest/model/collection.rb | 1 + spec/couchrest/collection_spec.rb | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/couchrest/model/collection.rb b/lib/couchrest/model/collection.rb index 540b299..5d6dd76 100644 --- a/lib/couchrest/model/collection.rb +++ b/lib/couchrest/model/collection.rb @@ -244,6 +244,7 @@ module CouchRest else options = { :limit => per_page, :skip => per_page * (page - 1) } end + options[:include_docs] = true view_options.merge(options) end diff --git a/spec/couchrest/collection_spec.rb b/spec/couchrest/collection_spec.rb index 528a350..63a1626 100644 --- a/spec/couchrest/collection_spec.rb +++ b/spec/couchrest/collection_spec.rb @@ -27,21 +27,20 @@ describe "Collections" do end it "should provide a class method for paginate" do articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', - :per_page => 3, :descending => true, :key => Date.today, :include_docs => true) + :per_page => 3, :descending => true, :key => Date.today) articles.size.should == 3 - + articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', - :per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true) + :per_page => 3, :page => 2, :descending => true, :key => Date.today) articles.size.should == 3 - + articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', - :per_page => 3, :page => 3, :descending => true, :key => Date.today, :include_docs => true) + :per_page => 3, :page => 3, :descending => true, :key => Date.today) articles.size.should == 1 end it "should provide a class method for paginated_each" do options = { :design_doc => 'Article', :view_name => 'by_date', - :per_page => 3, :page => 1, :descending => true, :key => Date.today, - :include_docs => true } + :per_page => 3, :page => 1, :descending => true, :key => Date.today } Article.paginated_each(options) do |a| a.should_not be_nil end From d50d47c32a5bd8a11777c37f606f7b3fe1779aed Mon Sep 17 00:00:00 2001 From: Kostiantyn Kahanskyi Date: Mon, 6 Jun 2011 18:07:33 +0200 Subject: [PATCH 11/34] Should be able to assign a casted hash to a hash property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/couchrest/couchrest_model/issues/68 Signed-off-by: Marcos Tapajós --- lib/couchrest/model/property.rb | 2 +- spec/couchrest/property_spec.rb | 11 +++++++++++ spec/fixtures/more/key_chain.rb | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/more/key_chain.rb diff --git a/lib/couchrest/model/property.rb b/lib/couchrest/model/property.rb index 7661858..0ea9962 100644 --- a/lib/couchrest/model/property.rb +++ b/lib/couchrest/model/property.rb @@ -39,7 +39,7 @@ module CouchRest::Model arr = value.collect { |data| cast_value(parent, data) } # allow casted_by calls to be passed up chain by wrapping in CastedArray CastedArray.new(arr, self, parent) - elsif (type == Object || type == Hash) && (value.class == Hash) + elsif (type == Object || type == Hash) && (value.kind_of?(Hash)) # allow casted_by calls to be passed up chain by wrapping in CastedHash CastedHash[value, self, parent] elsif !value.nil? diff --git a/spec/couchrest/property_spec.rb b/spec/couchrest/property_spec.rb index bb55bfc..13ada9f 100644 --- a/spec/couchrest/property_spec.rb +++ b/spec/couchrest/property_spec.rb @@ -9,6 +9,7 @@ require File.join(FIXTURE_PATH, 'more', 'service') require File.join(FIXTURE_PATH, 'more', 'event') require File.join(FIXTURE_PATH, 'more', 'user') require File.join(FIXTURE_PATH, 'more', 'course') +require File.join(FIXTURE_PATH, "more", "key_chain") describe "Model properties" do @@ -239,6 +240,16 @@ describe "Model properties" do end +describe "properties of hash of casted models" do + it "should be able to assign a casted hash to a hash property" do + chain = KeyChain.new + keys = {"House" => "8==$", "Office" => "<>==U"} + chain.keys = keys + chain.keys = chain.keys + chain.keys.should == keys + end +end + describe "properties of array of casted models" do before(:each) do diff --git a/spec/fixtures/more/key_chain.rb b/spec/fixtures/more/key_chain.rb new file mode 100644 index 0000000..0837e39 --- /dev/null +++ b/spec/fixtures/more/key_chain.rb @@ -0,0 +1,5 @@ +class KeyChain < CouchRest::Model::Base + use_database(DB) + + property(:keys, Hash) +end From f244b51fbff2e6e2b50ec0491bea9ba730f78de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Tue, 7 Jun 2011 20:51:37 -0300 Subject: [PATCH 12/34] Update History --- history.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/history.md b/history.md index b2df9f5..bec70d7 100644 --- a/history.md +++ b/history.md @@ -18,8 +18,8 @@ * #destroy freezes object instead of removing _id and _rev, better for callbacks (pointer by karmi) * #destroyed? method now available * #reload no longer uses Hash#merge! which was causing issues with dirty tracking on casted models. (pointer by kostia) - - + * Fixing problem assigning a CastedHash to a property declared as a Hash (Kostiantyn Kahanskyi, gfmtim) + ## 1.1.0.beta5 - 2011-04-30 * Major changes: From 0bb00860d1344280fc729ec6374047b07c178bfa Mon Sep 17 00:00:00 2001 From: Kostiantyn Kahanskyi Date: Mon, 6 Jun 2011 11:55:15 +0200 Subject: [PATCH 13/34] Casted array should notify changes on deletion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise there is no direct way to delete particular elements. Workaround with assigning to another array without those elements is pretty ugly. * Should notify on deletion at a particular index (Array#delete_at) * Should notify on deletion of a particular element (Array#delete) Signed-off-by: Marcos Tapajós --- lib/couchrest/model/casted_array.rb | 10 +++++++ spec/couchrest/dirty_spec.rb | 44 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/lib/couchrest/model/casted_array.rb b/lib/couchrest/model/casted_array.rb index 8e4ff66..65b7f46 100644 --- a/lib/couchrest/model/casted_array.rb +++ b/lib/couchrest/model/casted_array.rb @@ -50,6 +50,16 @@ module CouchRest::Model super end + def delete(obj) + couchrest_parent_will_change! if use_dirty? && self.length > 0 + super(obj) + end + + def delete_at(index) + couchrest_parent_will_change! if use_dirty? && self.length > 0 + super(index) + end + def build(*args) obj = casted_by_property.build(*args) self.push(obj) diff --git a/spec/couchrest/dirty_spec.rb b/spec/couchrest/dirty_spec.rb index 8cbd7be..9129a23 100644 --- a/spec/couchrest/dirty_spec.rb +++ b/spec/couchrest/dirty_spec.rb @@ -257,6 +257,50 @@ describe "Dirty" do end end + it "should report changes on deletion from an array" do + should_change_array do |array, obj| + array << "keyword" + obj.save! + array.delete_at(0) + end + + should_change_array do |array, obj| + array << "keyword" + obj.save! + array.delete("keyword") + end + end + + it "should report changes on deletion from an array after reload" do + should_change_array do |array, obj| + array << "keyword" + obj.save! + obj.reload + array.delete_at(0) + end + + should_change_array do |array, obj| + array << "keyword" + obj.save! + obj.reload + array.delete("keyword") + end + end + + it "should report no changes on deletion from an empty array" do + should_not_change_array do |array, obj| + array.clear + obj.save! + array.delete_at(0) + end + + should_not_change_array do |array, obj| + array.clear + obj.save! + array.delete("keyword") + end + end + it "should report changes if an array is pushed" do should_change_array do |array, obj| array.push("keyword") From d99150547c879da89254a583c6637c634f2492a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Tue, 7 Jun 2011 21:00:41 -0300 Subject: [PATCH 14/34] History update --- history.md | 1 + 1 file changed, 1 insertion(+) diff --git a/history.md b/history.md index bec70d7..40e594f 100644 --- a/history.md +++ b/history.md @@ -6,6 +6,7 @@ * Properties with a nil value are now no longer sent to the database. * Now possible to build new objects via CastedArray#build * Implement #get! and #find! class methods + * Now is possible delete particular elements in casted array(Kostiantyn Kahanskyi) * Minor fixes * #as_json now correctly uses ActiveSupports methods. From 7c7ee2c2b1a4c9a778f44f6fb1fe688dc173e081 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Wed, 8 Jun 2011 18:22:35 +0200 Subject: [PATCH 15/34] Upgrading to use CouchRest 1.1.0.pre3 and new Hash-less design --- VERSION | 2 +- couchrest_model.gemspec | 5 +- history.md | 6 +- lib/couchrest/model/base.rb | 21 ++--- lib/couchrest/model/document_queries.rb | 11 +-- lib/couchrest/model/persistence.rb | 11 ++- lib/couchrest/model/properties.rb | 28 +++--- lib/couchrest/model/property.rb | 4 +- lib/couchrest/model/proxyable.rb | 15 +++- .../model/support/couchrest_design.rb | 2 +- spec/couchrest/base_spec.rb | 85 +++++++++++++------ spec/couchrest/design_doc_spec.rb | 2 +- spec/couchrest/inherited_spec.rb | 67 +++++++-------- spec/couchrest/persistence_spec.rb | 20 ++--- spec/couchrest/property_spec.rb | 22 +++++ spec/couchrest/proxyable_spec.rb | 21 +++-- spec/fixtures/base.rb | 7 +- spec/fixtures/more/article.rb | 1 + spec/fixtures/more/cat.rb | 4 + 19 files changed, 197 insertions(+), 137 deletions(-) diff --git a/VERSION b/VERSION index 2304d76..0859045 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0.beta5 +1.1.0.rc diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index ce7f2ab..7a500b7 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -23,11 +23,12 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_dependency(%q, "1.1.0.pre2") + s.add_dependency(%q, "1.1.0.pre3") s.add_dependency(%q, "~> 1.15") s.add_dependency(%q, "~> 3.0") s.add_dependency(%q, "~> 0.3.22") - s.add_development_dependency(%q, ">= 2.0.0") + s.add_development_dependency(%q, "~> 2.6.0") + s.add_development_dependency(%q, ["~> 1.5.1"]) s.add_development_dependency(%q, ">= 0.5.7") # s.add_development_dependency("jruby-openssl", ">= 0.7.3") end diff --git a/history.md b/history.md index b2df9f5..d8737a0 100644 --- a/history.md +++ b/history.md @@ -1,11 +1,11 @@ # CouchRest Model Change History -## 1.1.0 - 2011-05-XX +## 1.1.0.rc - 2011-06-08 * New Features * Properties with a nil value are now no longer sent to the database. * Now possible to build new objects via CastedArray#build - * Implement #get! and #find! class methods + * Implement #get! and #find! class methods * Minor fixes * #as_json now correctly uses ActiveSupports methods. @@ -18,6 +18,8 @@ * #destroy freezes object instead of removing _id and _rev, better for callbacks (pointer by karmi) * #destroyed? method now available * #reload no longer uses Hash#merge! which was causing issues with dirty tracking on casted models. (pointer by kostia) + * Non-property mass assignment on #new no longer possible without :directly_set_attributes option. + * Using CouchRest 1.1.0.pre3. (No more Hashes!) ## 1.1.0.beta5 - 2011-04-30 diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index 5f3a6cd..bf64e4b 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -1,6 +1,6 @@ module CouchRest module Model - class Base < Document + class Base < CouchRest::Document extend ActiveModel::Naming @@ -51,14 +51,15 @@ module CouchRest # # If a block is provided the new model will be passed into the # block so that it can be populated. - def initialize(doc = {}, options = {}) - doc = prepare_all_attributes(doc, options) - # set the instances database, if provided + def initialize(attributes = {}, options = {}) + super() + prepare_all_attributes(attributes, options) + # set the instance's database, if provided self.database = options[:database] unless options[:database].nil? - super(doc) unless self['_id'] && self['_rev'] self[self.model_type_key] = self.class.to_s end + yield self if block_given? after_initialize if respond_to?(:after_initialize) @@ -79,16 +80,6 @@ module CouchRest super end - ## Compatibility with ActiveSupport and older frameworks - - # Hack so that CouchRest::Document, which descends from Hash, - # doesn't appear to Rails routing as a Hash of options - def is_a?(klass) - return false if klass == Hash - super - end - alias :kind_of? :is_a? - def persisted? !new? end diff --git a/lib/couchrest/model/document_queries.rb b/lib/couchrest/model/document_queries.rb index 839674b..22aec40 100644 --- a/lib/couchrest/model/document_queries.rb +++ b/lib/couchrest/model/document_queries.rb @@ -1,13 +1,10 @@ module CouchRest module Model module DocumentQueries - - def self.included(base) - base.extend(ClassMethods) - end - + extend ActiveSupport::Concern + module ClassMethods - + # Load all documents that have the model_type_key's field equal to the # name of the current class. Take the standard set of # CouchRest::Database#view options. @@ -73,7 +70,7 @@ module CouchRest end end alias :find :get - + # Load a document from the database by id # An exception will be raised if the document isn't found # diff --git a/lib/couchrest/model/persistence.rb b/lib/couchrest/model/persistence.rb index c63b8f4..03a9ee8 100644 --- a/lib/couchrest/model/persistence.rb +++ b/lib/couchrest/model/persistence.rb @@ -106,14 +106,17 @@ module CouchRest module ClassMethods - # Creates a new instance, bypassing attribute protection + # Creates a new instance, bypassing attribute protection and + # uses the type field to determine which model to use to instanatiate + # the new object. # # ==== Returns # a document instance # - def build_from_database(doc = {}) - base = (doc[model_type_key].blank? || doc[model_type_key] == self.to_s) ? self : doc[model_type_key].constantize - base.new(doc, :directly_set_attributes => true) + def build_from_database(doc = {}, options = {}, &block) + src = doc[model_type_key] + base = (src.blank? || src == self.to_s) ? self : src.constantize + base.new(doc, options.merge(:directly_set_attributes => true), &block) end # Defines an instance and save it directly to the database diff --git a/lib/couchrest/model/properties.rb b/lib/couchrest/model/properties.rb index 5d815ab..133e9b9 100644 --- a/lib/couchrest/model/properties.rb +++ b/lib/couchrest/model/properties.rb @@ -80,17 +80,18 @@ module CouchRest self.disable_dirty = dirty end - def prepare_all_attributes(doc = {}, options = {}) + def prepare_all_attributes(attrs = {}, options = {}) self.disable_dirty = !!options[:directly_set_attributes] apply_all_property_defaults if options[:directly_set_attributes] - directly_set_read_only_attributes(doc) + directly_set_read_only_attributes(attrs) + directly_set_attributes(attrs, true) else - doc = remove_protected_attributes(doc) + attrs = remove_protected_attributes(attrs) + directly_set_attributes(attrs) end - res = doc.nil? ? doc : directly_set_attributes(doc) self.disable_dirty = false - res + self end def find_property!(property) @@ -101,16 +102,13 @@ module CouchRest # Set all the attributes and return a hash with the attributes # that have not been accepted. - def directly_set_attributes(hash) - hash.reject do |attribute_name, attribute_value| - if self.respond_to?("#{attribute_name}=") - self.send("#{attribute_name}=", attribute_value) - true - elsif mass_assign_any_attribute # config option - self[attribute_name] = attribute_value - true - else - false + def directly_set_attributes(hash, mass_assign = false) + return if hash.nil? + hash.reject do |key, value| + if self.respond_to?("#{key}=") + self.send("#{key}=", value) + elsif mass_assign || mass_assign_any_attribute + self[key] = value end end end diff --git a/lib/couchrest/model/property.rb b/lib/couchrest/model/property.rb index 7661858..07fdfe8 100644 --- a/lib/couchrest/model/property.rb +++ b/lib/couchrest/model/property.rb @@ -26,7 +26,7 @@ module CouchRest::Model if type.is_a?(Array) if value.nil? value = [] - elsif [Hash, HashWithIndifferentAccess].include?(value.class) + elsif value.is_a?(Hash) # Assume provided as a Hash where key is index! data = value value = [ ] @@ -39,7 +39,7 @@ module CouchRest::Model arr = value.collect { |data| cast_value(parent, data) } # allow casted_by calls to be passed up chain by wrapping in CastedArray CastedArray.new(arr, self, parent) - elsif (type == Object || type == Hash) && (value.class == Hash) + elsif (type == Object || type == Hash) && (value.is_a?(Hash)) # allow casted_by calls to be passed up chain by wrapping in CastedHash CastedHash[value, self, parent] elsif !value.nil? diff --git a/lib/couchrest/model/proxyable.rb b/lib/couchrest/model/proxyable.rb index 53a88da..9fb812c 100644 --- a/lib/couchrest/model/proxyable.rb +++ b/lib/couchrest/model/proxyable.rb @@ -73,12 +73,12 @@ module CouchRest end # Base - def new(*args) - proxy_update(model.new(*args)) + def new(attrs = {}, options = {}, &block) + proxy_block_update(:new, attrs, options, &block) end - def build_from_database(doc = {}) - proxy_update(model.build_from_database(doc)) + def build_from_database(attrs = {}, options = {}, &block) + proxy_block_update(:build_from_database, attrs, options, &block) end def method_missing(m, *args, &block) @@ -170,6 +170,13 @@ module CouchRest end end + def proxy_block_update(method, *args, &block) + model.send(method, *args) do |doc| + proxy_update(doc) + yield doc if block_given? + end + end + end end end diff --git a/lib/couchrest/model/support/couchrest_design.rb b/lib/couchrest/model/support/couchrest_design.rb index a5caf07..73e9eaa 100644 --- a/lib/couchrest/model/support/couchrest_design.rb +++ b/lib/couchrest/model/support/couchrest_design.rb @@ -18,7 +18,7 @@ CouchRest::Design.class_eval do flatten = lambda {|r| (recurse = lambda {|v| - if v.is_a?(Hash) + if v.is_a?(Hash) || v.is_a?(CouchRest::Document) v.to_a.map{|v| recurse.call(v)}.flatten elsif v.is_a?(Array) v.flatten.map{|v| recurse.call(v)} diff --git a/spec/couchrest/base_spec.rb b/spec/couchrest/base_spec.rb index 208d6e7..e3c665f 100644 --- a/spec/couchrest/base_spec.rb +++ b/spec/couchrest/base_spec.rb @@ -49,8 +49,34 @@ describe "Model Base" do @obj.database.should eql('database') end + it "should only set defined properties" do + @doc = WithDefaultValues.new(:name => 'test', :foo => 'bar') + @doc['name'].should eql('test') + @doc['foo'].should be_nil + end + + it "should set all properties with :directly_set_attributes option" do + @doc = WithDefaultValues.new({:name => 'test', :foo => 'bar'}, :directly_set_attributes => true) + @doc['name'].should eql('test') + @doc['foo'].should eql('bar') + end + + it "should set the model type" do + @doc = WithDefaultValues.new() + @doc[WithDefaultValues.model_type_key].should eql('WithDefaultValues') + end + + it "should call after_initialize method if available" do + @doc = WithAfterInitializeMethod.new + @doc['some_value'].should eql('value') + end + + it "should call after_initialize after block" do + @doc = WithAfterInitializeMethod.new {|d| d.some_value = "foo"} + @doc['some_value'].should eql('foo') + end end - + describe "ActiveModel compatability Basic" do before(:each) do @@ -109,9 +135,23 @@ describe "Model Base" do end end + describe "#destroyed?" do + it "should be present" do + @obj.should respond_to(:destroyed?) + end + it "should return false with new object" do + @obj.destroyed?.should be_false + end + it "should return true after destroy" do + @obj.save + @obj.destroy + @obj.destroyed?.should be_true + end + end + end - + describe "update attributes without saving" do before(:each) do a = Article.get "big-bad-danger" rescue nil @@ -152,7 +192,7 @@ describe "Model Base" do }.should_not raise_error @art.slug.should == "big-bad-danger" end - + #it "should not change other attributes if there is an error" do # lambda { # @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger") @@ -160,7 +200,7 @@ describe "Model Base" do # @art['title'].should == "big bad danger" #end end - + describe "update attributes" do before(:each) do a = Article.get "big-bad-danger" rescue nil @@ -175,7 +215,7 @@ describe "Model Base" do loaded['title'].should == "super danger" end end - + describe "with default" do it "should have the default value set at initalization" do @obj.preset.should == {:right => 10, :top_align => false} @@ -232,7 +272,7 @@ describe "Model Base" do WithTemplateAndUniqueID.all.map{|o| o.destroy} WithTemplateAndUniqueID.database.bulk_delete @tmpl = WithTemplateAndUniqueID.new - @tmpl2 = WithTemplateAndUniqueID.new(:preset => 'not_value', 'important-field' => '1') + @tmpl2 = WithTemplateAndUniqueID.new(:preset => 'not_value', 'slug' => '1') end it "should have fields set when new" do @tmpl.preset.should == 'value' @@ -253,10 +293,10 @@ describe "Model Base" do before(:all) do WithTemplateAndUniqueID.all.map{|o| o.destroy} WithTemplateAndUniqueID.database.bulk_delete - WithTemplateAndUniqueID.new('important-field' => '1').save - WithTemplateAndUniqueID.new('important-field' => '2').save - WithTemplateAndUniqueID.new('important-field' => '3').save - WithTemplateAndUniqueID.new('important-field' => '4').save + WithTemplateAndUniqueID.new('slug' => '1').save + WithTemplateAndUniqueID.new('slug' => '2').save + WithTemplateAndUniqueID.new('slug' => '3').save + WithTemplateAndUniqueID.new('slug' => '4').save end it "should find all" do rs = WithTemplateAndUniqueID.all @@ -274,9 +314,9 @@ describe "Model Base" do end it ".count should return the number of documents" do - WithTemplateAndUniqueID.new('important-field' => '1').save - WithTemplateAndUniqueID.new('important-field' => '2').save - WithTemplateAndUniqueID.new('important-field' => '3').save + WithTemplateAndUniqueID.new('slug' => '1').save + WithTemplateAndUniqueID.new('slug' => '2').save + WithTemplateAndUniqueID.new('slug' => '3').save WithTemplateAndUniqueID.count.should == 3 end @@ -285,14 +325,14 @@ describe "Model Base" do describe "finding the first instance of a model" do before(:each) do @db = reset_test_db! - WithTemplateAndUniqueID.new('important-field' => '1').save - WithTemplateAndUniqueID.new('important-field' => '2').save - WithTemplateAndUniqueID.new('important-field' => '3').save - WithTemplateAndUniqueID.new('important-field' => '4').save + WithTemplateAndUniqueID.new('slug' => '1').save + WithTemplateAndUniqueID.new('slug' => '2').save + WithTemplateAndUniqueID.new('slug' => '3').save + WithTemplateAndUniqueID.new('slug' => '4').save end it "should find first" do rs = WithTemplateAndUniqueID.first - rs['important-field'].should == "1" + rs['slug'].should == "1" end it "should return nil if no instances are found" do WithTemplateAndUniqueID.all.each {|obj| obj.destroy } @@ -370,14 +410,7 @@ describe "Model Base" do end end - describe "initialization" do - it "should call after_initialize method if available" do - @doc = WithAfterInitializeMethod.new - @doc['some_value'].should eql('value') - end - end - - describe "recursive validation on a model" do + describe "recursive validation on a model" do before :each do reset_test_db! @cat = Cat.new(:name => 'Sockington') diff --git a/spec/couchrest/design_doc_spec.rb b/spec/couchrest/design_doc_spec.rb index efda852..fcd40c1 100644 --- a/spec/couchrest/design_doc_spec.rb +++ b/spec/couchrest/design_doc_spec.rb @@ -202,7 +202,7 @@ describe "Design Documents" do describe "lazily refreshing the design document" do before(:all) do @db = reset_test_db! - WithTemplateAndUniqueID.new('important-field' => '1').save + WithTemplateAndUniqueID.new('slug' => '1').save end it "should not save the design doc twice" do WithTemplateAndUniqueID.all diff --git a/spec/couchrest/inherited_spec.rb b/spec/couchrest/inherited_spec.rb index 69eba6a..f19546f 100644 --- a/spec/couchrest/inherited_spec.rb +++ b/spec/couchrest/inherited_spec.rb @@ -1,40 +1,33 @@ require File.expand_path('../../spec_helper', __FILE__) -begin - require 'rubygems' unless ENV['SKIP_RUBYGEMS'] - require 'active_support/json' - ActiveSupport::JSON.backend = :JSONGem - - class PlainParent - class_inheritable_accessor :foo - self.foo = :bar - end - - class PlainChild < PlainParent - end - - class ExtendedParent < CouchRest::Model::Base - class_inheritable_accessor :foo - self.foo = :bar - end - - class ExtendedChild < ExtendedParent - end - - describe "Using chained inheritance without CouchRest::Model::Base" do - it "should preserve inheritable attributes" do - PlainParent.foo.should == :bar - PlainChild.foo.should == :bar - end - end - - describe "Using chained inheritance with CouchRest::Model::Base" do - it "should preserve inheritable attributes" do - ExtendedParent.foo.should == :bar - ExtendedChild.foo.should == :bar - end - end - -rescue LoadError - puts "This spec requires 'active_support/json' to be loaded" +class PlainParent + class_inheritable_accessor :foo + self.foo = :bar end + +class PlainChild < PlainParent +end + +class ExtendedParent < CouchRest::Model::Base + class_inheritable_accessor :foo + self.foo = :bar +end + +class ExtendedChild < ExtendedParent +end + +describe "Using chained inheritance without CouchRest::Model::Base" do + it "should preserve inheritable attributes" do + PlainParent.foo.should == :bar + PlainChild.foo.should == :bar + end +end + +describe "Using chained inheritance with CouchRest::Model::Base" do + it "should preserve inheritable attributes" do + ExtendedParent.foo.should == :bar + ExtendedChild.foo.should == :bar + end +end + + diff --git a/spec/couchrest/persistence_spec.rb b/spec/couchrest/persistence_spec.rb index 8def959..71ced97 100644 --- a/spec/couchrest/persistence_spec.rb +++ b/spec/couchrest/persistence_spec.rb @@ -35,11 +35,11 @@ describe "Model Persistence" do describe "basic saving and retrieving" do it "should work fine" do @obj.name = "should be easily saved and retrieved" - @obj.save - saved_obj = WithDefaultValues.get(@obj.id) + @obj.save! + saved_obj = WithDefaultValues.get!(@obj.id) saved_obj.should_not be_nil end - + it "should parse the Time attributes automatically" do @obj.name = "should parse the Time attributes automatically" @obj.set_by_proc.should be_an_instance_of(Time) @@ -223,34 +223,34 @@ describe "Model Persistence" do it "should require the field" do lambda{@templated.save}.should raise_error - @templated['important-field'] = 'very-important' + @templated['slug'] = 'very-important' @templated.save.should be_true end it "should save with the id" do - @templated['important-field'] = 'very-important' + @templated['slug'] = 'very-important' @templated.save.should be_true t = WithTemplateAndUniqueID.get('very-important') t.should == @templated end it "should not change the id on update" do - @templated['important-field'] = 'very-important' + @templated['slug'] = 'very-important' @templated.save.should be_true - @templated['important-field'] = 'not-important' + @templated['slug'] = 'not-important' @templated.save.should be_true t = WithTemplateAndUniqueID.get('very-important') t.id.should == @templated.id end it "should raise an error when the id is taken" do - @templated['important-field'] = 'very-important' + @templated['slug'] = 'very-important' @templated.save.should be_true - lambda{WithTemplateAndUniqueID.new('important-field' => 'very-important').save}.should raise_error + lambda{WithTemplateAndUniqueID.new('slug' => 'very-important').save}.should raise_error end it "should set the id" do - @templated['important-field'] = 'very-important' + @templated['slug'] = 'very-important' @templated.save.should be_true @templated.id.should == 'very-important' end diff --git a/spec/couchrest/property_spec.rb b/spec/couchrest/property_spec.rb index bb55bfc..fd82a14 100644 --- a/spec/couchrest/property_spec.rb +++ b/spec/couchrest/property_spec.rb @@ -315,6 +315,28 @@ describe "a casted model retrieved from the database" do end end +describe "nested models (not casted)" do + before(:each) do + reset_test_db! + @cat = ChildCat.new(:name => 'Stimpy') + @cat.mother = {:name => 'Stinky'} + @cat.siblings = [{:name => 'Feather'}, {:name => 'Felix'}] + @cat.save + @cat = ChildCat.get(@cat.id) + end + + it "should correctly save single relation" do + @cat.mother.name.should eql('Stinky') + @cat.mother.casted_by.should eql(@cat) + end + + it "should correctly save collection" do + @cat.siblings.first.name.should eql("Feather") + @cat.siblings.last.casted_by.should eql(@cat) + end + +end + describe "Property Class" do it "should provide name as string" do diff --git a/spec/couchrest/proxyable_spec.rb b/spec/couchrest/proxyable_spec.rb index 7e0c508..3dc2064 100644 --- a/spec/couchrest/proxyable_spec.rb +++ b/spec/couchrest/proxyable_spec.rb @@ -87,7 +87,7 @@ describe "Proxyable" do DummyProxyable.proxy_for(:cats) @obj = DummyProxyable.new CouchRest::Model::Proxyable::ModelProxy.should_receive(:new).with(Cat, @obj, 'dummy_proxyable', 'db').and_return(true) - @obj.should_receive('proxy_database').and_return('db') + @obj.should_receive(:proxy_database).and_return('db') @obj.cats end @@ -165,15 +165,13 @@ describe "Proxyable" do end it "should proxy new call" do - Cat.should_receive(:new).and_return({}) - @obj.should_receive(:proxy_update).and_return(true) - @obj.new + @obj.should_receive(:proxy_block_update).with(:new, 'attrs', 'opts') + @obj.new('attrs', 'opts') end it "should proxy build_from_database" do - Cat.should_receive(:build_from_database).and_return({}) - @obj.should_receive(:proxy_update).with({}).and_return(true) - @obj.build_from_database + @obj.should_receive(:proxy_block_update).with(:build_from_database, 'attrs', 'opts') + @obj.build_from_database('attrs', 'opts') end describe "#method_missing" do @@ -313,6 +311,15 @@ describe "Proxyable" do @obj.send(:proxy_update_all, docs) end + describe "#proxy_block_update" do + it "should proxy block updates" do + doc = { } + @obj.model.should_receive(:new).and_yield(doc) + @obj.should_receive(:proxy_update).with(doc) + @obj.send(:proxy_block_update, :new) + end + end + end end diff --git a/spec/fixtures/base.rb b/spec/fixtures/base.rb index d395b4a..65fabfa 100644 --- a/spec/fixtures/base.rb +++ b/spec/fixtures/base.rb @@ -86,15 +86,16 @@ end class WithTemplateAndUniqueID < CouchRest::Model::Base use_database TEST_SERVER.default_database unique_id do |model| - model['important-field'] + model.slug end + property :slug property :preset, :default => 'value' property :has_no_default end class WithGetterAndSetterMethods < CouchRest::Model::Base use_database TEST_SERVER.default_database - + property :other_arg def arg other_arg @@ -107,7 +108,7 @@ end class WithAfterInitializeMethod < CouchRest::Model::Base use_database TEST_SERVER.default_database - + property :some_value def after_initialize diff --git a/spec/fixtures/more/article.rb b/spec/fixtures/more/article.rb index 9e370e3..6e2af99 100644 --- a/spec/fixtures/more/article.rb +++ b/spec/fixtures/more/article.rb @@ -22,6 +22,7 @@ class Article < CouchRest::Model::Base property :date, Date property :slug, :read_only => true + property :user_id property :title property :tags, [String] diff --git a/spec/fixtures/more/cat.rb b/spec/fixtures/more/cat.rb index 903eafd..481bc37 100644 --- a/spec/fixtures/more/cat.rb +++ b/spec/fixtures/more/cat.rb @@ -17,3 +17,7 @@ class Cat < CouchRest::Model::Base property :number end +class ChildCat < Cat + property :mother, Cat + property :siblings, [Cat] +end From ea4325f5bf8da0d0113b7cdd6874478480baf24b Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Wed, 8 Jun 2011 19:14:01 +0200 Subject: [PATCH 16/34] Fixing assiging hashes to casted arrays properties --- VERSION | 2 +- lib/couchrest/model/property.rb | 18 +++++++++++------- spec/couchrest/property_spec.rb | 8 ++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/VERSION b/VERSION index 0859045..de1d3a8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0.rc +1.1.0.rc1 diff --git a/lib/couchrest/model/property.rb b/lib/couchrest/model/property.rb index 07fdfe8..9beb5c7 100644 --- a/lib/couchrest/model/property.rb +++ b/lib/couchrest/model/property.rb @@ -26,13 +26,9 @@ module CouchRest::Model if type.is_a?(Array) if value.nil? value = [] - elsif value.is_a?(Hash) - # Assume provided as a Hash where key is index! - data = value - value = [ ] - data.keys.sort.each do |k| - value << data[k] - end + elsif [Hash, HashWithIndifferentAccess].include?(value.class) + # Assume provided as a params hash where key is index + value = parameter_hash_to_array(value) elsif !value.is_a?(Array) raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}" end @@ -78,6 +74,14 @@ module CouchRest::Model private + def parameter_hash_to_array(source) + value = [ ] + source.keys.each do |k| + value[k.to_i] = source[k] + end + value.compact + end + def associate_casted_value_to_parent(parent, value) value.casted_by = parent if value.respond_to?(:casted_by) value.casted_by_property = self if value.respond_to?(:casted_by_property) diff --git a/spec/couchrest/property_spec.rb b/spec/couchrest/property_spec.rb index 5379269..6da6b3b 100644 --- a/spec/couchrest/property_spec.rb +++ b/spec/couchrest/property_spec.rb @@ -276,9 +276,9 @@ describe "properties of array of casted models" do end it "should allow attribute to be set from hash with ordered keys and sub-hashes" do - @course.questions = { '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} } - @course.questions.length.should eql(2) - @course.questions.last.q.should eql('Test2') + @course.questions = { '10' => {:q => 'Test10'}, '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} } + @course.questions.length.should eql(3) + @course.questions.last.q.should eql('Test10') @course.questions.last.class.should eql(Question) end @@ -295,7 +295,7 @@ describe "properties of array of casted models" do it "should raise an error if attempting to set single value for array type" do lambda { @course.questions = Question.new(:q => 'test1') - }.should raise_error + }.should raise_error(/Expecting an array/) end From 1b5c431053ce9f58851ffa81163b9b97025d0e65 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kahanskyi Date: Wed, 8 Jun 2011 22:31:57 +0200 Subject: [PATCH 17/34] Should be able to set contextual validations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contextual validations are active __only__ within an appropriate context: either ”create" or ”update”. validates(:name, :presence => true, :on => :create) validates(:name, :presence => {:on => :update}) See http://edgeguides.rubyonrails.org/active_record_validations_callbacks.html#on Should fix https://github.com/couchrest/couchrest_model/pull/90 --- lib/couchrest/model/base.rb | 2 +- lib/couchrest/model/callbacks.rb | 6 ++++-- lib/couchrest/model/casted_model.rb | 3 ++- spec/couchrest/persistence_spec.rb | 21 +++++++++++++++++++++ spec/fixtures/base.rb | 11 +++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index 5f3a6cd..508f568 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -7,7 +7,6 @@ module CouchRest include CouchRest::Model::Configuration include CouchRest::Model::Connection include CouchRest::Model::Persistence - include CouchRest::Model::Callbacks include CouchRest::Model::DocumentQueries include CouchRest::Model::Views include CouchRest::Model::DesignDoc @@ -21,6 +20,7 @@ module CouchRest include CouchRest::Model::Designs include CouchRest::Model::CastedBy include CouchRest::Model::Dirty + include CouchRest::Model::Callbacks def self.subclasses @subclasses ||= [] diff --git a/lib/couchrest/model/callbacks.rb b/lib/couchrest/model/callbacks.rb index 3037810..dd6a67f 100644 --- a/lib/couchrest/model/callbacks.rb +++ b/lib/couchrest/model/callbacks.rb @@ -16,8 +16,10 @@ module CouchRest #:nodoc: end - def valid?(*) #nodoc - _run_validation_callbacks { super } + def valid?(context = nil) + context ||= (new_record? ? :create : :update) + output = super(context) + errors.empty? && output end end diff --git a/lib/couchrest/model/casted_model.rb b/lib/couchrest/model/casted_model.rb index f20de16..e8c6a20 100644 --- a/lib/couchrest/model/casted_model.rb +++ b/lib/couchrest/model/casted_model.rb @@ -5,13 +5,14 @@ module CouchRest::Model included do include CouchRest::Model::Configuration - include CouchRest::Model::Callbacks include CouchRest::Model::Properties include CouchRest::Model::PropertyProtection include CouchRest::Model::Associations include CouchRest::Model::Validations include CouchRest::Model::CastedBy include CouchRest::Model::Dirty + include CouchRest::Model::Callbacks + class_eval do # Override CastedBy's base_doc? def base_doc? diff --git a/spec/couchrest/persistence_spec.rb b/spec/couchrest/persistence_spec.rb index 8def959..b9e1037 100644 --- a/spec/couchrest/persistence_spec.rb +++ b/spec/couchrest/persistence_spec.rb @@ -362,6 +362,27 @@ describe "Model Persistence" do end end + describe "with contextual validation on ”create”" do + it "should validate only within ”create” context" do + doc = WithContextualValidationOnCreate.new + doc.save.should be_false + doc.name = "Alice" + doc.save.should be_true + + doc.update_attributes(:name => nil).should be_true + end + end + + describe "with contextual validation on ”update”" do + it "should validate only within ”update” context" do + doc = WithContextualValidationOnUpdate.new + doc.save.should be_true + + doc.update_attributes(:name => nil).should be_false + doc.update_attributes(:name => "Bob").should be_true + end + end + describe "save" do it "should run the after filter after saving" do @doc.run_after_save.should be_nil diff --git a/spec/fixtures/base.rb b/spec/fixtures/base.rb index d395b4a..aaf6d39 100644 --- a/spec/fixtures/base.rb +++ b/spec/fixtures/base.rb @@ -83,6 +83,17 @@ class WithCallBacks < CouchRest::Model::Base end end +# Following two fixture classes have __intentionally__ diffent syntax for setting the validation context +class WithContextualValidationOnCreate < CouchRest::Model::Base + property(:name, String) + validates(:name, :presence => {:on => :create}) +end + +class WithContextualValidationOnUpdate < CouchRest::Model::Base + property(:name, String) + validates(:name, :presence => true, :on => :update) +end + class WithTemplateAndUniqueID < CouchRest::Model::Base use_database TEST_SERVER.default_database unique_id do |model| From 3579e0e3344d75c2424c4c83fbd623fa5f86b7fb Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Thu, 9 Jun 2011 01:05:22 +0200 Subject: [PATCH 18/34] Refactoring tests and Validation callbacks --- lib/couchrest/model/base.rb | 2 +- lib/couchrest/model/callbacks.rb | 24 ++++++------ lib/couchrest/model/casted_model.rb | 2 +- lib/couchrest/model/validations.rb | 27 ++++++++++---- lib/couchrest_model.rb | 2 +- spec/fixtures/{more => models}/article.rb | 0 spec/fixtures/{ => models}/base.rb | 0 spec/fixtures/{more => models}/card.rb | 2 + spec/fixtures/{more => models}/cat.rb | 0 spec/fixtures/{more => models}/client.rb | 0 spec/fixtures/{more => models}/course.rb | 4 +- spec/fixtures/{more => models}/event.rb | 0 spec/fixtures/{more => models}/invoice.rb | 4 +- spec/fixtures/{more => models}/key_chain.rb | 0 spec/fixtures/models/membership.rb | 4 ++ spec/fixtures/{more => models}/person.rb | 2 + spec/fixtures/{more => models}/question.rb | 0 spec/fixtures/{more => models}/sale_entry.rb | 0 .../fixtures/{more => models}/sale_invoice.rb | 7 ++-- spec/fixtures/{more => models}/service.rb | 0 spec/fixtures/{more => models}/user.rb | 0 spec/functional/validations_spec.rb | 8 ++++ spec/spec_helper.rb | 37 ++++++++++++------- spec/unit/active_model_lint_spec.rb | 30 +++++++++++++++ spec/{couchrest => unit}/assocations_spec.rb | 4 +- spec/{couchrest => unit}/attachment_spec.rb | 2 +- spec/{couchrest => unit}/base_spec.rb | 8 +--- spec/{couchrest => unit}/casted_model_spec.rb | 9 +---- spec/{couchrest => unit}/casted_spec.rb | 5 +-- spec/{couchrest => unit}/class_proxy_spec.rb | 2 +- spec/{couchrest => unit}/collection_spec.rb | 3 +- .../{couchrest => unit}/configuration_spec.rb | 5 +-- spec/{couchrest => unit}/connection_spec.rb | 4 +- .../core_extensions/time_parsing.rb | 0 spec/{couchrest => unit}/design_doc_spec.rb | 7 +--- spec/{couchrest => unit}/designs/view_spec.rb | 0 spec/{couchrest => unit}/designs_spec.rb | 5 +-- spec/{couchrest => unit}/dirty_spec.rb | 8 +--- spec/{couchrest => unit}/inherited_spec.rb | 2 +- spec/{couchrest => unit}/persistence_spec.rb | 10 +---- .../property_protection_spec.rb | 2 +- spec/{couchrest => unit}/property_spec.rb | 15 +------- spec/{couchrest => unit}/proxyable_spec.rb | 6 +-- spec/{couchrest => unit}/subclass_spec.rb | 6 +-- spec/{couchrest => unit}/typecast_spec.rb | 5 +-- spec/{couchrest => unit}/validations_spec.rb | 12 +----- spec/{couchrest => unit}/view_spec.rb | 9 +---- 47 files changed, 142 insertions(+), 142 deletions(-) rename spec/fixtures/{more => models}/article.rb (100%) rename spec/fixtures/{ => models}/base.rb (100%) rename spec/fixtures/{more => models}/card.rb (95%) rename spec/fixtures/{more => models}/cat.rb (100%) rename spec/fixtures/{more => models}/client.rb (100%) rename spec/fixtures/{more => models}/course.rb (88%) rename spec/fixtures/{more => models}/event.rb (100%) rename spec/fixtures/{more => models}/invoice.rb (98%) rename spec/fixtures/{more => models}/key_chain.rb (100%) create mode 100644 spec/fixtures/models/membership.rb rename spec/fixtures/{more => models}/person.rb (91%) rename spec/fixtures/{more => models}/question.rb (100%) rename spec/fixtures/{more => models}/sale_entry.rb (100%) rename spec/fixtures/{more => models}/sale_invoice.rb (72%) rename spec/fixtures/{more => models}/service.rb (100%) rename spec/fixtures/{more => models}/user.rb (100%) create mode 100644 spec/functional/validations_spec.rb create mode 100644 spec/unit/active_model_lint_spec.rb rename spec/{couchrest => unit}/assocations_spec.rb (98%) rename spec/{couchrest => unit}/attachment_spec.rb (99%) rename spec/{couchrest => unit}/base_spec.rb (98%) rename spec/{couchrest => unit}/casted_model_spec.rb (97%) rename spec/{couchrest => unit}/casted_spec.rb (89%) rename spec/{couchrest => unit}/class_proxy_spec.rb (98%) rename spec/{couchrest => unit}/collection_spec.rb (97%) rename spec/{couchrest => unit}/configuration_spec.rb (92%) rename spec/{couchrest => unit}/connection_spec.rb (97%) rename spec/{couchrest => unit}/core_extensions/time_parsing.rb (100%) rename spec/{couchrest => unit}/design_doc_spec.rb (97%) rename spec/{couchrest => unit}/designs/view_spec.rb (100%) rename spec/{couchrest => unit}/designs_spec.rb (97%) rename spec/{couchrest => unit}/dirty_spec.rb (97%) rename spec/{couchrest => unit}/inherited_spec.rb (92%) rename spec/{couchrest => unit}/persistence_spec.rb (97%) rename spec/{couchrest => unit}/property_protection_spec.rb (99%) rename spec/{couchrest => unit}/property_spec.rb (96%) rename spec/{couchrest => unit}/proxyable_spec.rb (98%) rename spec/{couchrest => unit}/subclass_spec.rb (89%) rename spec/{couchrest => unit}/typecast_spec.rb (98%) rename spec/{couchrest => unit}/validations_spec.rb (92%) rename spec/{couchrest => unit}/view_spec.rb (97%) diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index bf64e4b..f9bcec9 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -7,7 +7,6 @@ module CouchRest include CouchRest::Model::Configuration include CouchRest::Model::Connection include CouchRest::Model::Persistence - include CouchRest::Model::Callbacks include CouchRest::Model::DocumentQueries include CouchRest::Model::Views include CouchRest::Model::DesignDoc @@ -18,6 +17,7 @@ module CouchRest include CouchRest::Model::PropertyProtection include CouchRest::Model::Associations include CouchRest::Model::Validations + include CouchRest::Model::Callbacks include CouchRest::Model::Designs include CouchRest::Model::CastedBy include CouchRest::Model::Dirty diff --git a/lib/couchrest/model/callbacks.rb b/lib/couchrest/model/callbacks.rb index 3037810..18a36e9 100644 --- a/lib/couchrest/model/callbacks.rb +++ b/lib/couchrest/model/callbacks.rb @@ -5,21 +5,23 @@ module CouchRest #:nodoc: module Callbacks extend ActiveSupport::Concern + + CALLBACKS = [ + :before_validation, :after_validation, + :after_initialize, + :before_create, :around_create, :after_create, + :before_destroy, :around_destroy, :after_destroy, + :before_save, :around_save, :after_save, + :before_update, :around_update, :after_update, + ] + included do extend ActiveModel::Callbacks + include ActiveModel::Validations::Callbacks - define_model_callbacks \ - :create, - :destroy, - :save, - :update - + define_model_callbacks :initialize, :only => :after + define_model_callbacks :create, :destroy, :save, :update end - - def valid?(*) #nodoc - _run_validation_callbacks { super } - end - end end diff --git a/lib/couchrest/model/casted_model.rb b/lib/couchrest/model/casted_model.rb index f20de16..aa27e0a 100644 --- a/lib/couchrest/model/casted_model.rb +++ b/lib/couchrest/model/casted_model.rb @@ -5,11 +5,11 @@ module CouchRest::Model included do include CouchRest::Model::Configuration - include CouchRest::Model::Callbacks include CouchRest::Model::Properties include CouchRest::Model::PropertyProtection include CouchRest::Model::Associations include CouchRest::Model::Validations + include CouchRest::Model::Callbacks include CouchRest::Model::CastedBy include CouchRest::Model::Dirty class_eval do diff --git a/lib/couchrest/model/validations.rb b/lib/couchrest/model/validations.rb index 11a2571..825b169 100644 --- a/lib/couchrest/model/validations.rb +++ b/lib/couchrest/model/validations.rb @@ -13,22 +13,33 @@ module CouchRest # Validations may be applied to both Model::Base and Model::CastedModel module Validations extend ActiveSupport::Concern - included do - include ActiveModel::Validations - include ActiveModel::Validations::Callbacks + include ActiveModel::Validations + + # Determine if the document is valid. + # + # @example Is the document valid? + # person.valid? + # + # @example Is the document valid in a context? + # person.valid?(:create) + # + # @param [ Symbol ] context The optional validation context. + # + # @return [ true, false ] True if valid, false if not. + # + def valid?(context = nil) + super context ? context : (new? ? :create : :update) end - module ClassMethods - - # Validates the associated casted model. This method should not be + + # Validates the associated casted model. This method should not be # used within your code as it is automatically included when a CastedModel # is used inside the model. - # def validates_casted_model(*args) validates_with(CastedModelValidator, _merge_attributes(args)) end - + # Validates if the field is unique for this type of document. Automatically creates # a view if one does not already exist and performs a search for all matching # documents. diff --git a/lib/couchrest_model.rb b/lib/couchrest_model.rb index 956a2a6..97a491c 100644 --- a/lib/couchrest_model.rb +++ b/lib/couchrest_model.rb @@ -60,8 +60,8 @@ require "couchrest/model/core_extensions/time_parsing" # Base libraries require "couchrest/model/casted_model" require "couchrest/model/base" -# Add rails support *after* everything has loaded +# Add rails support *after* everything has loaded if defined?(Rails) require "couchrest/railtie" end diff --git a/spec/fixtures/more/article.rb b/spec/fixtures/models/article.rb similarity index 100% rename from spec/fixtures/more/article.rb rename to spec/fixtures/models/article.rb diff --git a/spec/fixtures/base.rb b/spec/fixtures/models/base.rb similarity index 100% rename from spec/fixtures/base.rb rename to spec/fixtures/models/base.rb diff --git a/spec/fixtures/more/card.rb b/spec/fixtures/models/card.rb similarity index 95% rename from spec/fixtures/more/card.rb rename to spec/fixtures/models/card.rb index 7494b44..8cf72ab 100644 --- a/spec/fixtures/more/card.rb +++ b/spec/fixtures/models/card.rb @@ -1,3 +1,5 @@ +require 'person' + class Card < CouchRest::Model::Base # Set the default database to use use_database DB diff --git a/spec/fixtures/more/cat.rb b/spec/fixtures/models/cat.rb similarity index 100% rename from spec/fixtures/more/cat.rb rename to spec/fixtures/models/cat.rb diff --git a/spec/fixtures/more/client.rb b/spec/fixtures/models/client.rb similarity index 100% rename from spec/fixtures/more/client.rb rename to spec/fixtures/models/client.rb diff --git a/spec/fixtures/more/course.rb b/spec/fixtures/models/course.rb similarity index 88% rename from spec/fixtures/more/course.rb rename to spec/fixtures/models/course.rb index d06f6a0..2998bb2 100644 --- a/spec/fixtures/more/course.rb +++ b/spec/fixtures/models/course.rb @@ -1,5 +1,5 @@ -require File.join(FIXTURE_PATH, 'more', 'question') -require File.join(FIXTURE_PATH, 'more', 'person') +require 'question' +require 'person' class Course < CouchRest::Model::Base use_database TEST_SERVER.default_database diff --git a/spec/fixtures/more/event.rb b/spec/fixtures/models/event.rb similarity index 100% rename from spec/fixtures/more/event.rb rename to spec/fixtures/models/event.rb diff --git a/spec/fixtures/more/invoice.rb b/spec/fixtures/models/invoice.rb similarity index 98% rename from spec/fixtures/more/invoice.rb rename to spec/fixtures/models/invoice.rb index 540c9ff..927fd8f 100644 --- a/spec/fixtures/more/invoice.rb +++ b/spec/fixtures/models/invoice.rb @@ -6,9 +6,9 @@ class Invoice < CouchRest::Model::Base property :client_name property :employee_name property :location - + # Validation validates_presence_of :client_name, :employee_name validates_presence_of :location, :message => "Hey stupid!, you forgot the location" - + end diff --git a/spec/fixtures/more/key_chain.rb b/spec/fixtures/models/key_chain.rb similarity index 100% rename from spec/fixtures/more/key_chain.rb rename to spec/fixtures/models/key_chain.rb diff --git a/spec/fixtures/models/membership.rb b/spec/fixtures/models/membership.rb new file mode 100644 index 0000000..0c7f639 --- /dev/null +++ b/spec/fixtures/models/membership.rb @@ -0,0 +1,4 @@ +class Membership < Hash + include CouchRest::Model::CastedModel + +end diff --git a/spec/fixtures/more/person.rb b/spec/fixtures/models/person.rb similarity index 91% rename from spec/fixtures/more/person.rb rename to spec/fixtures/models/person.rb index 076bbc0..de5de5c 100644 --- a/spec/fixtures/more/person.rb +++ b/spec/fixtures/models/person.rb @@ -1,3 +1,5 @@ +require 'cat' + class Person < Hash include ::CouchRest::Model::CastedModel property :pet, Cat diff --git a/spec/fixtures/more/question.rb b/spec/fixtures/models/question.rb similarity index 100% rename from spec/fixtures/more/question.rb rename to spec/fixtures/models/question.rb diff --git a/spec/fixtures/more/sale_entry.rb b/spec/fixtures/models/sale_entry.rb similarity index 100% rename from spec/fixtures/more/sale_entry.rb rename to spec/fixtures/models/sale_entry.rb diff --git a/spec/fixtures/more/sale_invoice.rb b/spec/fixtures/models/sale_invoice.rb similarity index 72% rename from spec/fixtures/more/sale_invoice.rb rename to spec/fixtures/models/sale_invoice.rb index ad6beb2..290dd9c 100644 --- a/spec/fixtures/more/sale_invoice.rb +++ b/spec/fixtures/models/sale_invoice.rb @@ -1,5 +1,6 @@ -require File.join(FIXTURE_PATH, 'more', 'client') -require File.join(FIXTURE_PATH, 'more', 'sale_entry') +require 'client' +require 'sale_entry' + class SaleInvoice < CouchRest::Model::Base use_database DB @@ -10,4 +11,4 @@ class SaleInvoice < CouchRest::Model::Base property :date, Date property :price, Integer -end \ No newline at end of file +end diff --git a/spec/fixtures/more/service.rb b/spec/fixtures/models/service.rb similarity index 100% rename from spec/fixtures/more/service.rb rename to spec/fixtures/models/service.rb diff --git a/spec/fixtures/more/user.rb b/spec/fixtures/models/user.rb similarity index 100% rename from spec/fixtures/more/user.rb rename to spec/fixtures/models/user.rb diff --git a/spec/functional/validations_spec.rb b/spec/functional/validations_spec.rb new file mode 100644 index 0000000..fc06203 --- /dev/null +++ b/spec/functional/validations_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe CouchRest::Model::Validations do + + let(:invoice) do + Invoice.new() + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 75ed89d..284d0d1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,9 +1,14 @@ +$LOAD_PATH.unshift(File.dirname(__FILE__)) +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) + +MODELS = File.join(File.dirname(__FILE__), "fixtures", "models") +$LOAD_PATH.unshift(MODELS) + require "bundler/setup" require "rubygems" -require "rspec" # Satisfies Autotest and anyone else not using the Rake tasks +require "rspec" -require File.join(File.dirname(__FILE__), '..','lib','couchrest_model') -# check the following file to see how to use the spec'd features. +require 'couchrest_model' unless defined?(FIXTURE_PATH) FIXTURE_PATH = File.join(File.dirname(__FILE__), '/fixtures') @@ -16,6 +21,21 @@ unless defined?(FIXTURE_PATH) DB = TEST_SERVER.database(TESTDB) end +RSpec.configure do |config| + config.before(:all) { reset_test_db! } + + config.after(:all) do + cr = TEST_SERVER + test_dbs = cr.databases.select { |db| db =~ /^#{TESTDB}/ } + test_dbs.each do |db| + cr.database(db).delete! rescue nil + end + end +end + +# Require each of the fixture models +Dir[ File.join(MODELS, "*.rb") ].sort.each { |file| require File.basename(file) } + class Basic < CouchRest::Model::Base use_database TEST_SERVER.default_database end @@ -27,17 +47,6 @@ def reset_test_db! DB end -RSpec.configure do |config| - config.before(:all) { reset_test_db! } - - config.after(:all) do - cr = TEST_SERVER - test_dbs = cr.databases.select { |db| db =~ /^#{TESTDB}/ } - test_dbs.each do |db| - cr.database(db).delete! rescue nil - end - end -end def couchdb_lucene_available? lucene_path = "http://localhost:5985/" diff --git a/spec/unit/active_model_lint_spec.rb b/spec/unit/active_model_lint_spec.rb new file mode 100644 index 0000000..3cc5d3d --- /dev/null +++ b/spec/unit/active_model_lint_spec.rb @@ -0,0 +1,30 @@ +# encoding: utf-8 +require 'spec_helper' +require 'test/unit/assertions' +require 'active_model/lint' + +class CompliantModel < CouchRest::Model::Base +end + + +describe CouchRest::Model::Base do + include Test::Unit::Assertions + include ActiveModel::Lint::Tests + + before :each do + @model = CompliantModel.new + end + + describe "active model lint tests" do + ActiveModel::Lint::Tests.public_instance_methods.map{|m| m.to_s}.grep(/^test/).each do |m| + example m.gsub('_',' ') do + send m + end + end + end + + def model + @model + end + +end diff --git a/spec/couchrest/assocations_spec.rb b/spec/unit/assocations_spec.rb similarity index 98% rename from spec/couchrest/assocations_spec.rb rename to spec/unit/assocations_spec.rb index 0affb6e..528b5d0 100644 --- a/spec/couchrest/assocations_spec.rb +++ b/spec/unit/assocations_spec.rb @@ -1,7 +1,5 @@ # encoding: utf-8 -require File.expand_path('../../spec_helper', __FILE__) -require File.join(FIXTURE_PATH, 'more', 'sale_invoice') - +require 'spec_helper' describe "Assocations" do diff --git a/spec/couchrest/attachment_spec.rb b/spec/unit/attachment_spec.rb similarity index 99% rename from spec/couchrest/attachment_spec.rb rename to spec/unit/attachment_spec.rb index 420b88c..0d050d3 100644 --- a/spec/couchrest/attachment_spec.rb +++ b/spec/unit/attachment_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../spec_helper', __FILE__) +require 'spec_helper' describe "Model attachments" do diff --git a/spec/couchrest/base_spec.rb b/spec/unit/base_spec.rb similarity index 98% rename from spec/couchrest/base_spec.rb rename to spec/unit/base_spec.rb index e3c665f..c375a7c 100644 --- a/spec/couchrest/base_spec.rb +++ b/spec/unit/base_spec.rb @@ -1,11 +1,5 @@ # encoding: utf-8 - -require File.expand_path("../../spec_helper", __FILE__) -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'article') -require File.join(FIXTURE_PATH, 'more', 'course') -require File.join(FIXTURE_PATH, 'more', 'card') -require File.join(FIXTURE_PATH, 'base') +require "spec_helper" describe "Model Base" do diff --git a/spec/couchrest/casted_model_spec.rb b/spec/unit/casted_model_spec.rb similarity index 97% rename from spec/couchrest/casted_model_spec.rb rename to spec/unit/casted_model_spec.rb index 2bed08c..9e47ab0 100644 --- a/spec/couchrest/casted_model_spec.rb +++ b/spec/unit/casted_model_spec.rb @@ -1,12 +1,5 @@ # encoding: utf-8 - -require File.expand_path('../../spec_helper', __FILE__) -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'person') -require File.join(FIXTURE_PATH, 'more', 'card') -require File.join(FIXTURE_PATH, 'more', 'question') -require File.join(FIXTURE_PATH, 'more', 'course') - +require "spec_helper" class WithCastedModelMixin < Hash include CouchRest::Model::CastedModel diff --git a/spec/couchrest/casted_spec.rb b/spec/unit/casted_spec.rb similarity index 89% rename from spec/couchrest/casted_spec.rb rename to spec/unit/casted_spec.rb index 44e4231..a7b4860 100644 --- a/spec/couchrest/casted_spec.rb +++ b/spec/unit/casted_spec.rb @@ -1,7 +1,4 @@ -require File.expand_path('../../spec_helper', __FILE__) -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'person') -require File.join(FIXTURE_PATH, 'more', 'card') +require "spec_helper" class Driver < CouchRest::Model::Base use_database TEST_SERVER.default_database diff --git a/spec/couchrest/class_proxy_spec.rb b/spec/unit/class_proxy_spec.rb similarity index 98% rename from spec/couchrest/class_proxy_spec.rb rename to spec/unit/class_proxy_spec.rb index 1572d8d..f945d30 100644 --- a/spec/couchrest/class_proxy_spec.rb +++ b/spec/unit/class_proxy_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path("../../spec_helper", __FILE__) +require "spec_helper" class UnattachedDoc < CouchRest::Model::Base # Note: no use_database here diff --git a/spec/couchrest/collection_spec.rb b/spec/unit/collection_spec.rb similarity index 97% rename from spec/couchrest/collection_spec.rb rename to spec/unit/collection_spec.rb index 63a1626..e501072 100644 --- a/spec/couchrest/collection_spec.rb +++ b/spec/unit/collection_spec.rb @@ -1,5 +1,4 @@ -require File.expand_path("../../spec_helper", __FILE__) -require File.join(FIXTURE_PATH, 'more', 'article') +require "spec_helper" describe "Collections" do diff --git a/spec/couchrest/configuration_spec.rb b/spec/unit/configuration_spec.rb similarity index 92% rename from spec/couchrest/configuration_spec.rb rename to spec/unit/configuration_spec.rb index 9053eab..26255f9 100644 --- a/spec/couchrest/configuration_spec.rb +++ b/spec/unit/configuration_spec.rb @@ -1,8 +1,7 @@ # encoding: utf-8 -require File.expand_path('../../spec_helper', __FILE__) -require File.join(FIXTURE_PATH, 'more', 'cat') +require "spec_helper" -describe CouchRest::Model::Base do +describe CouchRest::Model::Configuration do before do @class = Class.new(CouchRest::Model::Base) diff --git a/spec/couchrest/connection_spec.rb b/spec/unit/connection_spec.rb similarity index 97% rename from spec/couchrest/connection_spec.rb rename to spec/unit/connection_spec.rb index 4fc6b74..4607991 100644 --- a/spec/couchrest/connection_spec.rb +++ b/spec/unit/connection_spec.rb @@ -1,7 +1,7 @@ # encoding: utf-8 -require File.expand_path('../../spec_helper', __FILE__) +require 'spec_helper' -describe CouchRest::Model::Base do +describe CouchRest::Model::Connection do before do @class = Class.new(CouchRest::Model::Base) diff --git a/spec/couchrest/core_extensions/time_parsing.rb b/spec/unit/core_extensions/time_parsing.rb similarity index 100% rename from spec/couchrest/core_extensions/time_parsing.rb rename to spec/unit/core_extensions/time_parsing.rb diff --git a/spec/couchrest/design_doc_spec.rb b/spec/unit/design_doc_spec.rb similarity index 97% rename from spec/couchrest/design_doc_spec.rb rename to spec/unit/design_doc_spec.rb index fcd40c1..d034d68 100644 --- a/spec/couchrest/design_doc_spec.rb +++ b/spec/unit/design_doc_spec.rb @@ -1,10 +1,7 @@ # encoding: utf-8 +require 'spec_helper' -require File.expand_path("../../spec_helper", __FILE__) -require File.join(FIXTURE_PATH, 'base') -require File.join(FIXTURE_PATH, 'more', 'article') - -describe "Design Documents" do +describe CouchRest::Model::DesignDoc do before :all do reset_test_db! diff --git a/spec/couchrest/designs/view_spec.rb b/spec/unit/designs/view_spec.rb similarity index 100% rename from spec/couchrest/designs/view_spec.rb rename to spec/unit/designs/view_spec.rb diff --git a/spec/couchrest/designs_spec.rb b/spec/unit/designs_spec.rb similarity index 97% rename from spec/couchrest/designs_spec.rb rename to spec/unit/designs_spec.rb index 3fa2f6a..e4e981d 100644 --- a/spec/couchrest/designs_spec.rb +++ b/spec/unit/designs_spec.rb @@ -1,10 +1,9 @@ -require File.expand_path("../../spec_helper", __FILE__) +require "spec_helper" class DesignModel < CouchRest::Model::Base - end -describe "Design" do +describe CouchRest::Model::Designs do it "should accessable from model" do DesignModel.respond_to?(:design).should be_true diff --git a/spec/couchrest/dirty_spec.rb b/spec/unit/dirty_spec.rb similarity index 97% rename from spec/couchrest/dirty_spec.rb rename to spec/unit/dirty_spec.rb index 9129a23..f527d9c 100644 --- a/spec/couchrest/dirty_spec.rb +++ b/spec/unit/dirty_spec.rb @@ -1,10 +1,4 @@ -require File.expand_path("../../spec_helper", __FILE__) - -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'article') -require File.join(FIXTURE_PATH, 'more', 'course') -require File.join(FIXTURE_PATH, 'more', 'card') -require File.join(FIXTURE_PATH, 'base') +require "spec_helper" class WithCastedModelMixin < Hash include CouchRest::Model::CastedModel diff --git a/spec/couchrest/inherited_spec.rb b/spec/unit/inherited_spec.rb similarity index 92% rename from spec/couchrest/inherited_spec.rb rename to spec/unit/inherited_spec.rb index f19546f..04b8e3d 100644 --- a/spec/couchrest/inherited_spec.rb +++ b/spec/unit/inherited_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../../spec_helper', __FILE__) +require 'spec_helper' class PlainParent class_inheritable_accessor :foo diff --git a/spec/couchrest/persistence_spec.rb b/spec/unit/persistence_spec.rb similarity index 97% rename from spec/couchrest/persistence_spec.rb rename to spec/unit/persistence_spec.rb index 71ced97..3a8a042 100644 --- a/spec/couchrest/persistence_spec.rb +++ b/spec/unit/persistence_spec.rb @@ -1,13 +1,7 @@ # encoding: utf-8 -require File.expand_path('../../spec_helper', __FILE__) -require File.join(FIXTURE_PATH, 'base') -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'article') -require File.join(FIXTURE_PATH, 'more', 'course') -require File.join(FIXTURE_PATH, 'more', 'card') -require File.join(FIXTURE_PATH, 'more', 'event') +require 'spec_helper' -describe "Model Persistence" do +describe CouchRest::Model::Persistence do before(:each) do @obj = WithDefaultValues.new diff --git a/spec/couchrest/property_protection_spec.rb b/spec/unit/property_protection_spec.rb similarity index 99% rename from spec/couchrest/property_protection_spec.rb rename to spec/unit/property_protection_spec.rb index eb5ca55..9de0d94 100644 --- a/spec/couchrest/property_protection_spec.rb +++ b/spec/unit/property_protection_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path("../../spec_helper", __FILE__) +require "spec_helper" describe "Model Attributes" do diff --git a/spec/couchrest/property_spec.rb b/spec/unit/property_spec.rb similarity index 96% rename from spec/couchrest/property_spec.rb rename to spec/unit/property_spec.rb index 6da6b3b..b799908 100644 --- a/spec/couchrest/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -1,18 +1,7 @@ # encoding: utf-8 -require File.expand_path('../../spec_helper', __FILE__) -require File.join(FIXTURE_PATH, 'more', 'article') -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'person') -require File.join(FIXTURE_PATH, 'more', 'card') -require File.join(FIXTURE_PATH, 'more', 'invoice') -require File.join(FIXTURE_PATH, 'more', 'service') -require File.join(FIXTURE_PATH, 'more', 'event') -require File.join(FIXTURE_PATH, 'more', 'user') -require File.join(FIXTURE_PATH, 'more', 'course') -require File.join(FIXTURE_PATH, "more", "key_chain") +require 'spec_helper' - -describe "Model properties" do +describe CouchRest::Model::Property do before(:each) do reset_test_db! diff --git a/spec/couchrest/proxyable_spec.rb b/spec/unit/proxyable_spec.rb similarity index 98% rename from spec/couchrest/proxyable_spec.rb rename to spec/unit/proxyable_spec.rb index 3dc2064..fd38fbc 100644 --- a/spec/couchrest/proxyable_spec.rb +++ b/spec/unit/proxyable_spec.rb @@ -1,6 +1,4 @@ -require File.expand_path("../../spec_helper", __FILE__) - -require File.join(FIXTURE_PATH, 'more', 'cat') +require "spec_helper" class DummyProxyable < CouchRest::Model::Base proxy_database_method :db @@ -12,7 +10,7 @@ end class ProxyKitten < CouchRest::Model::Base end -describe "Proxyable" do +describe CouchRest::Model::Proxyable do describe "#proxy_database" do diff --git a/spec/couchrest/subclass_spec.rb b/spec/unit/subclass_spec.rb similarity index 89% rename from spec/couchrest/subclass_spec.rb rename to spec/unit/subclass_spec.rb index 75ab218..d8f835e 100644 --- a/spec/couchrest/subclass_spec.rb +++ b/spec/unit/subclass_spec.rb @@ -1,8 +1,4 @@ -require File.expand_path("../../spec_helper", __FILE__) -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'person') -require File.join(FIXTURE_PATH, 'more', 'card') -require File.join(FIXTURE_PATH, 'more', 'course') +require "spec_helper" # add a default value Card.property :bg_color, :default => '#ccc' diff --git a/spec/couchrest/typecast_spec.rb b/spec/unit/typecast_spec.rb similarity index 98% rename from spec/couchrest/typecast_spec.rb rename to spec/unit/typecast_spec.rb index 4174001..6db127e 100644 --- a/spec/couchrest/typecast_spec.rb +++ b/spec/unit/typecast_spec.rb @@ -1,8 +1,5 @@ # encoding: utf-8 -require File.expand_path('../../spec_helper', __FILE__) -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'person') -require File.join(FIXTURE_PATH, 'more', 'course') +require 'spec_helper' describe "Type Casting" do diff --git a/spec/couchrest/validations_spec.rb b/spec/unit/validations_spec.rb similarity index 92% rename from spec/couchrest/validations_spec.rb rename to spec/unit/validations_spec.rb index b387323..b9e2b09 100644 --- a/spec/couchrest/validations_spec.rb +++ b/spec/unit/validations_spec.rb @@ -1,14 +1,6 @@ -require File.expand_path("../../spec_helper", __FILE__) +require "spec_helper" -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'article') -require File.join(FIXTURE_PATH, 'more', 'course') -require File.join(FIXTURE_PATH, 'more', 'card') -require File.join(FIXTURE_PATH, 'base') - -# TODO Move validations from other specs to here - -describe "Validations" do +describe CouchRest::Model::Validations do describe "Uniqueness" do diff --git a/spec/couchrest/view_spec.rb b/spec/unit/view_spec.rb similarity index 97% rename from spec/couchrest/view_spec.rb rename to spec/unit/view_spec.rb index d54d252..b58e037 100644 --- a/spec/couchrest/view_spec.rb +++ b/spec/unit/view_spec.rb @@ -1,10 +1,6 @@ -require File.expand_path("../../spec_helper", __FILE__) -require File.join(FIXTURE_PATH, 'more', 'cat') -require File.join(FIXTURE_PATH, 'more', 'person') -require File.join(FIXTURE_PATH, 'more', 'article') -require File.join(FIXTURE_PATH, 'more', 'course') +require "spec_helper" -describe "Model views" do +describe CouchRest::Model::Views do class Unattached < CouchRest::Model::Base property :title @@ -17,7 +13,6 @@ describe "Model views" do nil end end - describe "ClassMethods" do # NOTE! Add more unit tests! From 778e486328c7b4e69bbaf4e6990a7e8fa2a42704 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Thu, 9 Jun 2011 01:49:09 +0200 Subject: [PATCH 19/34] Adding comparison using ids rather than hashes --- lib/couchrest/model/base.rb | 15 +++++++++++++++ spec/fixtures/models/project.rb | 6 ++++++ spec/unit/base_spec.rb | 19 ++++++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/models/project.rb diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index 8ae8cac..7b6255a 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -92,6 +92,21 @@ module CouchRest alias :to_param :id alias :new_record? :new? alias :new_document? :new? + + # Compare this model with another by confirming to see + # if the IDs and their databases match! + # + # Camparison of the database is required in case the + # model has been proxied or loaded elsewhere. + # + # A Basic CouchRest document will only ever compare using + # a Hash comparison on the attributes. + def == other + return false unless other.is_a?(Base) + database == other.database && id == other.id + end + alias :eql? :== + end end end diff --git a/spec/fixtures/models/project.rb b/spec/fixtures/models/project.rb new file mode 100644 index 0000000..d8de244 --- /dev/null +++ b/spec/fixtures/models/project.rb @@ -0,0 +1,6 @@ +class Project < CouchRest::Model::Base + use_database DB + property :name, String + timestamps! + view_by :name +end diff --git a/spec/unit/base_spec.rb b/spec/unit/base_spec.rb index c375a7c..fb17f0a 100644 --- a/spec/unit/base_spec.rb +++ b/spec/unit/base_spec.rb @@ -142,8 +142,25 @@ describe "Model Base" do @obj.destroyed?.should be_true end end + end - + describe "comparisons" do + describe "#==" do + it "should be true on same document" do + p = Project.create + p.should eql(p) + end + it "should be true after loading" do + p = Project.create + p.should eql(Project.get(p.id)) + end + it "should not be true if databases do not match" do + p = Project.create + p2 = p.dup + p2.stub!(:database).and_return('other') + p.should_not eql(p2) + end + end end describe "update attributes without saving" do From 98772ae98a4685d6a69a77a50d30bd2118cc3aad Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Thu, 9 Jun 2011 01:52:29 +0200 Subject: [PATCH 20/34] Updating history --- history.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/history.md b/history.md index 99cf842..8c457e9 100644 --- a/history.md +++ b/history.md @@ -1,6 +1,12 @@ # CouchRest Model Change History -## 1.1.0.rc - 2011-06-08 +## 1.1.0 - 2011-06-XX + +* Minor Fixes + * Validation callbacks now support context (thanks kostia) + * Document comparisons now performed using database and document ID (pointer by neocsr) + +## 1.1.0.rc1 - 2011-06-08 * New Features * Properties with a nil value are now no longer sent to the database. From f6d88530b711ac374bdef57c8b1f07c3d22bff41 Mon Sep 17 00:00:00 2001 From: Chase DuBois Date: Sun, 12 Jun 2011 14:19:59 -0400 Subject: [PATCH 21/34] set default ORM in Rails 3.1 --- lib/couchrest/railtie.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/couchrest/railtie.rb b/lib/couchrest/railtie.rb index b1266c4..b08d00f 100644 --- a/lib/couchrest/railtie.rb +++ b/lib/couchrest/railtie.rb @@ -2,10 +2,13 @@ require "rails" require "active_model/railtie" module CouchRest - # = Active Record Railtie class ModelRailtie < Rails::Railtie - config.generators.orm :couchrest_model - config.generators.test_framework :test_unit, :fixture => false + def self.generator + config.respond_to?(:app_generators) ? :app_generators : :generators + end + + config.send(generator).orm :couchrest_model + config.send(generator).test_framework :test_unit, :fixture => false initializer "couchrest_model.configure_default_connection" do CouchRest::Model::Base.configure do |conf| From b3a005b86d4f8ee08703d219c3b710f3076c35bd Mon Sep 17 00:00:00 2001 From: Chase DuBois Date: Sun, 12 Jun 2011 14:27:45 -0400 Subject: [PATCH 22/34] persisted? should be false after a document has been destroyed. See https://github.com/rails/rails/blob/master/activemodel/CHANGELOG#L72 --- lib/couchrest/model/base.rb | 2 +- lib/couchrest/model/casted_model.rb | 2 +- spec/unit/base_spec.rb | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index 7b6255a..82b6561 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -82,7 +82,7 @@ module CouchRest end def persisted? - !new? + !new? && !destroyed? end def to_key diff --git a/lib/couchrest/model/casted_model.rb b/lib/couchrest/model/casted_model.rb index 0074e18..614c410 100644 --- a/lib/couchrest/model/casted_model.rb +++ b/lib/couchrest/model/casted_model.rb @@ -44,7 +44,7 @@ module CouchRest::Model alias :new_record? :new? def persisted? - !new? + !new? && !destroyed? end # The to_param method is needed for rails to generate resourceful routes. diff --git a/spec/unit/base_spec.rb b/spec/unit/base_spec.rb index fb17f0a..59ed673 100644 --- a/spec/unit/base_spec.rb +++ b/spec/unit/base_spec.rb @@ -110,14 +110,22 @@ describe "Model Base" do describe "#persisted?" do context "when the document is new" do it "returns false" do - @obj.persisted?.should == false + @obj.persisted?.should be_false end end context "when the document is not new" do it "returns id" do @obj.save - @obj.persisted?.should == true + @obj.persisted?.should be_true + end + end + + context "when the document is destroyed" do + it "returns false" do + @obj.save + @obj.destroy + @obj.persisted?.should be_false end end end From 406d2bc791978c66f0b72c22cb33af10515fde32 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sat, 25 Jun 2011 00:58:50 +0200 Subject: [PATCH 23/34] Casted Model now no longer depends on a Hash --- history.md | 3 +++ lib/couchrest/model/base.rb | 4 ---- lib/couchrest/model/casted_model.rb | 24 +++++++------------ lib/couchrest/model/persistence.rb | 7 +++++- lib/couchrest/model/properties.rb | 2 +- spec/fixtures/models/cat.rb | 4 ++-- spec/unit/casted_model_spec.rb | 37 +++++++++++++++++++++++++++-- spec/unit/dirty_spec.rb | 2 +- 8 files changed, 57 insertions(+), 26 deletions(-) diff --git a/history.md b/history.md index 8c457e9..62b8542 100644 --- a/history.md +++ b/history.md @@ -2,6 +2,9 @@ ## 1.1.0 - 2011-06-XX +* Major Fixes + * CastedModel no longer requires a Hash. Automatically includes all required methods. + * Minor Fixes * Validation callbacks now support context (thanks kostia) * Document comparisons now performed using database and document ID (pointer by neocsr) diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index 7b6255a..66f3b66 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -81,10 +81,6 @@ module CouchRest super end - def persisted? - !new? - end - def to_key new? ? nil : [id] end diff --git a/lib/couchrest/model/casted_model.rb b/lib/couchrest/model/casted_model.rb index 0074e18..e445f96 100644 --- a/lib/couchrest/model/casted_model.rb +++ b/lib/couchrest/model/casted_model.rb @@ -1,9 +1,9 @@ module CouchRest::Model module CastedModel - extend ActiveSupport::Concern included do + include CouchRest::Attributes include CouchRest::Model::Configuration include CouchRest::Model::Properties include CouchRest::Model::PropertyProtection @@ -19,23 +19,17 @@ module CouchRest::Model def base_doc? false # Can never be base doc! end + + # Initialize a new Casted Model. Accepts the same + # options as CouchRest::Model::Base for preparing and initializing + # attributes. + def initialize(keys = {}, options = {}) + super() + prepare_all_attributes(keys, options) + end end end - def initialize(keys = {}) - raise StandardError unless self.is_a? Hash - prepare_all_attributes(keys) - super() - end - - def []= key, value - super(key.to_s, value) - end - - def [] key - super(key.to_s) - end - # False if the casted model has already # been saved in the containing document def new? diff --git a/lib/couchrest/model/persistence.rb b/lib/couchrest/model/persistence.rb index 03a9ee8..a9e0a75 100644 --- a/lib/couchrest/model/persistence.rb +++ b/lib/couchrest/model/persistence.rb @@ -28,7 +28,8 @@ module CouchRest # Trigger the callbacks (before, after, around) # only if the document isn't new def update(options = {}) - raise "Calling #{self.class.name}#update on document that has not been created!" if self.new? + raise "Cannot save a destroyed document!" if destroyed? + raise "Calling #{self.class.name}#update on document that has not been created!" if new? return false unless perform_validations(options) return true if !self.disable_dirty && !self.changed? _run_update_callbacks do @@ -69,6 +70,10 @@ module CouchRest !!@_destroyed end + def persisted? + !new? && !destroyed? + end + # Update the document's attributes and save. For example: # # doc.update_attributes :name => "Fred" diff --git a/lib/couchrest/model/properties.rb b/lib/couchrest/model/properties.rb index 133e9b9..93cd447 100644 --- a/lib/couchrest/model/properties.rb +++ b/lib/couchrest/model/properties.rb @@ -168,7 +168,7 @@ module CouchRest # check if this property is going to casted type = options.delete(:type) || options.delete(:cast_as) if block_given? - type = Class.new(Hash) do + type = Class.new do include CastedModel end if block.arity == 1 # Traditional, with options diff --git a/spec/fixtures/models/cat.rb b/spec/fixtures/models/cat.rb index 481bc37..d8e5584 100644 --- a/spec/fixtures/models/cat.rb +++ b/spec/fixtures/models/cat.rb @@ -1,6 +1,6 @@ -class CatToy < Hash - include ::CouchRest::Model::CastedModel +class CatToy + include CouchRest::Model::CastedModel property :name diff --git a/spec/unit/casted_model_spec.rb b/spec/unit/casted_model_spec.rb index 9e47ab0..5c8b6f5 100644 --- a/spec/unit/casted_model_spec.rb +++ b/spec/unit/casted_model_spec.rb @@ -1,7 +1,7 @@ # encoding: utf-8 require "spec_helper" -class WithCastedModelMixin < Hash +class WithCastedModelMixin include CouchRest::Model::CastedModel property :name property :no_value @@ -9,11 +9,17 @@ class WithCastedModelMixin < Hash property :casted_attribute, WithCastedModelMixin end +class OldFashionedMixin < Hash + include CouchRest::Model::CastedModel + property :name +end + class DummyModel < CouchRest::Model::Base use_database TEST_SERVER.default_database raise "Default DB not set" if TEST_SERVER.default_database.nil? property :casted_attribute, WithCastedModelMixin property :keywords, [String] + property :old_casted_attribute, OldFashionedMixin property :sub_models do |child| child.property :title end @@ -22,7 +28,7 @@ class DummyModel < CouchRest::Model::Base end end -class WithCastedCallBackModel < Hash +class WithCastedCallBackModel include CouchRest::Model::CastedModel property :name property :run_before_validation @@ -155,6 +161,33 @@ describe CouchRest::Model::CastedModel do end end + # Basic testing for an old fashioned casted hash + describe "old hash casted as attribute" do + before :each do + @obj = DummyModel.new(:old_casted_attribute => {:name => 'Testing'}) + @casted_obj = @obj.old_casted_attribute + end + it "should be available from its parent" do + @casted_obj.should be_an_instance_of(OldFashionedMixin) + end + + it "should have the getters defined" do + @casted_obj.name.should == 'Testing' + end + + it "should know who casted it" do + @casted_obj.casted_by.should == @obj + end + + it "should know which property casted it" do + @casted_obj.casted_by_property.should == @obj.properties.detect{|p| p.to_s == 'old_casted_attribute'} + end + + it "should return nil for the unknown attribute" do + @casted_obj["unknown"].should be_nil + end + end + describe "casted as an array of a different type" do before(:each) do @obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé']) diff --git a/spec/unit/dirty_spec.rb b/spec/unit/dirty_spec.rb index f527d9c..ceadb16 100644 --- a/spec/unit/dirty_spec.rb +++ b/spec/unit/dirty_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -class WithCastedModelMixin < Hash +class WithCastedModelMixin include CouchRest::Model::CastedModel property :name property :details, Object, :default => {} From a8a1372e571e6f05b63b46eea5f4b380d5d7722d Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sat, 25 Jun 2011 01:49:15 +0200 Subject: [PATCH 24/34] Removing old fashioned class_evals (more to go) --- lib/couchrest/model/associations.rb | 8 ++--- lib/couchrest/model/properties.rb | 52 +++++++++++----------------- spec/fixtures/models/sale_invoice.rb | 2 +- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/lib/couchrest/model/associations.rb b/lib/couchrest/model/associations.rb index f8e5a12..e98e48e 100644 --- a/lib/couchrest/model/associations.rb +++ b/lib/couchrest/model/associations.rb @@ -183,19 +183,19 @@ module CouchRest casted_by[casted_by_property.to_s] << obj.id end end - + def << obj check_obj(obj) casted_by[casted_by_property.to_s] << obj.id super(obj) end - + def push(obj) check_obj(obj) casted_by[casted_by_property.to_s].push obj.id super(obj) end - + def unshift(obj) check_obj(obj) casted_by[casted_by_property.to_s].unshift obj.id @@ -212,7 +212,7 @@ module CouchRest casted_by[casted_by_property.to_s].pop super end - + def shift casted_by[casted_by_property.to_s].shift super diff --git a/lib/couchrest/model/properties.rb b/lib/couchrest/model/properties.rb index 93cd447..94ad48d 100644 --- a/lib/couchrest/model/properties.rb +++ b/lib/couchrest/model/properties.rb @@ -149,15 +149,13 @@ module CouchRest # These properties are casted as Time objects, so they should always # be set to UTC. def timestamps! - class_eval <<-EOS, __FILE__, __LINE__ - property(:updated_at, Time, :read_only => true, :protected => true, :auto_validation => false) - property(:created_at, Time, :read_only => true, :protected => true, :auto_validation => false) + property(:updated_at, Time, :read_only => true, :protected => true, :auto_validation => false) + property(:created_at, Time, :read_only => true, :protected => true, :auto_validation => false) - set_callback :save, :before do |object| - write_attribute('updated_at', Time.now) - write_attribute('created_at', Time.now) if object.new? - end - EOS + set_callback :save, :before do |object| + write_attribute('updated_at', Time.now) + write_attribute('created_at', Time.now) if object.new? + end end protected @@ -191,42 +189,32 @@ module CouchRest # defines the getter for the property (and optional aliases) def create_property_getter(property) - # meth = property.name - class_eval <<-EOS, __FILE__, __LINE__ + 1 - def #{property.name} - read_attribute('#{property.name}') - end - EOS + define_method(property.name) do + read_attribute(property.name) + end if ['boolean', TrueClass.to_s.downcase].include?(property.type.to_s.downcase) - class_eval <<-EOS, __FILE__, __LINE__ - def #{property.name}? - value = read_attribute('#{property.name}') - !(value.nil? || value == false) - end - EOS + define_method("#{property.name}?") do + value = read_attribute(property.name) + !(value.nil? || value == false) + end end if property.alias - class_eval <<-EOS, __FILE__, __LINE__ + 1 - alias #{property.alias.to_sym} #{property.name.to_sym} - EOS + alias_method(property.alias, property.name.to_sym) end end # defines the setter for the property (and optional aliases) def create_property_setter(property) - property_name = property.name - class_eval <<-EOS - def #{property_name}=(value) - write_attribute('#{property_name}', value) - end - EOS + name = property.name + + define_method("#{name}=") do |value| + write_attribute(name, value) + end if property.alias - class_eval <<-EOS - alias #{property.alias.to_sym}= #{property_name.to_sym}= - EOS + alias_method "#{property.alias}=", "#{name}=" end end diff --git a/spec/fixtures/models/sale_invoice.rb b/spec/fixtures/models/sale_invoice.rb index 290dd9c..5244ba5 100644 --- a/spec/fixtures/models/sale_invoice.rb +++ b/spec/fixtures/models/sale_invoice.rb @@ -1,7 +1,7 @@ require 'client' require 'sale_entry' -class SaleInvoice < CouchRest::Model::Base +class SaleInvoice < CouchRest::Model::Base use_database DB belongs_to :client From e91812ca538cbda699e74d96614392d385fdb9ed Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sat, 25 Jun 2011 02:30:47 +0200 Subject: [PATCH 25/34] Comparing using attribute hash if ids are nil --- history.md | 1 + lib/couchrest/model/base.rb | 7 +++++- spec/unit/base_spec.rb | 43 ++++++++++++++++++++++++++----------- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/history.md b/history.md index 9261320..fdfd2b6 100644 --- a/history.md +++ b/history.md @@ -9,6 +9,7 @@ * Validation callbacks now support context (thanks kostia) * Document comparisons now performed using database and document ID (pointer by neocsr) * Automatic config generation now supported (thanks lucasrenan) + * Comparing documents resorts to Hash comparison if both IDs are nil. (pointer by kostia) ## 1.1.0.rc1 - 2011-06-08 diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index 66f3b66..53cdc42 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -99,7 +99,12 @@ module CouchRest # a Hash comparison on the attributes. def == other return false unless other.is_a?(Base) - database == other.database && id == other.id + if id.nil? && other.id.nil? + # no ids? assume comparing nested and revert to hash comparison + to_hash == other.to_hash + else + database == other.database && id == other.id + end end alias :eql? :== diff --git a/spec/unit/base_spec.rb b/spec/unit/base_spec.rb index 59ed673..1454a1f 100644 --- a/spec/unit/base_spec.rb +++ b/spec/unit/base_spec.rb @@ -154,19 +154,38 @@ describe "Model Base" do describe "comparisons" do describe "#==" do - it "should be true on same document" do - p = Project.create - p.should eql(p) + context "on saved document" do + it "should be true on same document" do + p = Project.create + p.should eql(p) + end + it "should be true after loading" do + p = Project.create + p.should eql(Project.get(p.id)) + end + it "should not be true if databases do not match" do + p = Project.create + p2 = p.dup + p2.stub!(:database).and_return('other') + p.should_not eql(p2) + end + it "should always be false if one document not saved" do + p = Project.create(:name => 'test') + o = Project.new(:name => 'test') + p.should_not eql(o) + end end - it "should be true after loading" do - p = Project.create - p.should eql(Project.get(p.id)) - end - it "should not be true if databases do not match" do - p = Project.create - p2 = p.dup - p2.stub!(:database).and_return('other') - p.should_not eql(p2) + context "with new documents" do + it "should be true when attributes match" do + p = Project.new(:name => 'test') + o = Project.new(:name => 'test') + p.should eql(o) + end + it "should not be true when attributes don't match" do + p = Project.new(:name => 'test') + o = Project.new(:name => 'testing') + p.should_not eql(o) + end end end end From e40b96519e9ee00523eee254a8d0fb966d42abc9 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sat, 25 Jun 2011 17:36:32 +0200 Subject: [PATCH 26/34] Preparing for 1.1.0 release --- VERSION | 2 +- couchrest_model.gemspec | 2 +- history.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index de1d3a8..9084fa2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0.rc1 +1.1.0 diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index 7a500b7..9138525 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_dependency(%q, "1.1.0.pre3") + s.add_dependency(%q, "1.1.0") s.add_dependency(%q, "~> 1.15") s.add_dependency(%q, "~> 3.0") s.add_dependency(%q, "~> 0.3.22") diff --git a/history.md b/history.md index fdfd2b6..cff753c 100644 --- a/history.md +++ b/history.md @@ -1,6 +1,6 @@ # CouchRest Model Change History -## 1.1.0 - 2011-06-XX +## 1.1.0 - 2011-06-25 * Major Fixes * CastedModel no longer requires a Hash. Automatically includes all required methods. From 8efa5208da250824f29939937aec70e746d44406 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Sat, 25 Jun 2011 19:24:43 +0200 Subject: [PATCH 27/34] Renaming casted model to embeddable and preparing for 1.1.0 launch@ --- history.md | 3 +- lib/couchrest/model/base.rb | 5 ++-- .../model/{casted_model.rb => embeddable.rb} | 11 ++++++- lib/couchrest/model/properties.rb | 2 +- lib/couchrest/model/property.rb | 2 +- lib/couchrest/railtie.rb | 2 +- lib/couchrest_model.rb | 3 +- spec/fixtures/models/cat.rb | 2 +- spec/fixtures/models/membership.rb | 4 +-- spec/fixtures/models/person.rb | 4 +-- spec/fixtures/models/question.rb | 6 ++-- spec/unit/base_spec.rb | 11 +++++++ ...asted_model_spec.rb => embeddable_spec.rb} | 29 +++++++++---------- 13 files changed, 51 insertions(+), 33 deletions(-) rename lib/couchrest/model/{casted_model.rb => embeddable.rb} (87%) rename spec/unit/{casted_model_spec.rb => embeddable_spec.rb} (97%) diff --git a/history.md b/history.md index cff753c..f7fd395 100644 --- a/history.md +++ b/history.md @@ -2,8 +2,9 @@ ## 1.1.0 - 2011-06-25 -* Major Fixes +* Major Alterations * CastedModel no longer requires a Hash. Automatically includes all required methods. + * CastedModel module renamed to Embeddable (old still works!) * Minor Fixes * Validation callbacks now support context (thanks kostia) diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index 53cdc42..ed46699 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -47,8 +47,8 @@ module CouchRest # # Options supported: # - # * :directly_set_attributes: true when data comes directly from database - # * :database: provide an alternative database + # * :directly_set_attributes, true when data comes directly from database + # * :database, provide an alternative database # # If a block is provided the new model will be passed into the # block so that it can be populated. @@ -64,6 +64,7 @@ module CouchRest yield self if block_given? after_initialize if respond_to?(:after_initialize) + run_callbacks(:initialize) { self } end diff --git a/lib/couchrest/model/casted_model.rb b/lib/couchrest/model/embeddable.rb similarity index 87% rename from lib/couchrest/model/casted_model.rb rename to lib/couchrest/model/embeddable.rb index e445f96..2da140c 100644 --- a/lib/couchrest/model/casted_model.rb +++ b/lib/couchrest/model/embeddable.rb @@ -1,5 +1,5 @@ module CouchRest::Model - module CastedModel + module Embeddable extend ActiveSupport::Concern included do @@ -26,6 +26,7 @@ module CouchRest::Model def initialize(keys = {}, options = {}) super() prepare_all_attributes(keys, options) + run_callbacks(:initialize) { self } end end end @@ -61,6 +62,14 @@ module CouchRest::Model end alias :attributes= :update_attributes_without_saving + end # End Embeddable + + # Provide backwards compatability with previous versions (pre 1.1.0) + module CastedModel + extend ActiveSupport::Concern + included do + include CouchRest::Model::Embeddable + end end end diff --git a/lib/couchrest/model/properties.rb b/lib/couchrest/model/properties.rb index 94ad48d..7b2c494 100644 --- a/lib/couchrest/model/properties.rb +++ b/lib/couchrest/model/properties.rb @@ -167,7 +167,7 @@ module CouchRest type = options.delete(:type) || options.delete(:cast_as) if block_given? type = Class.new do - include CastedModel + include Embeddable end if block.arity == 1 # Traditional, with options type.class_eval { yield type } diff --git a/lib/couchrest/model/property.rb b/lib/couchrest/model/property.rb index 9beb5c7..f303b07 100644 --- a/lib/couchrest/model/property.rb +++ b/lib/couchrest/model/property.rb @@ -45,7 +45,7 @@ module CouchRest::Model # Cast an individual value, not an array def cast_value(parent, value) - raise "An array inside an array cannot be casted, use CastedModel" if value.is_a?(Array) + raise "An array inside an array cannot be casted, use Embeddable module" if value.is_a?(Array) value = typecast_value(value, self) associate_casted_value_to_parent(parent, value) end diff --git a/lib/couchrest/railtie.rb b/lib/couchrest/railtie.rb index b08d00f..0c4d04a 100644 --- a/lib/couchrest/railtie.rb +++ b/lib/couchrest/railtie.rb @@ -6,7 +6,7 @@ module CouchRest def self.generator config.respond_to?(:app_generators) ? :app_generators : :generators end - + config.send(generator).orm :couchrest_model config.send(generator).test_framework :test_unit, :fixture => false diff --git a/lib/couchrest_model.rb b/lib/couchrest_model.rb index 97a491c..699da9e 100644 --- a/lib/couchrest_model.rb +++ b/lib/couchrest_model.rb @@ -33,7 +33,6 @@ require "couchrest/model/property_protection" require "couchrest/model/properties" require "couchrest/model/casted_array" require "couchrest/model/casted_hash" -require "couchrest/model/casted_model" require "couchrest/model/validations" require "couchrest/model/callbacks" require "couchrest/model/document_queries" @@ -58,7 +57,7 @@ require "couchrest/model/core_extensions/hash" require "couchrest/model/core_extensions/time_parsing" # Base libraries -require "couchrest/model/casted_model" +require "couchrest/model/embeddable" require "couchrest/model/base" # Add rails support *after* everything has loaded diff --git a/spec/fixtures/models/cat.rb b/spec/fixtures/models/cat.rb index d8e5584..a28f505 100644 --- a/spec/fixtures/models/cat.rb +++ b/spec/fixtures/models/cat.rb @@ -1,6 +1,6 @@ class CatToy - include CouchRest::Model::CastedModel + include CouchRest::Model::Embeddable property :name diff --git a/spec/fixtures/models/membership.rb b/spec/fixtures/models/membership.rb index 0c7f639..81c804e 100644 --- a/spec/fixtures/models/membership.rb +++ b/spec/fixtures/models/membership.rb @@ -1,4 +1,4 @@ -class Membership < Hash - include CouchRest::Model::CastedModel +class Membership + include CouchRest::Model::Embeddable end diff --git a/spec/fixtures/models/person.rb b/spec/fixtures/models/person.rb index de5de5c..71525e4 100644 --- a/spec/fixtures/models/person.rb +++ b/spec/fixtures/models/person.rb @@ -1,7 +1,7 @@ require 'cat' -class Person < Hash - include ::CouchRest::Model::CastedModel +class Person + include ::CouchRest::Model::Embeddable property :pet, Cat property :name, [String] diff --git a/spec/fixtures/models/question.rb b/spec/fixtures/models/question.rb index 5efcd20..ada5bd9 100644 --- a/spec/fixtures/models/question.rb +++ b/spec/fixtures/models/question.rb @@ -1,6 +1,6 @@ -class Question < Hash - include ::CouchRest::Model::CastedModel - +class Question + include ::CouchRest::Model::Embeddable + property :q property :a diff --git a/spec/unit/base_spec.rb b/spec/unit/base_spec.rb index 1454a1f..de4a619 100644 --- a/spec/unit/base_spec.rb +++ b/spec/unit/base_spec.rb @@ -69,6 +69,17 @@ describe "Model Base" do @doc = WithAfterInitializeMethod.new {|d| d.some_value = "foo"} @doc['some_value'].should eql('foo') end + + it "should call after_initialize callback if available" do + klass = Class.new(CouchRest::Model::Base) + klass.class_eval do # for ruby 1.8.7 + property :name + after_initialize :set_name + def set_name; self.name = "foobar"; end + end + @doc = klass.new + @doc.name.should eql("foobar") + end end describe "ActiveModel compatability Basic" do diff --git a/spec/unit/casted_model_spec.rb b/spec/unit/embeddable_spec.rb similarity index 97% rename from spec/unit/casted_model_spec.rb rename to spec/unit/embeddable_spec.rb index 5c8b6f5..c56f304 100644 --- a/spec/unit/casted_model_spec.rb +++ b/spec/unit/embeddable_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" class WithCastedModelMixin - include CouchRest::Model::CastedModel + include CouchRest::Model::Embeddable property :name property :no_value property :details, Object, :default => {} @@ -29,7 +29,7 @@ class DummyModel < CouchRest::Model::Base end class WithCastedCallBackModel - include CouchRest::Model::CastedModel + include CouchRest::Model::Embeddable property :name property :run_before_validation property :run_after_validation @@ -50,19 +50,7 @@ class CastedCallbackDoc < CouchRest::Model::Base property :callback_model, WithCastedCallBackModel end -describe CouchRest::Model::CastedModel do - - describe "A non hash class including CastedModel" do - it "should fail raising and include error" do - lambda do - class NotAHashButWithCastedModelMixin - include CouchRest::CastedModel - property :name - end - - end.should raise_error - end - end +describe CouchRest::Model::Embeddable do describe "isolated" do before(:each) do @@ -81,7 +69,16 @@ describe CouchRest::Model::CastedModel do it "should always return base_doc? as false" do @obj.base_doc?.should be_false end - + it "should call after_initialize callback if available" do + klass = Class.new do + include CouchRest::Model::CastedModel + after_initialize :set_name + property :name + def set_name; self.name = "foobar"; end + end + @obj = klass.new + @obj.name.should eql("foobar") + end end describe "casted as an attribute, but without a value" do From 9d724aee47e532c263c818b1dce839377bd8abab Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Mon, 4 Jul 2011 18:53:25 +0200 Subject: [PATCH 28/34] Fix Embeddable and super issues. Prep for release 1.1.1 --- VERSION | 2 +- couchrest_model.gemspec | 2 +- history.md | 6 ++++++ lib/couchrest/model/embeddable.rb | 21 ++++++++++++--------- spec/unit/embeddable_spec.rb | 11 +++++++++++ 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 9084fa2..524cb55 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 +1.1.1 diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index 9138525..8e7e315 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_dependency(%q, "1.1.0") + s.add_dependency(%q, "1.1.1") s.add_dependency(%q, "~> 1.15") s.add_dependency(%q, "~> 3.0") s.add_dependency(%q, "~> 0.3.22") diff --git a/history.md b/history.md index f7fd395..e1c8329 100644 --- a/history.md +++ b/history.md @@ -1,5 +1,11 @@ # CouchRest Model Change History +## 1.1.1 - 2011-07-04 + +* Minor fix + * Bumping CouchRest version dependency for important initialize method fix. + * Ensuring super on Embeddable#initialize can be called. + ## 1.1.0 - 2011-06-25 * Major Alterations diff --git a/lib/couchrest/model/embeddable.rb b/lib/couchrest/model/embeddable.rb index 2da140c..05970a2 100644 --- a/lib/couchrest/model/embeddable.rb +++ b/lib/couchrest/model/embeddable.rb @@ -2,8 +2,10 @@ module CouchRest::Model module Embeddable extend ActiveSupport::Concern + # Include Attributes early to ensure super() will work + include CouchRest::Attributes + included do - include CouchRest::Attributes include CouchRest::Model::Configuration include CouchRest::Model::Properties include CouchRest::Model::PropertyProtection @@ -20,17 +22,18 @@ module CouchRest::Model false # Can never be base doc! end - # Initialize a new Casted Model. Accepts the same - # options as CouchRest::Model::Base for preparing and initializing - # attributes. - def initialize(keys = {}, options = {}) - super() - prepare_all_attributes(keys, options) - run_callbacks(:initialize) { self } - end end end + # Initialize a new Casted Model. Accepts the same + # options as CouchRest::Model::Base for preparing and initializing + # attributes. + def initialize(keys = {}, options = {}) + super() + prepare_all_attributes(keys, options) + run_callbacks(:initialize) { self } + end + # False if the casted model has already # been saved in the containing document def new? diff --git a/spec/unit/embeddable_spec.rb b/spec/unit/embeddable_spec.rb index c56f304..9b5b52e 100644 --- a/spec/unit/embeddable_spec.rb +++ b/spec/unit/embeddable_spec.rb @@ -79,6 +79,17 @@ describe CouchRest::Model::Embeddable do @obj = klass.new @obj.name.should eql("foobar") end + it "should allow override of initialize with super" do + klass = Class.new do + include CouchRest::Model::Embeddable + after_initialize :set_name + property :name + def set_name; self.name = "foobar"; end + def initialize(attrs = {}); super(); end + end + @obj = klass.new + @obj.name.should eql("foobar") + end end describe "casted as an attribute, but without a value" do From 3f1b2ea0c63db643447cf5ea6306df26c950da7f Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Tue, 19 Jul 2011 18:03:31 +0200 Subject: [PATCH 29/34] Casting array type properties now possible --- history.md | 5 +++++ lib/couchrest/model/property.rb | 3 +-- spec/unit/property_spec.rb | 15 +++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/history.md b/history.md index e1c8329..26bdae8 100644 --- a/history.md +++ b/history.md @@ -1,5 +1,10 @@ # CouchRest Model Change History +## 1.1.2 - 2011-07-XX + +* Minor fix + * Removing restriction that prohibited objects that cast as an array to be loaded. + ## 1.1.1 - 2011-07-04 * Minor fix diff --git a/lib/couchrest/model/property.rb b/lib/couchrest/model/property.rb index f303b07..6058fda 100644 --- a/lib/couchrest/model/property.rb +++ b/lib/couchrest/model/property.rb @@ -43,9 +43,8 @@ module CouchRest::Model end end - # Cast an individual value, not an array + # Cast an individual value def cast_value(parent, value) - raise "An array inside an array cannot be casted, use Embeddable module" if value.is_a?(Array) value = typecast_value(value, self) associate_casted_value_to_parent(parent, value) end diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index b799908..49548e9 100644 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -442,15 +442,18 @@ describe "Property Class" do ary.last.should eql(Date.new(2011, 05, 22)) end - it "should raise and error if value is array when type is not" do - property = CouchRest::Model::Property.new(:test, Date) + it "should cast an object that provides an array" do + prop = Class.new do + attr_accessor :ary + def initialize(val); self.ary = val; end + def as_json; ary; end + end + property = CouchRest::Model::Property.new(:test, prop) parent = mock("FooClass") - lambda { - cast = property.cast(parent, [Date.new(2010, 6, 1)]) - }.should raise_error + cast = property.cast(parent, [1, 2]) + cast.ary.should eql([1, 2]) end - it "should set parent as casted_by object in CastedArray" do property = CouchRest::Model::Property.new(:test, [Object]) parent = mock("FooObject") From 3258ac22e99c7e3e90fc9623c5d0193e24f04f80 Mon Sep 17 00:00:00 2001 From: Sam Lown Date: Tue, 19 Jul 2011 21:28:44 +0200 Subject: [PATCH 30/34] updating to couchrest 1.1.2 and as_couch_json method --- couchrest_model.gemspec | 2 +- history.md | 6 ++++++ lib/couchrest/model/properties.rb | 6 ++++-- spec/unit/property_spec.rb | 14 +++++++++++--- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index 8e7e315..4c873e8 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_dependency(%q, "1.1.1") + s.add_dependency(%q, "~> 1.1.2") s.add_dependency(%q, "~> 1.15") s.add_dependency(%q, "~> 3.0") s.add_dependency(%q, "~> 0.3.22") diff --git a/history.md b/history.md index e1c8329..387f171 100644 --- a/history.md +++ b/history.md @@ -1,5 +1,11 @@ # CouchRest Model Change History +## 1.1.2 - 2011-07-XX + +* Minor fixes + * Upgrade to couchrest 1.1.2 + * Override as_couch_json to ensure nil values not stored + ## 1.1.1 - 2011-07-04 * Minor fix diff --git a/lib/couchrest/model/properties.rb b/lib/couchrest/model/properties.rb index 7b2c494..54cfb71 100644 --- a/lib/couchrest/model/properties.rb +++ b/lib/couchrest/model/properties.rb @@ -12,8 +12,10 @@ module CouchRest raise "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (method_defined?(:[]) && method_defined?(:[]=)) end - def as_json(options = nil) - Hash[self].reject{|k,v| v.nil?}.as_json(options) + # Provide an attribute hash ready to be sent to CouchDB but with + # all the nil attributes removed. + def as_couch_json + super.delete_if{|k,v| v.nil?} end # Returns the Class properties with their values diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index b799908..790beb4 100644 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -62,15 +62,23 @@ describe CouchRest::Model::Property do @card.updated_at.should_not be_nil end - describe "#as_json" do + describe "#as_couch_json" do it "should provide a simple hash from model" do - @card.as_json.class.should eql(Hash) + @card.as_couch_json.class.should eql(Hash) end it "should remove properties from Hash if value is nil" do @card.last_name = nil - @card.as_json.keys.include?('last_name').should be_false + @card.as_couch_json.keys.include?('last_name').should be_false + end + + end + + describe "#as_json" do + + it "should provide a simple hash from model" do + @card.as_json.class.should eql(Hash) end it "should pass options to Active Support's as_json" do From 72e3ac37d65b905d7d405d1fe8a32c799fdae07f Mon Sep 17 00:00:00 2001 From: Kim Burgestrand Date: Sun, 31 Jul 2011 04:40:31 +0200 Subject: [PATCH 31/34] Add CouchRest::Model::Base.respond_to_missing? and respond_to? --- lib/couchrest/model/base.rb | 15 +++++++++++++++ spec/unit/view_spec.rb | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/couchrest/model/base.rb b/lib/couchrest/model/base.rb index ed46699..40cb968 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -82,6 +82,21 @@ module CouchRest super end + # compatbility for 1.8, it does not use respond_to_missing? + # thing is, when using it like this only, doing method(:find_by_view) + # will throw an error + def self.respond_to?(m, include_private = false) + super || respond_to_missing?(m, include_private) + end + + # ruby 1.9 feature + # this allows ruby to know that the method is defined using + # method_missing, and as such, method(:find_by_view) will actually + # give a Method back, and not throw an error like in 1.8! + def self.respond_to_missing?(m, include_private = false) + has_view?(m) || has_view?(m.to_s[/^find_(by_.+)/, 1]) + end + def to_key new? ? nil : [id] end diff --git a/spec/unit/view_spec.rb b/spec/unit/view_spec.rb index b58e037..d69630c 100644 --- a/spec/unit/view_spec.rb +++ b/spec/unit/view_spec.rb @@ -173,7 +173,22 @@ describe CouchRest::Model::Views do 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! From 465c0681e22aa47dbcd1ec3c15575eb3c6819a08 Mon Sep 17 00:00:00 2001 From: Kim Burgestrand Date: Sun, 31 Jul 2011 04:44:35 +0200 Subject: [PATCH 32/34] Tell contextual validations specs which database to use --- spec/fixtures/models/base.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/fixtures/models/base.rb b/spec/fixtures/models/base.rb index b64df11..8e62eb8 100644 --- a/spec/fixtures/models/base.rb +++ b/spec/fixtures/models/base.rb @@ -85,11 +85,13 @@ end # Following two fixture classes have __intentionally__ diffent syntax for setting the validation context class WithContextualValidationOnCreate < CouchRest::Model::Base + use_database TEST_SERVER.default_database property(:name, String) validates(:name, :presence => {:on => :create}) end class WithContextualValidationOnUpdate < CouchRest::Model::Base + use_database TEST_SERVER.default_database property(:name, String) validates(:name, :presence => true, :on => :update) end From 2f00599209b415d0f374b010b28674128ca2eb86 Mon Sep 17 00:00:00 2001 From: Marcos Tapajos Date: Sun, 21 Aug 2011 01:59:48 -0300 Subject: [PATCH 33/34] added rake to gemfile --- couchrest_model.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index fe58c09..cc22676 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -30,6 +30,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, "~> 2.6.0") s.add_development_dependency(%q, ["~> 1.5.1"]) s.add_development_dependency(%q, ">= 0.5.7") + s.add_development_dependency("rake", ">= 0.8.0") # s.add_development_dependency("jruby-openssl", ">= 0.7.3") end From 1c695f58bf76e310259f5928c884c961bfa47586 Mon Sep 17 00:00:00 2001 From: Marcos Tapajos Date: Sun, 21 Aug 2011 02:02:56 -0300 Subject: [PATCH 34/34] update readme --- history.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/history.md b/history.md index 01f0c44..a8b18ce 100644 --- a/history.md +++ b/history.md @@ -1,5 +1,9 @@ # CouchRest Model Change History +## 1.1.3 + + * CouchRest::Model::Base.respond_to_missing? and respond_to? (Kim Burgestrand) + ## 1.1.2 - 2011-07-23 * Minor fixes