bugfix: allow CastedArrays to be assigned

(And be a bit more general about it)
This commit is contained in:
Will Leinweber 2010-09-01 16:13:52 -05:00
parent c32992c21b
commit c2fde42fa6
2 changed files with 60 additions and 52 deletions

View file

@ -33,7 +33,7 @@ module CouchRest::Model
data.keys.sort.each do |k| data.keys.sort.each do |k|
value << data[k] value << data[k]
end end
elsif value.class != Array elsif !value.is_a?(Array)
raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}" raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}"
end end
arr = value.collect { |data| cast_value(parent, data) } arr = value.collect { |data| cast_value(parent, data) }
@ -72,12 +72,12 @@ module CouchRest::Model
def parse_type(type) def parse_type(type)
if type.nil? if type.nil?
@casted = false @casted = false
@type = nil @type = nil
@type_class = nil @type_class = nil
else else
base = type.is_a?(Array) ? type.first : type base = type.is_a?(Array) ? type.first : type
base = Object if base.nil? base = Object if base.nil?
raise "Defining a property type as a #{type.class.name.humanize} is not supported in CouchRest Model!" if base.class != Class raise "Defining a property type as a #{type.class.name.humanize} is not supported in CouchRest Model!" if base.class != Class
@type_class = base @type_class = base
@type = type @type = type
end end

View file

