diff --git a/couchrest_model.gemspec b/couchrest_model.gemspec index 7442dfa9..f2cb8d9b 100644 --- a/couchrest_model.gemspec +++ b/couchrest_model.gemspec @@ -31,6 +31,7 @@ Gem::Specification.new do |s| 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("debugger", "~> 1.2.0") # s.add_development_dependency("jruby-openssl", ">= 0.7.3") end diff --git a/lib/couchrest/model/associations.rb b/lib/couchrest/model/associations.rb index 974fb288..1bf3060a 100644 --- a/lib/couchrest/model/associations.rb +++ b/lib/couchrest/model/associations.rb @@ -38,7 +38,7 @@ module ClassMethods def belongs_to(attrib, *options) opts = merge_belongs_to_association_options(attrib, options.first) - property(opts[:foreign_key], opts) + property(opts[:foreign_key], String, opts) create_belongs_to_getter(attrib, opts) create_belongs_to_setter(attrib, opts) @@ -88,7 +88,7 @@ def collection_of(attrib, *options) opts[:foreign_key] = opts[:foreign_key].pluralize opts[:readonly] = true - property(opts[:foreign_key], [], opts) + property(opts[:foreign_key], [String], opts) create_collection_of_property_setter(attrib, opts) create_collection_of_getter(attrib, opts) @@ -225,6 +225,15 @@ def check_obj(obj) raise "Object cannot be added to #{casted_by.class.to_s}##{casted_by_property.to_s} collection unless saved" if obj.new? end + # Override CastedArray instantiation_and_cast method for a simpler + # version that will not try to cast the model. + def instantiate_and_cast(obj, change = true) + couchrest_parent_will_change! if change && use_dirty? + obj.casted_by = casted_by if obj.respond_to?(:casted_by) + obj.casted_by_property = casted_by_property if obj.respond_to?(:casted_by_property) + obj + end + end end diff --git a/lib/couchrest/model/properties.rb b/lib/couchrest/model/properties.rb index 05f668f6..e365e511 100644 --- a/lib/couchrest/model/properties.rb +++ b/lib/couchrest/model/properties.rb @@ -40,7 +40,7 @@ def read_attribute(property) # with a property and update dirty status def write_attribute(property, value) prop = find_property!(property) - value = prop.is_a?(String) ? value : prop.cast(self, value) + value = prop.cast(self, value) couchrest_attribute_will_change!(prop.name) if use_dirty? && self[prop.name] != value self[prop.name] = value end diff --git a/lib/couchrest/model/property.rb b/lib/couchrest/model/property.rb index 6656f843..ed2ec2a6 100644 --- a/lib/couchrest/model/property.rb +++ b/lib/couchrest/model/property.rb @@ -33,6 +33,7 @@ def cast(parent, value) raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}" end arr = value.collect { |data| cast_value(parent, data) } + arr.reject!{ |data| data.nil? } unless allow_blank # allow casted_by calls to be passed up chain by wrapping in CastedArray CastedArray.new(arr, self, parent) elsif (type == Object || type == Hash) && (value.is_a?(Hash)) @@ -45,8 +46,12 @@ def cast(parent, value) # Cast an individual value def cast_value(parent, value) - value = typecast_value(parent, self, value) - associate_casted_value_to_parent(parent, value) + if !allow_blank && value.to_s.empty? + nil + else + value = typecast_value(parent, self, value) + associate_casted_value_to_parent(parent, value) + end end def default_value diff --git a/lib/couchrest/model/typecast.rb b/lib/couchrest/model/typecast.rb index 454b6fa9..fd40b0e9 100644 --- a/lib/couchrest/model/typecast.rb +++ b/lib/couchrest/model/typecast.rb @@ -3,7 +3,7 @@ module Model module Typecast def typecast_value(parent, property, value) - return nil if value.nil? || (!property.allow_blank && value.to_s.empty?) + return nil if value.nil? klass = property.type_class if value.instance_of?(klass) || klass == Object if klass == Time && !value.utc? diff --git a/spec/unit/assocations_spec.rb b/spec/unit/assocations_spec.rb index 528b5d09..26faa957 100644 --- a/spec/unit/assocations_spec.rb +++ b/spec/unit/assocations_spec.rb @@ -70,6 +70,11 @@ def SaleInvoice.merge_assoc_opts(*args) @invoice.client end + it "should ignore blank ids" do + @invoice.client_id = "" + @invoice.client_id.should be_nil + end + it "should allow override of foreign key" do @invoice.respond_to?(:alternate_client).should be_true @invoice.respond_to?("alternate_client=").should be_true @@ -116,6 +121,11 @@ def SaleInvoice.merge_assoc_opts(*args) @invoice.entries.first.should eql(@entries.first) end + it "should ignore blank ids when set directly" do + @invoice.entry_ids = ["", @entries.first.id] + @invoice.entry_ids.length.should be(1) + end + it "should replace collection if ids replaced" do @invoice.entry_ids = @entries.collect{|i| i.id} @invoice.entries.length.should eql(3) # load once diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index 98d00bfd..baac4de1 100644 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -328,7 +328,6 @@ @course.questions.last.class.should eql(Question) end - it "should raise an error if attempting to set single value for array type" do lambda { @course.questions = Question.new(:q => 'test1') @@ -433,7 +432,7 @@ property.allow_blank.should be_true end - it "should allow setting of the allow_blank option to false" do + it "should allow setting of the allow_blank option to false" do property = CouchRest::Model::Property.new(:test, String, :allow_blank => false) property.allow_blank.should be_false end @@ -476,6 +475,22 @@ property.cast(parent, ["2010-06-01", "2010-06-02"]).should eql([Date.new(2010, 6, 1), Date.new(2010, 6, 2)]) end + context "when allow_blank is false" do + let :parent do + mock("FooObject") + end + + it "should convert blank to nil" do + property = CouchRest::Model::Property.new(:test, String, :allow_blank => false) + property.cast(parent, "").should be_nil + end + + it "should remove blank array entries" do + property = CouchRest::Model::Property.new(:test, [String], :allow_blank => false) + property.cast(parent, ["", "foo"]).should eql(["foo"]) + end + end + it "should set a CastedArray on array of Objects" do property = CouchRest::Model::Property.new(:test, [Object]) parent = mock("FooObject")