couchrest_model-radiant 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +176 -0
- data/README.md +19 -0
- data/Rakefile +74 -0
- data/THANKS.md +21 -0
- data/history.txt +207 -0
- data/lib/couchrest/model.rb +10 -0
- data/lib/couchrest/model/associations.rb +223 -0
- data/lib/couchrest/model/base.rb +111 -0
- data/lib/couchrest/model/callbacks.rb +27 -0
- data/lib/couchrest/model/casted_array.rb +39 -0
- data/lib/couchrest/model/casted_model.rb +68 -0
- data/lib/couchrest/model/class_proxy.rb +122 -0
- data/lib/couchrest/model/collection.rb +263 -0
- data/lib/couchrest/model/configuration.rb +51 -0
- data/lib/couchrest/model/design_doc.rb +123 -0
- data/lib/couchrest/model/document_queries.rb +83 -0
- data/lib/couchrest/model/errors.rb +23 -0
- data/lib/couchrest/model/extended_attachments.rb +77 -0
- data/lib/couchrest/model/persistence.rb +155 -0
- data/lib/couchrest/model/properties.rb +208 -0
- data/lib/couchrest/model/property.rb +97 -0
- data/lib/couchrest/model/property_protection.rb +71 -0
- data/lib/couchrest/model/support/couchrest.rb +19 -0
- data/lib/couchrest/model/support/hash.rb +9 -0
- data/lib/couchrest/model/typecast.rb +175 -0
- data/lib/couchrest/model/validations.rb +68 -0
- data/lib/couchrest/model/validations/casted_model.rb +14 -0
- data/lib/couchrest/model/validations/locale/en.yml +5 -0
- data/lib/couchrest/model/validations/uniqueness.rb +44 -0
- data/lib/couchrest/model/views.rb +160 -0
- data/lib/couchrest/railtie.rb +12 -0
- data/lib/couchrest_model.rb +62 -0
- data/lib/rails/generators/couchrest_model.rb +16 -0
- data/lib/rails/generators/couchrest_model/model/model_generator.rb +27 -0
- data/lib/rails/generators/couchrest_model/model/templates/model.rb +2 -0
- data/spec/couchrest/assocations_spec.rb +196 -0
- data/spec/couchrest/attachment_spec.rb +176 -0
- data/spec/couchrest/base_spec.rb +463 -0
- data/spec/couchrest/casted_model_spec.rb +438 -0
- data/spec/couchrest/casted_spec.rb +75 -0
- data/spec/couchrest/class_proxy_spec.rb +132 -0
- data/spec/couchrest/configuration_spec.rb +78 -0
- data/spec/couchrest/inherited_spec.rb +40 -0
- data/spec/couchrest/persistence_spec.rb +415 -0
- data/spec/couchrest/property_protection_spec.rb +192 -0
- data/spec/couchrest/property_spec.rb +871 -0
- data/spec/couchrest/subclass_spec.rb +99 -0
- data/spec/couchrest/validations.rb +85 -0
- data/spec/couchrest/view_spec.rb +463 -0
- data/spec/fixtures/attachments/README +3 -0
- data/spec/fixtures/attachments/couchdb.png +0 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/base.rb +139 -0
- data/spec/fixtures/more/article.rb +35 -0
- data/spec/fixtures/more/card.rb +17 -0
- data/spec/fixtures/more/cat.rb +19 -0
- data/spec/fixtures/more/client.rb +6 -0
- data/spec/fixtures/more/course.rb +25 -0
- data/spec/fixtures/more/event.rb +8 -0
- data/spec/fixtures/more/invoice.rb +14 -0
- data/spec/fixtures/more/person.rb +9 -0
- data/spec/fixtures/more/question.rb +7 -0
- data/spec/fixtures/more/sale_entry.rb +9 -0
- data/spec/fixtures/more/sale_invoice.rb +13 -0
- data/spec/fixtures/more/service.rb +10 -0
- data/spec/fixtures/more/user.rb +22 -0
- data/spec/fixtures/views/lib.js +3 -0
- data/spec/fixtures/views/test_view/lib.js +3 -0
- data/spec/fixtures/views/test_view/only-map.js +4 -0
- data/spec/fixtures/views/test_view/test-map.js +3 -0
- data/spec/fixtures/views/test_view/test-reduce.js +3 -0
- data/spec/spec_helper.rb +48 -0
- metadata +263 -0
@@ -0,0 +1,192 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe "Model Attributes" do
|
4
|
+
|
5
|
+
describe "no declarations" do
|
6
|
+
class NoProtection < CouchRest::Model::Base
|
7
|
+
use_database TEST_SERVER.default_database
|
8
|
+
property :name
|
9
|
+
property :phone
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not protect anything through new" do
|
13
|
+
user = NoProtection.new(:name => "will", :phone => "555-5555")
|
14
|
+
|
15
|
+
user.name.should == "will"
|
16
|
+
user.phone.should == "555-5555"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should not protect anything through attributes=" do
|
20
|
+
user = NoProtection.new
|
21
|
+
user.attributes = {:name => "will", :phone => "555-5555"}
|
22
|
+
|
23
|
+
user.name.should == "will"
|
24
|
+
user.phone.should == "555-5555"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should recreate from the database properly" do
|
28
|
+
user = NoProtection.new
|
29
|
+
user.name = "will"
|
30
|
+
user.phone = "555-5555"
|
31
|
+
user.save!
|
32
|
+
|
33
|
+
user = NoProtection.get(user.id)
|
34
|
+
user.name.should == "will"
|
35
|
+
user.phone.should == "555-5555"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should provide a list of all properties as accessible" do
|
39
|
+
user = NoProtection.new(:name => "will", :phone => "555-5555")
|
40
|
+
user.accessible_properties.length.should eql(2)
|
41
|
+
user.protected_properties.should be_empty
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "Model Base", "accessible flag" do
|
46
|
+
class WithAccessible < CouchRest::Model::Base
|
47
|
+
use_database TEST_SERVER.default_database
|
48
|
+
property :name, :accessible => true
|
49
|
+
property :admin, :default => false
|
50
|
+
end
|
51
|
+
|
52
|
+
it { expect { WithAccessible.new(nil) }.to_not raise_error }
|
53
|
+
|
54
|
+
it "should recognize accessible properties" do
|
55
|
+
props = WithAccessible.accessible_properties.map { |prop| prop.name}
|
56
|
+
props.should include("name")
|
57
|
+
props.should_not include("admin")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should protect non-accessible properties set through new" do
|
61
|
+
user = WithAccessible.new(:name => "will", :admin => true)
|
62
|
+
|
63
|
+
user.name.should == "will"
|
64
|
+
user.admin.should == false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should protect non-accessible properties set through attributes=" do
|
68
|
+
user = WithAccessible.new
|
69
|
+
user.attributes = {:name => "will", :admin => true}
|
70
|
+
|
71
|
+
user.name.should == "will"
|
72
|
+
user.admin.should == false
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should provide correct accessible and protected property lists" do
|
76
|
+
user = WithAccessible.new(:name => 'will', :admin => true)
|
77
|
+
user.accessible_properties.map{|p| p.to_s}.should eql(['name'])
|
78
|
+
user.protected_properties.map{|p| p.to_s}.should eql(['admin'])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "Model Base", "protected flag" do
|
83
|
+
class WithProtected < CouchRest::Model::Base
|
84
|
+
use_database TEST_SERVER.default_database
|
85
|
+
property :name
|
86
|
+
property :admin, :default => false, :protected => true
|
87
|
+
end
|
88
|
+
|
89
|
+
it { expect { WithProtected.new(nil) }.to_not raise_error }
|
90
|
+
|
91
|
+
it "should recognize protected properties" do
|
92
|
+
props = WithProtected.protected_properties.map { |prop| prop.name}
|
93
|
+
props.should_not include("name")
|
94
|
+
props.should include("admin")
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should protect non-accessible properties set through new" do
|
98
|
+
user = WithProtected.new(:name => "will", :admin => true)
|
99
|
+
|
100
|
+
user.name.should == "will"
|
101
|
+
user.admin.should == false
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should protect non-accessible properties set through attributes=" do
|
105
|
+
user = WithProtected.new
|
106
|
+
user.attributes = {:name => "will", :admin => true}
|
107
|
+
|
108
|
+
user.name.should == "will"
|
109
|
+
user.admin.should == false
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should not modify the provided attribute hash" do
|
113
|
+
user = WithProtected.new
|
114
|
+
attrs = {:name => "will", :admin => true}
|
115
|
+
user.attributes = attrs
|
116
|
+
attrs[:admin].should be_true
|
117
|
+
attrs[:name].should eql('will')
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should provide correct accessible and protected property lists" do
|
121
|
+
user = WithProtected.new(:name => 'will', :admin => true)
|
122
|
+
user.accessible_properties.map{|p| p.to_s}.should eql(['name'])
|
123
|
+
user.protected_properties.map{|p| p.to_s}.should eql(['admin'])
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "Model Base", "mixing protected and accessible flags" do
|
129
|
+
class WithBothAndUnspecified < CouchRest::Model::Base
|
130
|
+
use_database TEST_SERVER.default_database
|
131
|
+
property :name, :accessible => true
|
132
|
+
property :admin, :default => false, :protected => true
|
133
|
+
property :phone, :default => 'unset phone number'
|
134
|
+
end
|
135
|
+
|
136
|
+
it { expect { WithBothAndUnspecified.new }.to_not raise_error }
|
137
|
+
|
138
|
+
it 'should assume that any unspecified property is protected by default' do
|
139
|
+
user = WithBothAndUnspecified.new(:name => 'will', :admin => true, :phone => '555-1234')
|
140
|
+
|
141
|
+
user.name.should == 'will'
|
142
|
+
user.admin.should == false
|
143
|
+
user.phone.should == 'unset phone number'
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "from database" do
|
149
|
+
class WithProtected < CouchRest::Model::Base
|
150
|
+
use_database TEST_SERVER.default_database
|
151
|
+
property :name
|
152
|
+
property :admin, :default => false, :protected => true
|
153
|
+
view_by :name
|
154
|
+
end
|
155
|
+
|
156
|
+
before(:each) do
|
157
|
+
@user = WithProtected.new
|
158
|
+
@user.name = "will"
|
159
|
+
@user.admin = true
|
160
|
+
@user.save!
|
161
|
+
end
|
162
|
+
|
163
|
+
def verify_attrs(user)
|
164
|
+
user.name.should == "will"
|
165
|
+
user.admin.should == true
|
166
|
+
end
|
167
|
+
|
168
|
+
it "Base#get should not strip protected attributes" do
|
169
|
+
reloaded = WithProtected.get( @user.id )
|
170
|
+
verify_attrs reloaded
|
171
|
+
end
|
172
|
+
|
173
|
+
it "Base#get! should not strip protected attributes" do
|
174
|
+
reloaded = WithProtected.get!( @user.id )
|
175
|
+
verify_attrs reloaded
|
176
|
+
end
|
177
|
+
|
178
|
+
it "Base#all should not strip protected attributes" do
|
179
|
+
# all creates a CollectionProxy
|
180
|
+
docs = WithProtected.all(:key => @user.id)
|
181
|
+
docs.length.should == 1
|
182
|
+
reloaded = docs.first
|
183
|
+
verify_attrs reloaded
|
184
|
+
end
|
185
|
+
|
186
|
+
it "views should not strip protected attributes" do
|
187
|
+
docs = WithProtected.by_name(:startkey => "will", :endkey => "will")
|
188
|
+
reloaded = docs.first
|
189
|
+
verify_attrs reloaded
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,871 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
3
|
+
require File.join(FIXTURE_PATH, 'more', 'cat')
|
4
|
+
require File.join(FIXTURE_PATH, 'more', 'person')
|
5
|
+
require File.join(FIXTURE_PATH, 'more', 'card')
|
6
|
+
require File.join(FIXTURE_PATH, 'more', 'invoice')
|
7
|
+
require File.join(FIXTURE_PATH, 'more', 'service')
|
8
|
+
require File.join(FIXTURE_PATH, 'more', 'event')
|
9
|
+
require File.join(FIXTURE_PATH, 'more', 'user')
|
10
|
+
require File.join(FIXTURE_PATH, 'more', 'course')
|
11
|
+
|
12
|
+
|
13
|
+
describe "Model properties" do
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
reset_test_db!
|
17
|
+
@card = Card.new(:first_name => "matt")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be accessible from the object" do
|
21
|
+
@card.properties.should be_an_instance_of(Array)
|
22
|
+
@card.properties.map{|p| p.name}.should include("first_name")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should let you access a property value (getter)" do
|
26
|
+
@card.first_name.should == "matt"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should let you set a property value (setter)" do
|
30
|
+
@card.last_name = "Aimonetti"
|
31
|
+
@card.last_name.should == "Aimonetti"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not let you set a property value if it's read only" do
|
35
|
+
lambda{@card.read_only_value = "test"}.should raise_error
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should let you use an alias for an attribute" do
|
39
|
+
@card.last_name = "Aimonetti"
|
40
|
+
@card.family_name.should == "Aimonetti"
|
41
|
+
@card.family_name.should == @card.last_name
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should let you use an alias for a casted attribute" do
|
45
|
+
@card.cast_alias = Person.new(:name => ["Aimonetti"])
|
46
|
+
@card.cast_alias.name.should == ["Aimonetti"]
|
47
|
+
@card.calias.name.should == ["Aimonetti"]
|
48
|
+
card = Card.new(:first_name => "matt", :cast_alias => {:name => ["Aimonetti"]})
|
49
|
+
card.cast_alias.name.should == ["Aimonetti"]
|
50
|
+
card.calias.name.should == ["Aimonetti"]
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
it "should be auto timestamped" do
|
55
|
+
@card.created_at.should be_nil
|
56
|
+
@card.updated_at.should be_nil
|
57
|
+
@card.save.should be_true
|
58
|
+
@card.created_at.should_not be_nil
|
59
|
+
@card.updated_at.should_not be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#read_attribute' do
|
63
|
+
it "should let you use read_attribute method" do
|
64
|
+
@card.last_name = "Aimonetti"
|
65
|
+
@card.read_attribute(:last_name).should eql('Aimonetti')
|
66
|
+
@card.read_attribute('last_name').should eql('Aimonetti')
|
67
|
+
last_name_prop = @card.properties.find{|p| p.name == 'last_name'}
|
68
|
+
@card.read_attribute(last_name_prop).should eql('Aimonetti')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should raise an error if the property does not exist' do
|
72
|
+
expect { @card.read_attribute(:this_property_should_not_exist) }.to raise_error(ArgumentError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#write_attribute' do
|
77
|
+
it "should let you use write_attribute method" do
|
78
|
+
@card.write_attribute(:last_name, 'Aimonetti 1')
|
79
|
+
@card.last_name.should eql('Aimonetti 1')
|
80
|
+
@card.write_attribute('last_name', 'Aimonetti 2')
|
81
|
+
@card.last_name.should eql('Aimonetti 2')
|
82
|
+
last_name_prop = @card.properties.find{|p| p.name == 'last_name'}
|
83
|
+
@card.write_attribute(last_name_prop, 'Aimonetti 3')
|
84
|
+
@card.last_name.should eql('Aimonetti 3')
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should raise an error if the property does not exist' do
|
88
|
+
expect { @card.write_attribute(:this_property_should_not_exist, 823) }.to raise_error(ArgumentError)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
it "should let you use write_attribute on readonly properties" do
|
93
|
+
lambda {
|
94
|
+
@card.read_only_value = "foo"
|
95
|
+
}.should raise_error
|
96
|
+
@card.write_attribute(:read_only_value, "foo")
|
97
|
+
@card.read_only_value.should == 'foo'
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should cast via write_attribute" do
|
101
|
+
@card.write_attribute(:cast_alias, {:name => ["Sam", "Lown"]})
|
102
|
+
@card.cast_alias.class.should eql(Person)
|
103
|
+
@card.cast_alias.name.last.should eql("Lown")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should not cast via write_attribute if property not casted" do
|
107
|
+
@card.write_attribute(:first_name, {:name => "Sam"})
|
108
|
+
@card.first_name.class.should eql(Hash)
|
109
|
+
@card.first_name[:name].should eql("Sam")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "mass updating attributes without property" do
|
114
|
+
|
115
|
+
describe "when mass_assign_any_attribute false" do
|
116
|
+
|
117
|
+
it "should not allow them to be set" do
|
118
|
+
@card.attributes = {:test => 'fooobar'}
|
119
|
+
@card['test'].should be_nil
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "when mass_assign_any_attribute true" do
|
125
|
+
before(:each) do
|
126
|
+
# dup Card class so that no other tests are effected
|
127
|
+
card_class = Card.dup
|
128
|
+
card_class.class_eval do
|
129
|
+
mass_assign_any_attribute true
|
130
|
+
end
|
131
|
+
@card = card_class.new(:first_name => 'Sam')
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should allow them to be updated' do
|
135
|
+
@card.attributes = {:test => 'fooobar'}
|
136
|
+
@card['test'].should eql('fooobar')
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
describe "mass assignment protection" do
|
143
|
+
|
144
|
+
it "should not store protected attribute using mass assignment" do
|
145
|
+
cat_toy = CatToy.new(:name => "Zorro")
|
146
|
+
cat = Cat.create(:name => "Helena", :toys => [cat_toy], :favorite_toy => cat_toy, :number => 1)
|
147
|
+
cat.number.should be_nil
|
148
|
+
cat.number = 1
|
149
|
+
cat.save
|
150
|
+
cat.number.should == 1
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should not store protected attribute when 'declare accessible poperties, assume all the rest are protected'" do
|
154
|
+
user = User.create(:name => "Marcos Tapajós", :admin => true)
|
155
|
+
user.admin.should be_nil
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should not store protected attribute when 'declare protected properties, assume all the rest are accessible'" do
|
159
|
+
user = SpecialUser.create(:name => "Marcos Tapajós", :admin => true)
|
160
|
+
user.admin.should be_nil
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "validation" do
|
166
|
+
before(:each) do
|
167
|
+
@invoice = Invoice.new(:client_name => "matt", :employee_name => "Chris", :location => "San Diego, CA")
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should be able to be validated" do
|
171
|
+
@card.valid?.should == true
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should let you validate the presence of an attribute" do
|
175
|
+
@card.first_name = nil
|
176
|
+
@card.should_not be_valid
|
177
|
+
@card.errors.should_not be_empty
|
178
|
+
@card.errors[:first_name].should == ["can't be blank"]
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should let you look up errors for a field by a string name" do
|
182
|
+
@card.first_name = nil
|
183
|
+
@card.should_not be_valid
|
184
|
+
@card.errors['first_name'].should == ["can't be blank"]
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should validate the presence of 2 attributes" do
|
188
|
+
@invoice.clear
|
189
|
+
@invoice.should_not be_valid
|
190
|
+
@invoice.errors.should_not be_empty
|
191
|
+
@invoice.errors[:client_name].should == ["can't be blank"]
|
192
|
+
@invoice.errors[:employee_name].should_not be_empty
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should let you set an error message" do
|
196
|
+
@invoice.location = nil
|
197
|
+
@invoice.valid?
|
198
|
+
@invoice.errors[:location].should == ["Hey stupid!, you forgot the location"]
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should validate before saving" do
|
202
|
+
@invoice.location = nil
|
203
|
+
@invoice.should_not be_valid
|
204
|
+
@invoice.save.should be_false
|
205
|
+
@invoice.should be_new
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "casting" do
|
210
|
+
before(:each) do
|
211
|
+
@course = Course.new(:title => 'Relaxation')
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "when value is nil" do
|
215
|
+
it "leaves the value unchanged" do
|
216
|
+
@course.title = nil
|
217
|
+
@course['title'].should == nil
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe "when type primitive is an Object" do
|
222
|
+
it "it should not cast given value" do
|
223
|
+
@course.participants = [{}, 'q', 1]
|
224
|
+
@course['participants'].should == [{}, 'q', 1]
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should cast started_on to Date" do
|
228
|
+
@course.started_on = Date.today
|
229
|
+
@course['started_on'].should be_an_instance_of(Date)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "when type primitive is a String" do
|
234
|
+
it "keeps string value unchanged" do
|
235
|
+
value = "1.0"
|
236
|
+
@course.title = value
|
237
|
+
@course['title'].should equal(value)
|
238
|
+
end
|
239
|
+
|
240
|
+
it "it casts to string representation of the value" do
|
241
|
+
@course.title = 1.0
|
242
|
+
@course['title'].should eql("1.0")
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe 'when type primitive is a Float' do
|
247
|
+
it 'returns same value if a float' do
|
248
|
+
value = 24.0
|
249
|
+
@course.estimate = value
|
250
|
+
@course['estimate'].should equal(value)
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'returns float representation of a zero string integer' do
|
254
|
+
@course.estimate = '0'
|
255
|
+
@course['estimate'].should eql(0.0)
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'returns float representation of a positive string integer' do
|
259
|
+
@course.estimate = '24'
|
260
|
+
@course['estimate'].should eql(24.0)
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'returns float representation of a negative string integer' do
|
264
|
+
@course.estimate = '-24'
|
265
|
+
@course['estimate'].should eql(-24.0)
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'returns float representation of a zero string float' do
|
269
|
+
@course.estimate = '0.0'
|
270
|
+
@course['estimate'].should eql(0.0)
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'returns float representation of a positive string float' do
|
274
|
+
@course.estimate = '24.35'
|
275
|
+
@course['estimate'].should eql(24.35)
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'returns float representation of a negative string float' do
|
279
|
+
@course.estimate = '-24.35'
|
280
|
+
@course['estimate'].should eql(-24.35)
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'returns float representation of a zero string float, with no leading digits' do
|
284
|
+
@course.estimate = '.0'
|
285
|
+
@course['estimate'].should eql(0.0)
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'returns float representation of a positive string float, with no leading digits' do
|
289
|
+
@course.estimate = '.41'
|
290
|
+
@course['estimate'].should eql(0.41)
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'returns float representation of a zero integer' do
|
294
|
+
@course.estimate = 0
|
295
|
+
@course['estimate'].should eql(0.0)
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'returns float representation of a positive integer' do
|
299
|
+
@course.estimate = 24
|
300
|
+
@course['estimate'].should eql(24.0)
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'returns float representation of a negative integer' do
|
304
|
+
@course.estimate = -24
|
305
|
+
@course['estimate'].should eql(-24.0)
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'returns float representation of a zero decimal' do
|
309
|
+
@course.estimate = BigDecimal('0.0')
|
310
|
+
@course['estimate'].should eql(0.0)
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'returns float representation of a positive decimal' do
|
314
|
+
@course.estimate = BigDecimal('24.35')
|
315
|
+
@course['estimate'].should eql(24.35)
|
316
|
+
end
|
317
|
+
|
318
|
+
it 'returns float representation of a negative decimal' do
|
319
|
+
@course.estimate = BigDecimal('-24.35')
|
320
|
+
@course['estimate'].should eql(-24.35)
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'return float of a number with commas instead of points for decimals' do
|
324
|
+
@course.estimate = '23,35'
|
325
|
+
@course['estimate'].should eql(23.35)
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should handle numbers with commas and points" do
|
329
|
+
@course.estimate = '1,234.00'
|
330
|
+
@course.estimate.should eql(1234.00)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should handle a mis-match of commas and points and maintain the last one" do
|
334
|
+
@course.estimate = "1,232.434.123,323"
|
335
|
+
@course.estimate.should eql(1232434123.323)
|
336
|
+
end
|
337
|
+
|
338
|
+
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
|
339
|
+
it "does not typecast non-numeric value #{value.inspect}" do
|
340
|
+
@course.estimate = value
|
341
|
+
@course['estimate'].should equal(value)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
346
|
+
|
347
|
+
describe 'when type primitive is a Integer' do
|
348
|
+
it 'returns same value if an integer' do
|
349
|
+
value = 24
|
350
|
+
@course.hours = value
|
351
|
+
@course['hours'].should equal(value)
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'returns integer representation of a zero string integer' do
|
355
|
+
@course.hours = '0'
|
356
|
+
@course['hours'].should eql(0)
|
357
|
+
end
|
358
|
+
|
359
|
+
it 'returns integer representation of a positive string integer' do
|
360
|
+
@course.hours = '24'
|
361
|
+
@course['hours'].should eql(24)
|
362
|
+
end
|
363
|
+
|
364
|
+
it 'returns integer representation of a negative string integer' do
|
365
|
+
@course.hours = '-24'
|
366
|
+
@course['hours'].should eql(-24)
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'returns integer representation of a zero string float' do
|
370
|
+
@course.hours = '0.0'
|
371
|
+
@course['hours'].should eql(0)
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'returns integer representation of a positive string float' do
|
375
|
+
@course.hours = '24.35'
|
376
|
+
@course['hours'].should eql(24)
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'returns integer representation of a negative string float' do
|
380
|
+
@course.hours = '-24.35'
|
381
|
+
@course['hours'].should eql(-24)
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'returns integer representation of a zero string float, with no leading digits' do
|
385
|
+
@course.hours = '.0'
|
386
|
+
@course['hours'].should eql(0)
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'returns integer representation of a positive string float, with no leading digits' do
|
390
|
+
@course.hours = '.41'
|
391
|
+
@course['hours'].should eql(0)
|
392
|
+
end
|
393
|
+
|
394
|
+
it 'returns integer representation of a zero float' do
|
395
|
+
@course.hours = 0.0
|
396
|
+
@course['hours'].should eql(0)
|
397
|
+
end
|
398
|
+
|
399
|
+
it 'returns integer representation of a positive float' do
|
400
|
+
@course.hours = 24.35
|
401
|
+
@course['hours'].should eql(24)
|
402
|
+
end
|
403
|
+
|
404
|
+
it 'returns integer representation of a negative float' do
|
405
|
+
@course.hours = -24.35
|
406
|
+
@course['hours'].should eql(-24)
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'returns integer representation of a zero decimal' do
|
410
|
+
@course.hours = '0.0'
|
411
|
+
@course['hours'].should eql(0)
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'returns integer representation of a positive decimal' do
|
415
|
+
@course.hours = '24.35'
|
416
|
+
@course['hours'].should eql(24)
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'returns integer representation of a negative decimal' do
|
420
|
+
@course.hours = '-24.35'
|
421
|
+
@course['hours'].should eql(-24)
|
422
|
+
end
|
423
|
+
|
424
|
+
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
|
425
|
+
it "does not typecast non-numeric value #{value.inspect}" do
|
426
|
+
@course.hours = value
|
427
|
+
@course['hours'].should equal(value)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
describe 'when type primitive is a BigDecimal' do
|
433
|
+
it 'returns same value if a decimal' do
|
434
|
+
value = BigDecimal('24.0')
|
435
|
+
@course.profit = value
|
436
|
+
@course['profit'].should equal(value)
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'returns decimal representation of a zero string integer' do
|
440
|
+
@course.profit = '0'
|
441
|
+
@course['profit'].should eql(BigDecimal('0.0'))
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'returns decimal representation of a positive string integer' do
|
445
|
+
@course.profit = '24'
|
446
|
+
@course['profit'].should eql(BigDecimal('24.0'))
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'returns decimal representation of a negative string integer' do
|
450
|
+
@course.profit = '-24'
|
451
|
+
@course['profit'].should eql(BigDecimal('-24.0'))
|
452
|
+
end
|
453
|
+
|
454
|
+
it 'returns decimal representation of a zero string float' do
|
455
|
+
@course.profit = '0.0'
|
456
|
+
@course['profit'].should eql(BigDecimal('0.0'))
|
457
|
+
end
|
458
|
+
|
459
|
+
it 'returns decimal representation of a positive string float' do
|
460
|
+
@course.profit = '24.35'
|
461
|
+
@course['profit'].should eql(BigDecimal('24.35'))
|
462
|
+
end
|
463
|
+
|
464
|
+
it 'returns decimal representation of a negative string float' do
|
465
|
+
@course.profit = '-24.35'
|
466
|
+
@course['profit'].should eql(BigDecimal('-24.35'))
|
467
|
+
end
|
468
|
+
|
469
|
+
it 'returns decimal representation of a zero string float, with no leading digits' do
|
470
|
+
@course.profit = '.0'
|
471
|
+
@course['profit'].should eql(BigDecimal('0.0'))
|
472
|
+
end
|
473
|
+
|
474
|
+
it 'returns decimal representation of a positive string float, with no leading digits' do
|
475
|
+
@course.profit = '.41'
|
476
|
+
@course['profit'].should eql(BigDecimal('0.41'))
|
477
|
+
end
|
478
|
+
|
479
|
+
it 'returns decimal representation of a zero integer' do
|
480
|
+
@course.profit = 0
|
481
|
+
@course['profit'].should eql(BigDecimal('0.0'))
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'returns decimal representation of a positive integer' do
|
485
|
+
@course.profit = 24
|
486
|
+
@course['profit'].should eql(BigDecimal('24.0'))
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'returns decimal representation of a negative integer' do
|
490
|
+
@course.profit = -24
|
491
|
+
@course['profit'].should eql(BigDecimal('-24.0'))
|
492
|
+
end
|
493
|
+
|
494
|
+
it 'returns decimal representation of a zero float' do
|
495
|
+
@course.profit = 0.0
|
496
|
+
@course['profit'].should eql(BigDecimal('0.0'))
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'returns decimal representation of a positive float' do
|
500
|
+
@course.profit = 24.35
|
501
|
+
@course['profit'].should eql(BigDecimal('24.35'))
|
502
|
+
end
|
503
|
+
|
504
|
+
it 'returns decimal representation of a negative float' do
|
505
|
+
@course.profit = -24.35
|
506
|
+
@course['profit'].should eql(BigDecimal('-24.35'))
|
507
|
+
end
|
508
|
+
|
509
|
+
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
|
510
|
+
it "does not typecast non-numeric value #{value.inspect}" do
|
511
|
+
@course.profit = value
|
512
|
+
@course['profit'].should equal(value)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
describe 'when type primitive is a DateTime' do
|
518
|
+
describe 'and value given as a hash with keys like :year, :month, etc' do
|
519
|
+
it 'builds a DateTime instance from hash values' do
|
520
|
+
@course.updated_at = {
|
521
|
+
:year => '2006',
|
522
|
+
:month => '11',
|
523
|
+
:day => '23',
|
524
|
+
:hour => '12',
|
525
|
+
:min => '0',
|
526
|
+
:sec => '0'
|
527
|
+
}
|
528
|
+
result = @course['updated_at']
|
529
|
+
|
530
|
+
result.should be_kind_of(DateTime)
|
531
|
+
result.year.should eql(2006)
|
532
|
+
result.month.should eql(11)
|
533
|
+
result.day.should eql(23)
|
534
|
+
result.hour.should eql(12)
|
535
|
+
result.min.should eql(0)
|
536
|
+
result.sec.should eql(0)
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
describe 'and value is a string' do
|
541
|
+
it 'parses the string' do
|
542
|
+
@course.updated_at = 'Dec, 2006'
|
543
|
+
@course['updated_at'].month.should == 12
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
it 'does not typecast non-datetime values' do
|
548
|
+
@course.updated_at = 'not-datetime'
|
549
|
+
@course['updated_at'].should eql('not-datetime')
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
describe 'when type primitive is a Date' do
|
554
|
+
describe 'and value given as a hash with keys like :year, :month, etc' do
|
555
|
+
it 'builds a Date instance from hash values' do
|
556
|
+
@course.started_on = {
|
557
|
+
:year => '2007',
|
558
|
+
:month => '3',
|
559
|
+
:day => '25'
|
560
|
+
}
|
561
|
+
result = @course['started_on']
|
562
|
+
|
563
|
+
result.should be_kind_of(Date)
|
564
|
+
result.year.should eql(2007)
|
565
|
+
result.month.should eql(3)
|
566
|
+
result.day.should eql(25)
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
describe 'and value is a string' do
|
571
|
+
it 'parses the string' do
|
572
|
+
@course.started_on = 'Dec 20th, 2006'
|
573
|
+
@course.started_on.month.should == 12
|
574
|
+
@course.started_on.day.should == 20
|
575
|
+
@course.started_on.year.should == 2006
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
it 'does not typecast non-date values' do
|
580
|
+
@course.started_on = 'not-date'
|
581
|
+
@course['started_on'].should eql('not-date')
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
describe 'when type primitive is a Time' do
|
586
|
+
describe 'and value given as a hash with keys like :year, :month, etc' do
|
587
|
+
it 'builds a Time instance from hash values' do
|
588
|
+
@course.ends_at = {
|
589
|
+
:year => '2006',
|
590
|
+
:month => '11',
|
591
|
+
:day => '23',
|
592
|
+
:hour => '12',
|
593
|
+
:min => '0',
|
594
|
+
:sec => '0'
|
595
|
+
}
|
596
|
+
result = @course['ends_at']
|
597
|
+
|
598
|
+
result.should be_kind_of(Time)
|
599
|
+
result.year.should eql(2006)
|
600
|
+
result.month.should eql(11)
|
601
|
+
result.day.should eql(23)
|
602
|
+
result.hour.should eql(12)
|
603
|
+
result.min.should eql(0)
|
604
|
+
result.sec.should eql(0)
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
describe 'and value is a string' do
|
609
|
+
it 'parses the string' do
|
610
|
+
t = Time.now
|
611
|
+
@course.ends_at = t.strftime('%Y/%m/%d %H:%M:%S %z')
|
612
|
+
@course['ends_at'].year.should eql(t.year)
|
613
|
+
@course['ends_at'].month.should eql(t.month)
|
614
|
+
@course['ends_at'].day.should eql(t.day)
|
615
|
+
@course['ends_at'].hour.should eql(t.hour)
|
616
|
+
@course['ends_at'].min.should eql(t.min)
|
617
|
+
@course['ends_at'].sec.should eql(t.sec)
|
618
|
+
end
|
619
|
+
it 'parses the string without offset' do
|
620
|
+
t = Time.now
|
621
|
+
@course.ends_at = t.strftime("%Y-%m-%d %H:%M:%S")
|
622
|
+
@course['ends_at'].year.should eql(t.year)
|
623
|
+
@course['ends_at'].month.should eql(t.month)
|
624
|
+
@course['ends_at'].day.should eql(t.day)
|
625
|
+
@course['ends_at'].hour.should eql(t.hour)
|
626
|
+
@course['ends_at'].min.should eql(t.min)
|
627
|
+
@course['ends_at'].sec.should eql(t.sec)
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
it 'does not typecast non-time values' do
|
632
|
+
@course.ends_at = 'not-time'
|
633
|
+
@course['ends_at'].should eql('not-time')
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
describe 'when type primitive is a Class' do
|
638
|
+
it 'returns same value if a class' do
|
639
|
+
value = Course
|
640
|
+
@course.klass = value
|
641
|
+
@course['klass'].should equal(value)
|
642
|
+
end
|
643
|
+
|
644
|
+
it 'returns the class if found' do
|
645
|
+
@course.klass = 'Course'
|
646
|
+
@course['klass'].should eql(Course)
|
647
|
+
end
|
648
|
+
|
649
|
+
it 'does not typecast non-class values' do
|
650
|
+
@course.klass = 'NoClass'
|
651
|
+
@course['klass'].should eql('NoClass')
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
describe 'when type primitive is a Boolean' do
|
656
|
+
|
657
|
+
[ true, 'true', 'TRUE', '1', 1, 't', 'T' ].each do |value|
|
658
|
+
it "returns true when value is #{value.inspect}" do
|
659
|
+
@course.active = value
|
660
|
+
@course['active'].should be_true
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
[ false, 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
|
665
|
+
it "returns false when value is #{value.inspect}" do
|
666
|
+
@course.active = value
|
667
|
+
@course['active'].should be_false
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
[ 'string', 2, 1.0, BigDecimal('1.0'), DateTime.now, Time.now, Date.today, Class, Object.new, ].each do |value|
|
672
|
+
it "does not typecast value #{value.inspect}" do
|
673
|
+
@course.active = value
|
674
|
+
@course['active'].should equal(value)
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
it "should respond to requests with ? modifier" do
|
679
|
+
@course.active = nil
|
680
|
+
@course.active?.should be_false
|
681
|
+
@course.active = false
|
682
|
+
@course.active?.should be_false
|
683
|
+
@course.active = true
|
684
|
+
@course.active?.should be_true
|
685
|
+
end
|
686
|
+
|
687
|
+
it "should respond to requests with ? modifier on TrueClass" do
|
688
|
+
@course.very_active = nil
|
689
|
+
@course.very_active?.should be_false
|
690
|
+
@course.very_active = false
|
691
|
+
@course.very_active?.should be_false
|
692
|
+
@course.very_active = true
|
693
|
+
@course.very_active?.should be_true
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
describe "properties of array of casted models" do
|
701
|
+
|
702
|
+
before(:each) do
|
703
|
+
@course = Course.new :title => 'Test Course'
|
704
|
+
end
|
705
|
+
|
706
|
+
it "should allow attribute to be set from an array of objects" do
|
707
|
+
@course.questions = [Question.new(:q => "works?"), Question.new(:q => "Meaning of Life?")]
|
708
|
+
@course.questions.length.should eql(2)
|
709
|
+
end
|
710
|
+
|
711
|
+
it "should allow attribute to be set from an array of hashes" do
|
712
|
+
@course.questions = [{:q => "works?"}, {:q => "Meaning of Life?"}]
|
713
|
+
@course.questions.length.should eql(2)
|
714
|
+
@course.questions.last.q.should eql("Meaning of Life?")
|
715
|
+
@course.questions.last.class.should eql(Question) # typecasting
|
716
|
+
end
|
717
|
+
|
718
|
+
it "should allow attribute to be set from hash with ordered keys and objects" do
|
719
|
+
@course.questions = { '0' => Question.new(:q => "Test1"), '1' => Question.new(:q => 'Test2') }
|
720
|
+
@course.questions.length.should eql(2)
|
721
|
+
@course.questions.last.q.should eql('Test2')
|
722
|
+
@course.questions.last.class.should eql(Question)
|
723
|
+
end
|
724
|
+
|
725
|
+
it "should allow attribute to be set from hash with ordered keys and sub-hashes" do
|
726
|
+
@course.questions = { '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} }
|
727
|
+
@course.questions.length.should eql(2)
|
728
|
+
@course.questions.last.q.should eql('Test2')
|
729
|
+
@course.questions.last.class.should eql(Question)
|
730
|
+
end
|
731
|
+
|
732
|
+
it "should allow attribute to be set from hash with ordered keys and HashWithIndifferentAccess" do
|
733
|
+
# This is similar to what you'd find in an HTML POST parameters
|
734
|
+
hash = HashWithIndifferentAccess.new({ '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} })
|
735
|
+
@course.questions = hash
|
736
|
+
@course.questions.length.should eql(2)
|
737
|
+
@course.questions.last.q.should eql('Test2')
|
738
|
+
@course.questions.last.class.should eql(Question)
|
739
|
+
end
|
740
|
+
|
741
|
+
|
742
|
+
it "should raise an error if attempting to set single value for array type" do
|
743
|
+
lambda {
|
744
|
+
@course.questions = Question.new(:q => 'test1')
|
745
|
+
}.should raise_error
|
746
|
+
end
|
747
|
+
|
748
|
+
|
749
|
+
end
|
750
|
+
|
751
|
+
describe "a casted model retrieved from the database" do
|
752
|
+
before(:each) do
|
753
|
+
reset_test_db!
|
754
|
+
@cat = Cat.new(:name => 'Stimpy')
|
755
|
+
@cat.favorite_toy = CatToy.new(:name => 'Stinky')
|
756
|
+
@cat.toys << CatToy.new(:name => 'Feather')
|
757
|
+
@cat.toys << CatToy.new(:name => 'Mouse')
|
758
|
+
@cat.save
|
759
|
+
@cat = Cat.get(@cat.id)
|
760
|
+
end
|
761
|
+
|
762
|
+
describe "as a casted property" do
|
763
|
+
it "should already be casted_by its parent" do
|
764
|
+
@cat.favorite_toy.casted_by.should === @cat
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
describe "from a casted collection" do
|
769
|
+
it "should already be casted_by its parent" do
|
770
|
+
@cat.toys[0].casted_by.should === @cat
|
771
|
+
@cat.toys[1].casted_by.should === @cat
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
describe "Property Class" do
|
777
|
+
|
778
|
+
it "should provide name as string" do
|
779
|
+
property = CouchRest::Model::Property.new(:test, String)
|
780
|
+
property.name.should eql('test')
|
781
|
+
property.to_s.should eql('test')
|
782
|
+
end
|
783
|
+
|
784
|
+
it "should provide class from type" do
|
785
|
+
property = CouchRest::Model::Property.new(:test, String)
|
786
|
+
property.type_class.should eql(String)
|
787
|
+
end
|
788
|
+
|
789
|
+
it "should provide base class from type in array" do
|
790
|
+
property = CouchRest::Model::Property.new(:test, [String])
|
791
|
+
property.type_class.should eql(String)
|
792
|
+
end
|
793
|
+
|
794
|
+
it "should raise error if type as string requested" do
|
795
|
+
lambda {
|
796
|
+
property = CouchRest::Model::Property.new(:test, 'String')
|
797
|
+
}.should raise_error
|
798
|
+
end
|
799
|
+
|
800
|
+
it "should leave type nil and return class as nil also" do
|
801
|
+
property = CouchRest::Model::Property.new(:test, nil)
|
802
|
+
property.type.should be_nil
|
803
|
+
property.type_class.should be_nil
|
804
|
+
end
|
805
|
+
|
806
|
+
it "should convert empty type array to [Object]" do
|
807
|
+
property = CouchRest::Model::Property.new(:test, [])
|
808
|
+
property.type_class.should eql(Object)
|
809
|
+
end
|
810
|
+
|
811
|
+
it "should set init method option or leave as 'new'" do
|
812
|
+
# (bad example! Time already typecast)
|
813
|
+
property = CouchRest::Model::Property.new(:test, Time)
|
814
|
+
property.init_method.should eql('new')
|
815
|
+
property = CouchRest::Model::Property.new(:test, Time, :init_method => 'parse')
|
816
|
+
property.init_method.should eql('parse')
|
817
|
+
end
|
818
|
+
|
819
|
+
## Property Casting method. More thoroughly tested earlier.
|
820
|
+
|
821
|
+
describe "casting" do
|
822
|
+
it "should cast a value" do
|
823
|
+
property = CouchRest::Model::Property.new(:test, Date)
|
824
|
+
parent = mock("FooObject")
|
825
|
+
property.cast(parent, "2010-06-16").should eql(Date.new(2010, 6, 16))
|
826
|
+
property.cast_value(parent, "2010-06-16").should eql(Date.new(2010, 6, 16))
|
827
|
+
end
|
828
|
+
|
829
|
+
it "should cast an array of values" do
|
830
|
+
property = CouchRest::Model::Property.new(:test, [Date])
|
831
|
+
parent = mock("FooObject")
|
832
|
+
property.cast(parent, ["2010-06-01", "2010-06-02"]).should eql([Date.new(2010, 6, 1), Date.new(2010, 6, 2)])
|
833
|
+
end
|
834
|
+
|
835
|
+
it "should set a CastedArray on array of Objects" do
|
836
|
+
property = CouchRest::Model::Property.new(:test, [Object])
|
837
|
+
parent = mock("FooObject")
|
838
|
+
property.cast(parent, ["2010-06-01", "2010-06-02"]).class.should eql(CouchRest::Model::CastedArray)
|
839
|
+
end
|
840
|
+
|
841
|
+
it "should not set a CastedArray on array of Strings" do
|
842
|
+
property = CouchRest::Model::Property.new(:test, [String])
|
843
|
+
parent = mock("FooObject")
|
844
|
+
property.cast(parent, ["2010-06-01", "2010-06-02"]).class.should_not eql(CouchRest::Model::CastedArray)
|
845
|
+
end
|
846
|
+
|
847
|
+
it "should raise and error if value is array when type is not" do
|
848
|
+
property = CouchRest::Model::Property.new(:test, Date)
|
849
|
+
parent = mock("FooClass")
|
850
|
+
lambda {
|
851
|
+
cast = property.cast(parent, [Date.new(2010, 6, 1)])
|
852
|
+
}.should raise_error
|
853
|
+
end
|
854
|
+
|
855
|
+
|
856
|
+
it "should set parent as casted_by object in CastedArray" do
|
857
|
+
property = CouchRest::Model::Property.new(:test, [Object])
|
858
|
+
parent = mock("FooObject")
|
859
|
+
property.cast(parent, ["2010-06-01", "2010-06-02"]).casted_by.should eql(parent)
|
860
|
+
end
|
861
|
+
|
862
|
+
it "should set casted_by on new value" do
|
863
|
+
property = CouchRest::Model::Property.new(:test, CatToy)
|
864
|
+
parent = mock("CatObject")
|
865
|
+
cast = property.cast(parent, {:name => 'catnip'})
|
866
|
+
cast.casted_by.should eql(parent)
|
867
|
+
end
|
868
|
+
|
869
|
+
end
|
870
|
+
|
871
|
+
end
|