@ -31,11 +31,11 @@ class WithCastedCallBackModel < Hash
property :name property :name
property :run_before_validate property :run_before_validate
property :run_after_validate property :run_after_validate
before_validate do |object| before_validate do |object|
object.run_before_validate = true object.run_before_validate = true
end end
after_validate do |object| after_validate do |object|
object.run_after_validate = true object.run_after_validate = true
end end
end end
@ -47,7 +47,7 @@ class CastedCallbackDoc < CouchRest::Model::Base
end end
describe CouchRest::Model::CastedModel do describe CouchRest::Model::CastedModel do
describe "A non hash class including CastedModel" do describe "A non hash class including CastedModel" do
it "should fail raising and include error" do it "should fail raising and include error" do
lambda do lambda do
@ -55,27 +55,27 @@ describe CouchRest::Model::CastedModel do
include CouchRest::CastedModel include CouchRest::CastedModel
property :name property :name
end end
end.should raise_error end.should raise_error
end end
end end
describe "isolated" do describe "isolated" do
before(:each) do before(:each) do
@obj = WithCastedModelMixin.new @obj = WithCastedModelMixin.new
end end
it "should automatically include the property mixin and define getters and setters" do it "should automatically include the property mixin and define getters and setters" do
@obj.name = 'Matt' @obj.name = 'Matt'
@obj.name.should == 'Matt' @obj.name.should == 'Matt'
end end
it "should allow override of default" do it "should allow override of default" do
@obj = WithCastedModelMixin.new(:name => 'Eric', :details => {'color' => 'orange'}) @obj = WithCastedModelMixin.new(:name => 'Eric', :details => {'color' => 'orange'})
@obj.name.should == 'Eric' @obj.name.should == 'Eric'
@obj.details['color'].should == 'orange' @obj.details['color'].should == 'orange'
end end
end end
describe "casted as an attribute, but without a value" do describe "casted as an attribute, but without a value" do
before(:each) do before(:each) do
@obj = DummyModel.new @obj = DummyModel.new
@ -99,22 +99,22 @@ describe CouchRest::Model::CastedModel do
@obj.sub_models.first.title.should eql('test') @obj.sub_models.first.title.should eql('test')
end end
end end
describe "casted as attribute" do describe "casted as attribute" do
before(:each) do before(:each) do
casted = {:name => 'not whatever'} casted = {:name => 'not whatever'}
@obj = DummyModel.new(:casted_attribute => {:name => 'whatever', :casted_attribute => casted}) @obj = DummyModel.new(:casted_attribute => {:name => 'whatever', :casted_attribute => casted})
@casted_obj = @obj.casted_attribute @casted_obj = @obj.casted_attribute
end end
it "should be available from its parent" do it "should be available from its parent" do
@casted_obj.should be_an_instance_of(WithCastedModelMixin) @casted_obj.should be_an_instance_of(WithCastedModelMixin)
end end
it "should have the getters defined" do it "should have the getters defined" do
@casted_obj.name.should == 'whatever' @casted_obj.name.should == 'whatever'
end end
it "should know who casted it" do it "should know who casted it" do
@casted_obj.casted_by.should == @obj @casted_obj.casted_by.should == @obj
end end
@ -126,27 +126,27 @@ describe CouchRest::Model::CastedModel do
it "should return nil for the unknown attribute" do it "should return nil for the unknown attribute" do
@casted_obj["unknown"].should be_nil @casted_obj["unknown"].should be_nil
end end
it "should return {} for the hash attribute" do it "should return {} for the hash attribute" do
@casted_obj.details.should == {} @casted_obj.details.should == {}
end end
it "should cast its own attributes" do it "should cast its own attributes" do
@casted_obj.casted_attribute.should be_instance_of(WithCastedModelMixin) @casted_obj.casted_attribute.should be_instance_of(WithCastedModelMixin)
end end
end end
describe "casted as an array of a different type" do describe "casted as an array of a different type" do
before(:each) do before(:each) do
@obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé']) @obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé'])
end end
it "should cast the array properly" do it "should cast the array properly" do
@obj.keywords.should be_an_instance_of(Array) @obj.keywords.should be_an_instance_of(Array)
@obj.keywords.first.should == 'couch' @obj.keywords.first.should == 'couch'
end end
end end
describe "update attributes without saving" do describe "update attributes without saving" do
before(:each) do before(:each) do
@question = Question.new(:q => "What is your quest?", :a => "To seek the Holy Grail") @question = Question.new(:q => "What is your quest?", :a => "To seek the Holy Grail")
@ -158,20 +158,20 @@ describe CouchRest::Model::CastedModel do
@question['q'].should == "What is your favorite color?" @question['q'].should == "What is your favorite color?"
@question.a.should == "Blue" @question.a.should == "Blue"
end end
it "should also work for attributes= alias" do it "should also work for attributes= alias" do
@question.respond_to?(:attributes=).should be_true @question.respond_to?(:attributes=).should be_true
@question.attributes = {:q => "What is your favorite color?", 'a' => "Blue"} @question.attributes = {:q => "What is your favorite color?", 'a' => "Blue"}
@question['q'].should == "What is your favorite color?" @question['q'].should == "What is your favorite color?"
@question.a.should == "Blue" @question.a.should == "Blue"
end end
it "should flip out if an attribute= method is missing" do it "should flip out if an attribute= method is missing" do
lambda { lambda {
@q.update_attributes_without_saving('foo' => "something", :a => "No green") @q.update_attributes_without_saving('foo' => "something", :a => "No green")
}.should raise_error(NoMethodError) }.should raise_error(NoMethodError)
end end
it "should not change any attributes if there is an error" do it "should not change any attributes if there is an error" do
lambda { lambda {
@q.update_attributes_without_saving('foo' => "something", :a => "No green") @q.update_attributes_without_saving('foo' => "something", :a => "No green")
@ -180,7 +180,7 @@ describe CouchRest::Model::CastedModel do
@question.a.should == "To seek the Holy Grail" @question.a.should == "To seek the Holy Grail"
end end
end end
describe "saved document with casted models" do describe "saved document with casted models" do
before(:each) do before(:each) do
reset_test_db! reset_test_db!
@ -188,24 +188,24 @@ describe CouchRest::Model::CastedModel do
@obj.save.should be_true @obj.save.should be_true
@obj = DummyModel.get(@obj.id) @obj = DummyModel.get(@obj.id)
end end
it "should be able to load with the casted models" do it "should be able to load with the casted models" do
casted_obj = @obj.casted_attribute casted_obj = @obj.casted_attribute
casted_obj.should_not be_nil casted_obj.should_not be_nil
casted_obj.should be_an_instance_of(WithCastedModelMixin) casted_obj.should be_an_instance_of(WithCastedModelMixin)
end end
it "should have defined getters for the casted model" do it "should have defined getters for the casted model" do
casted_obj = @obj.casted_attribute casted_obj = @obj.casted_attribute
casted_obj.name.should == "whatever" casted_obj.name.should == "whatever"
end end
it "should have defined setters for the casted model" do it "should have defined setters for the casted model" do
casted_obj = @obj.casted_attribute casted_obj = @obj.casted_attribute
casted_obj.name = "test" casted_obj.name = "test"
casted_obj.name.should == "test" casted_obj.name.should == "test"
end end
it "should retain an override of a casted model attribute's default" do it "should retain an override of a casted model attribute's default" do
casted_obj = @obj.casted_attribute casted_obj = @obj.casted_attribute
casted_obj.details['color'] = 'orange' casted_obj.details['color'] = 'orange'
@ -213,7 +213,7 @@ describe CouchRest::Model::CastedModel do
casted_obj = DummyModel.get(@obj.id).casted_attribute casted_obj = DummyModel.get(@obj.id).casted_attribute
casted_obj.details['color'].should == 'orange' casted_obj.details['color'].should == 'orange'
end end
end end
describe "saving document with array of casted models and validation" do describe "saving document with array of casted models and validation" do
@ -238,7 +238,7 @@ describe CouchRest::Model::CastedModel do
@cat.should_not be_valid @cat.should_not be_valid
@cat.save.should be_false @cat.save.should be_false
end end
it "should not fail if the casted model doesn't have validation" do it "should not fail if the casted model doesn't have validation" do
Cat.property :masters, [Person], :default => [] Cat.property :masters, [Person], :default => []
Cat.validates_presence_of :name Cat.validates_presence_of :name
@ -248,7 +248,7 @@ describe CouchRest::Model::CastedModel do
cat.should be_valid cat.should be_valid
end end
end end
describe "calling valid?" do describe "calling valid?" do
before :each do before :each do
@cat = Cat.new @cat = Cat.new
@ -259,7 +259,7 @@ describe CouchRest::Model::CastedModel do
@cat.toys << @toy2 @cat.toys << @toy2
@cat.toys << @toy3 @cat.toys << @toy3
end end
describe "on the top document" do describe "on the top document" do
it "should put errors on all invalid casted models" do it "should put errors on all invalid casted models" do
@cat.should_not be_valid @cat.should_not be_valid
@ -268,10 +268,10 @@ describe CouchRest::Model::CastedModel do
@toy2.errors.should_not be_empty @toy2.errors.should_not be_empty
@toy3.errors.should_not be_empty @toy3.errors.should_not be_empty
end end
it "should not put errors on valid casted models" do it "should not put errors on valid casted models" do
@toy1.name = "Feather" @toy1.name = "Feather"
@toy2.name = "Twine" @toy2.name = "Twine"
@cat.should_not be_valid @cat.should_not be_valid
@cat.errors.should_not be_empty @cat.errors.should_not be_empty
@toy1.errors.should be_empty @toy1.errors.should be_empty
@ -279,7 +279,7 @@ describe CouchRest::Model::CastedModel do
@toy3.errors.should_not be_empty @toy3.errors.should_not be_empty
end end
end end
describe "on a casted model property" do describe "on a casted model property" do
it "should only validate itself" do it "should only validate itself" do
@toy1.should_not be_valid @toy1.should_not be_valid
@ -289,7 +289,7 @@ describe CouchRest::Model::CastedModel do
@toy3.errors.should be_empty @toy3.errors.should be_empty
end end
end end
describe "on a casted model inside a casted collection" do describe "on a casted model inside a casted collection" do
it "should only validate itself" do it "should only validate itself" do
@toy2.should_not be_valid @toy2.should_not be_valid
@ -300,7 +300,7 @@ describe CouchRest::Model::CastedModel do
end end
end end
end end
describe "calling new? on a casted model" do describe "calling new? on a casted model" do
before :each do before :each do
reset_test_db! reset_test_db!
@ -309,18 +309,18 @@ describe CouchRest::Model::CastedModel do
@cat.favorite_toy = @favorite_toy @cat.favorite_toy = @favorite_toy
@cat.toys << CatToy.new(:name => 'Fuzzy Stick') @cat.toys << CatToy.new(:name => 'Fuzzy Stick')
end end
it "should be true on new" do it "should be true on new" do
CatToy.new.should be_new CatToy.new.should be_new
CatToy.new.new_record?.should be_true CatToy.new.new_record?.should be_true
end end
it "should be true after assignment" do it "should be true after assignment" do
@cat.should be_new @cat.should be_new
@cat.favorite_toy.should be_new @cat.favorite_toy.should be_new
@cat.toys.first.should be_new @cat.toys.first.should be_new
end end
it "should not be true after create or save" do it "should not be true after create or save" do
@cat.create @cat.create
@cat.save @cat.save
@ -328,14 +328,14 @@ describe CouchRest::Model::CastedModel do
@cat.toys.first.casted_by.should eql(@cat) @cat.toys.first.casted_by.should eql(@cat)
@cat.toys.first.should_not be_new @cat.toys.first.should_not be_new
end end
it "should not be true after get from the database" do it "should not be true after get from the database" do
@cat.save @cat.save
@cat = Cat.get(@cat.id) @cat = Cat.get(@cat.id)
@cat.favorite_toy.should_not be_new @cat.favorite_toy.should_not be_new
@cat.toys.first.should_not be_new @cat.toys.first.should_not be_new
end end
it "should still be true after a failed create or save" do it "should still be true after a failed create or save" do
@cat.name = nil @cat.name = nil
@cat.create.should be_false @cat.create.should be_false
@ -344,7 +344,7 @@ describe CouchRest::Model::CastedModel do
@cat.toys.first.should be_new @cat.toys.first.should be_new
end end
end end
describe "calling base_doc from a nested casted model" do describe "calling base_doc from a nested casted model" do
before :each do before :each do
@course = Course.new(:title => 'Science 101') @course = Course.new(:title => 'Science 101')
@ -357,7 +357,15 @@ describe CouchRest::Model::CastedModel do
@cat.favorite_toy = @toy1 @cat.favorite_toy = @toy1
@cat.toys << @toy2 @cat.toys << @toy2
end end
it 'should let you copy over casted arrays' do
question = Question.new
@course.questions << question
new_course = Course.new
new_course.questions = @course.questions
new_course.questions.should include(question)
end
it "should reference the top document for" do it "should reference the top document for" do
@course.base_doc.should === @course @course.base_doc.should === @course
@professor.casted_by.should === @course @professor.casted_by.should === @course
@ -366,19 +374,19 @@ describe CouchRest::Model::CastedModel do
@toy1.base_doc.should === @course @toy1.base_doc.should === @course
@toy2.base_doc.should === @course @toy2.base_doc.should === @course
end end
it "should call setter on top document" do it "should call setter on top document" do
@toy1.base_doc.should_not be_nil @toy1.base_doc.should_not be_nil
@toy1.base_doc.title = 'Tom Foolery' @toy1.base_doc.title = 'Tom Foolery'
@course.title.should == 'Tom Foolery' @course.title.should == 'Tom Foolery'
end end
it "should return nil if not yet casted" do it "should return nil if not yet casted" do
person = Person.new person = Person.new
person.base_doc.should == nil person.base_doc.should == nil
end end
end end
describe "calling base_doc.save from a nested casted model" do describe "calling base_doc.save from a nested casted model" do
before :each do before :each do
reset_test_db! reset_test_db!
@ -386,13 +394,13 @@ describe CouchRest::Model::CastedModel do
@toy = CatToy.new @toy = CatToy.new
@cat.favorite_toy = @toy @cat.favorite_toy = @toy
end end
it "should not save parent document when casted model is invalid" do it "should not save parent document when casted model is invalid" do
@toy.should_not be_valid @toy.should_not be_valid
@toy.base_doc.save.should be_false @toy.base_doc.save.should be_false
lambda{@toy.base_doc.save!}.should raise_error lambda{@toy.base_doc.save!}.should raise_error
end end
it "should save parent document when nested casted model is valid" do it "should save parent document when nested casted model is valid" do
@toy.name = "Mr Squeaks" @toy.name = "Mr Squeaks"
@toy.should be_valid @toy.should be_valid
@ -400,14 +408,14 @@ describe CouchRest::Model::CastedModel do
lambda{@toy.base_doc.save!}.should_not raise_error lambda{@toy.base_doc.save!}.should_not raise_error
end end
end end
describe "callbacks" do describe "callbacks" do
before(:each) do before(:each) do
@doc = CastedCallbackDoc.new @doc = CastedCallbackDoc.new
@model = WithCastedCallBackModel.new @model = WithCastedCallBackModel.new
@doc.callback_model = @model @doc.callback_model = @model
end end
describe "validate" do describe "validate" do
it "should run before_validate before validating" do it "should run before_validate before validating" do
@model.run_before_validate.should be_nil @model.run_before_validate.should be_nil
@ -418,7 +426,7 @@ describe CouchRest::Model::CastedModel do
@model.run_after_validate.should be_nil @model.run_after_validate.should be_nil
@model.should be_valid @model.should be_valid
@model.run_after_validate.should be_true @model.run_after_validate.should be_true
end end
end end
end end
end end