diff --git a/VERSION b/VERSION index 45a1b3f..524cb55 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.2 +1.1.1 diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index cc22676..8e7e315 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -6,7 +6,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = ["J. Chris Anderson", "Matt Aimonetti", "Marcos Tapajos", "Will Leinweber", "Sam Lown"] - s.date = File.mtime('VERSION') + s.date = %q{2011-04-29} s.description = %q{CouchRest Model provides aditional features to the standard CouchRest Document class such as properties, view designs, associations, callbacks, typecasting and validations.} s.email = %q{jchris@apache.org} s.extra_rdoc_files = [ @@ -23,14 +23,13 @@ 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.2") + 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") 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 diff --git a/history.md b/history.md index a8b18ce..e1c8329 100644 --- a/history.md +++ b/history.md @@ -1,16 +1,5 @@ # 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 - * Upgrade to couchrest 1.1.2 - * Override as_couch_json to ensure nil values not stored - * 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/base.rb b/lib/couchrest/model/base.rb index 40cb968..ed46699 100644 --- a/lib/couchrest/model/base.rb +++ b/lib/couchrest/model/base.rb @@ -82,21 +82,6 @@ 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/lib/couchrest/model/design_doc.rb b/lib/couchrest/model/design_doc.rb index 36c3940..b634351 100644 --- a/lib/couchrest/model/design_doc.rb +++ b/lib/couchrest/model/design_doc.rb @@ -7,11 +7,7 @@ module CouchRest module ClassMethods def design_doc - @design_doc ||= if auto_update_design_doc - ::CouchRest::Design.new(default_design_doc) - else - stored_design_doc || ::CouchRest::Design.new(default_design_doc) - end + @design_doc ||= ::CouchRest::Design.new(default_design_doc) end def design_doc_id @@ -79,25 +75,16 @@ module CouchRest # If auto updates enabled, check checksum cache return design_doc if auto_update_design_doc && design_doc_cache_checksum(db) == checksum - retries = 1 - begin - # Load up the stored doc (if present), update, and save - saved = stored_design_doc(db) - if saved - if force || saved['couchrest-hash'] != checksum - saved.merge!(design_doc) - db.save_doc(saved) - @design_doc = saved # update memo to point to the document we actually saved - end - else - design_doc.delete('_rev') # This is a new document and so doesn't have a revision yet - db.save_doc(design_doc) + # Load up the stored doc (if present), update, and save + saved = stored_design_doc(db) + if saved + if force || saved['couchrest-hash'] != checksum + saved.merge!(design_doc) + db.save_doc(saved) end - rescue RestClient::Conflict - # if we get a conflict retry the operation... - raise if retries < 1 - retries -= 1 - retry + else + db.save_doc(design_doc) + design_doc.delete('_rev') # Prevent conflicts, never store rev as DB specific end # Ensure checksum cached for next attempt if using auto updates diff --git a/lib/couchrest/model/designs.rb b/lib/couchrest/model/designs.rb index 3862567..e17f1bc 100644 --- a/lib/couchrest/model/designs.rb +++ b/lib/couchrest/model/designs.rb @@ -58,11 +58,10 @@ module CouchRest self.model = model end - # Generate a method that will provide a new View instance when - # requested. This will also define the view in CouchDB unless - # auto_update_design_doc is disabled. + # Define a view and generate a method that will provide a new + # View instance when requested. def view(name, opts = {}) - View.create(model, name, opts) if model.auto_update_design_doc + View.create(model, name, opts) create_view_method(name) end diff --git a/lib/couchrest/model/properties.rb b/lib/couchrest/model/properties.rb index 54cfb71..7b2c494 100644 --- a/lib/couchrest/model/properties.rb +++ b/lib/couchrest/model/properties.rb @@ -12,10 +12,8 @@ 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 - # 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?} + def as_json(options = nil) + Hash[self].reject{|k,v| v.nil?}.as_json(options) end # Returns the Class properties with their values diff --git a/lib/couchrest/model/property.rb b/lib/couchrest/model/property.rb index 6058fda..f303b07 100644 --- a/lib/couchrest/model/property.rb +++ b/lib/couchrest/model/property.rb @@ -43,8 +43,9 @@ module CouchRest::Model end end - # Cast an individual value + # Cast an individual value, not an array 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/lib/couchrest/model/views.rb b/lib/couchrest/model/views.rb index f8331e5..89dffbd 100644 --- a/lib/couchrest/model/views.rb +++ b/lib/couchrest/model/views.rb @@ -72,12 +72,9 @@ module CouchRest # spec/couchrest/more/extended_doc_spec.rb. def view_by(*keys) - return unless auto_update_design_doc - opts = keys.pop if keys.last.is_a?(Hash) opts ||= {} ducktype = opts.delete(:ducktype) - unless ducktype || opts[:map] opts[:guards] ||= [] opts[:guards].push "(doc['#{model_type_key}'] == '#{self.to_s}')" diff --git a/spec/fixtures/models/base.rb b/spec/fixtures/models/base.rb index 8e62eb8..b64df11 100644 --- a/spec/fixtures/models/base.rb +++ b/spec/fixtures/models/base.rb @@ -85,13 +85,11 @@ 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 diff --git a/spec/unit/design_doc_spec.rb b/spec/unit/design_doc_spec.rb index 299f5e6..d034d68 100644 --- a/spec/unit/design_doc_spec.rb +++ b/spec/unit/design_doc_spec.rb @@ -159,68 +159,39 @@ describe CouchRest::Model::DesignDoc do end describe "when auto_update_design_doc false" do - # We really do need a new class for each of example. If we try - # to use the same class the examples interact with each in ways - # that can hide failures because the design document gets cached - # at the class level. - let(:model_class) { - class_name = "#{example.metadata[:full_description].gsub(/\s+/,'_').camelize}Model" - doc = CouchRest::Document.new("_id" => "_design/#{class_name}") - doc["language"] = "javascript" - doc["views"] = {"all" => {"map" => - "function(doc) { - if (doc['type'] == 'Article') { - emit(doc['_id'],1); - } - }"}, - "by_name" => {"map" => - "function(doc) { - if ((doc['type'] == '#{class_name}') && (doc['name'] != null)) { - emit(doc['name'], null); - }", - "reduce" => - "function(keys, values, rereduce) { - return sum(values); - }"}} - - DB.save_doc doc + + before :all do + Article.auto_update_design_doc = false + Article.save_design_doc! + end - eval <<-KLASS - class ::#{class_name} < CouchRest::Model::Base - use_database DB - self.auto_update_design_doc = false - design do - view :by_name - end - property :name, String - end - KLASS + after :all do + Article.auto_update_design_doc = true + end - class_name.constantize - } + it "will not send a request for the saved design doc" do + Article.should_not_receive(:stored_design_doc) + Article.by_date + end it "will not update stored design doc if view changed" do - model_class.by_name - orig = model_class.stored_design_doc - design = model_class.design_doc - view = design['views']['by_name']['map'] - design['views']['by_name']['map'] = view + ' ' - model_class.by_name - model_class.stored_design_doc['_rev'].should eql(orig['_rev']) + Article.by_date + orig = Article.stored_design_doc + design = Article.design_doc + view = design['views']['by_date']['map'] + design['views']['by_date']['map'] = view + ' ' + Article.by_date + Article.stored_design_doc['_rev'].should eql(orig['_rev']) end it "will update stored design if forced" do - model_class.by_name - orig = model_class.stored_design_doc - design = model_class.design_doc - view = design['views']['by_name']['map'] - design['views']['by_name']['map'] = view + ' ' - model_class.save_design_doc! - model_class.stored_design_doc['_rev'].should_not eql(orig['_rev']) - end - - it "is able to use predefined views" do - model_class.by_name(key: "special").all + Article.by_date + orig = Article.stored_design_doc + design = Article.design_doc + view = design['views']['by_date']['map'] + design['views']['by_date']['map'] = view + ' ' + Article.save_design_doc! + Article.stored_design_doc['_rev'].should_not eql(orig['_rev']) end end end diff --git a/spec/unit/designs_spec.rb b/spec/unit/designs_spec.rb index 96d02a3..e4e981d 100644 --- a/spec/unit/designs_spec.rb +++ b/spec/unit/designs_spec.rb @@ -83,23 +83,7 @@ describe CouchRest::Model::Designs do @object.should_receive(:create_view_method).with('test') @object.view('test') end - end - context "for model with auto_update_design_doc disabled " do - class ::DesignModelAutoUpdateDesignDocDisabled < ::CouchRest::Model::Base - self.auto_update_design_doc = false - end - - describe "#view" do - before :each do - @object = @klass.new(DesignModelAutoUpdateDesignDocDisabled) - end - - it "does not attempt to create view" do - CouchRest::Model::Designs::View.should_not_receive(:create) - @object.view('test') - end - end end describe "#filter" do diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index ce4bd89..b799908 100644 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -62,25 +62,17 @@ describe CouchRest::Model::Property do @card.updated_at.should_not be_nil end - describe "#as_couch_json" do - - it "should provide a simple hash from model" do - @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_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 remove properties from Hash if value is nil" do + @card.last_name = nil + @card.as_json.keys.include?('last_name').should be_false + end + it "should pass options to Active Support's as_json" do @card.last_name = "Aimonetti" @card.as_json(:only => 'last_name').should eql('last_name' => 'Aimonetti') @@ -450,18 +442,15 @@ describe "Property Class" do ary.last.should eql(Date.new(2011, 05, 22)) end - 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) + it "should raise and error if value is array when type is not" do + property = CouchRest::Model::Property.new(:test, Date) parent = mock("FooClass") - cast = property.cast(parent, [1, 2]) - cast.ary.should eql([1, 2]) + lambda { + cast = property.cast(parent, [Date.new(2010, 6, 1)]) + }.should raise_error end + it "should set parent as casted_by object in CastedArray" do property = CouchRest::Model::Property.new(:test, [Object]) parent = mock("FooObject") diff --git a/spec/unit/view_spec.rb b/spec/unit/view_spec.rb index d69630c..b58e037 100644 --- a/spec/unit/view_spec.rb +++ b/spec/unit/view_spec.rb @@ -173,22 +173,7 @@ 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!