Skip to content

Commit

Permalink
Completed support for :allow_blank property option and enabled suppor…
Browse files Browse the repository at this point in the history
…t to ignore blank values in associations. This is especially useful for html forms that provide empty values
  • Loading branch information
samlown committed Aug 2, 2012
1 parent fbfdc0d commit f7e2f5f
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 8 deletions.
1 change: 1 addition & 0 deletions couchrest_model.gemspec
Expand Up @@ -31,6 +31,7 @@ Gem::Specification.new do |s|
s.add_development_dependency(%q<json>, ["~> 1.5.1"])
s.add_development_dependency(%q<rack-test>, ">= 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

13 changes: 11 additions & 2 deletions lib/couchrest/model/associations.rb
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/couchrest/model/properties.rb
Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions lib/couchrest/model/property.rb
Expand Up @@ -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))
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/couchrest/model/typecast.rb
Expand Up @@ -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?
Expand Down
10 changes: 10 additions & 0 deletions spec/unit/assocations_spec.rb
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
19 changes: 17 additions & 2 deletions spec/unit/property_spec.rb
Expand Up @@ -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')
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit f7e2f5f

Please sign in to comment.