couchrest_model 2.1.0.rc1 → 2.2.0.beta1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +15 -4
- data/Gemfile.activesupport-4.x +4 -0
- data/Gemfile.activesupport-5.x +4 -0
- data/README.md +2 -0
- data/VERSION +1 -1
- data/couchrest_model.gemspec +3 -2
- data/history.md +14 -1
- data/lib/couchrest/model/associations.rb +3 -8
- data/lib/couchrest/model/base.rb +15 -7
- data/lib/couchrest/model/casted_array.rb +22 -34
- data/lib/couchrest/model/configuration.rb +2 -0
- data/lib/couchrest/model/design.rb +4 -3
- data/lib/couchrest/model/designs/view.rb +37 -32
- data/lib/couchrest/model/dirty.rb +93 -19
- data/lib/couchrest/model/embeddable.rb +2 -14
- data/lib/couchrest/model/extended_attachments.rb +2 -4
- data/lib/couchrest/model/persistence.rb +14 -17
- data/lib/couchrest/model/properties.rb +46 -54
- data/lib/couchrest/model/property.rb +0 -3
- data/lib/couchrest/model/proxyable.rb +20 -4
- data/lib/couchrest/model/validations/uniqueness.rb +4 -1
- data/lib/couchrest_model.rb +2 -2
- data/spec/fixtures/models/article.rb +1 -1
- data/spec/fixtures/models/card.rb +2 -1
- data/spec/fixtures/models/person.rb +1 -0
- data/spec/fixtures/models/project.rb +3 -0
- data/spec/unit/assocations_spec.rb +73 -73
- data/spec/unit/attachment_spec.rb +34 -34
- data/spec/unit/base_spec.rb +102 -102
- data/spec/unit/casted_array_spec.rb +7 -7
- data/spec/unit/casted_spec.rb +7 -7
- data/spec/unit/configuration_spec.rb +11 -11
- data/spec/unit/connection_spec.rb +30 -30
- data/spec/unit/core_extensions/{time_parsing.rb → time_parsing_spec.rb} +21 -21
- data/spec/unit/design_spec.rb +38 -38
- data/spec/unit/designs/design_mapper_spec.rb +26 -26
- data/spec/unit/designs/migrations_spec.rb +13 -13
- data/spec/unit/designs/view_spec.rb +319 -274
- data/spec/unit/designs_spec.rb +39 -39
- data/spec/unit/dirty_spec.rb +188 -103
- data/spec/unit/embeddable_spec.rb +119 -117
- data/spec/unit/inherited_spec.rb +4 -4
- data/spec/unit/persistence_spec.rb +122 -122
- data/spec/unit/properties_spec.rb +466 -16
- data/spec/unit/property_protection_spec.rb +32 -32
- data/spec/unit/property_spec.rb +45 -436
- data/spec/unit/proxyable_spec.rb +140 -82
- data/spec/unit/subclass_spec.rb +14 -14
- data/spec/unit/translations_spec.rb +5 -5
- data/spec/unit/typecast_spec.rb +131 -131
- data/spec/unit/utils/migrate_spec.rb +2 -2
- data/spec/unit/validations_spec.rb +31 -31
- metadata +27 -12
- data/lib/couchrest/model/casted_hash.rb +0 -84
@@ -12,16 +12,16 @@ describe "Model Attributes" do
|
|
12
12
|
it "should not protect anything through new" do
|
13
13
|
user = NoProtection.new(:name => "will", :phone => "555-5555")
|
14
14
|
|
15
|
-
user.name.
|
16
|
-
user.phone.
|
15
|
+
expect(user.name).to eq("will")
|
16
|
+
expect(user.phone).to eq("555-5555")
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should not protect anything through attributes=" do
|
20
20
|
user = NoProtection.new
|
21
21
|
user.attributes = {:name => "will", :phone => "555-5555"}
|
22
22
|
|
23
|
-
user.name.
|
24
|
-
user.phone.
|
23
|
+
expect(user.name).to eq("will")
|
24
|
+
expect(user.phone).to eq("555-5555")
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should recreate from the database properly" do
|
@@ -31,14 +31,14 @@ describe "Model Attributes" do
|
|
31
31
|
user.save!
|
32
32
|
|
33
33
|
user = NoProtection.get(user.id)
|
34
|
-
user.name.
|
35
|
-
user.phone.
|
34
|
+
expect(user.name).to eq("will")
|
35
|
+
expect(user.phone).to eq("555-5555")
|
36
36
|
end
|
37
37
|
|
38
38
|
it "should provide a list of all properties as accessible" do
|
39
39
|
user = NoProtection.new(:name => "will", :phone => "555-5555")
|
40
|
-
user.accessible_properties.length.
|
41
|
-
user.protected_properties.
|
40
|
+
expect(user.accessible_properties.length).to eql(2)
|
41
|
+
expect(user.protected_properties).to be_empty
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -53,29 +53,29 @@ describe "Model Attributes" do
|
|
53
53
|
|
54
54
|
it "should recognize accessible properties" do
|
55
55
|
props = WithAccessible.accessible_properties.map { |prop| prop.name}
|
56
|
-
props.
|
57
|
-
props.
|
56
|
+
expect(props).to include("name")
|
57
|
+
expect(props).not_to include("admin")
|
58
58
|
end
|
59
59
|
|
60
60
|
it "should protect non-accessible properties set through new" do
|
61
61
|
user = WithAccessible.new(:name => "will", :admin => true)
|
62
62
|
|
63
|
-
user.name.
|
64
|
-
user.admin.
|
63
|
+
expect(user.name).to eq("will")
|
64
|
+
expect(user.admin).to eq(false)
|
65
65
|
end
|
66
66
|
|
67
67
|
it "should protect non-accessible properties set through attributes=" do
|
68
68
|
user = WithAccessible.new
|
69
69
|
user.attributes = {:name => "will", :admin => true}
|
70
70
|
|
71
|
-
user.name.
|
72
|
-
user.admin.
|
71
|
+
expect(user.name).to eq("will")
|
72
|
+
expect(user.admin).to eq(false)
|
73
73
|
end
|
74
74
|
|
75
75
|
it "should provide correct accessible and protected property lists" do
|
76
76
|
user = WithAccessible.new(:name => 'will', :admin => true)
|
77
|
-
user.accessible_properties.map{|p| p.to_s}.
|
78
|
-
user.protected_properties.map{|p| p.to_s}.
|
77
|
+
expect(user.accessible_properties.map{|p| p.to_s}).to eql(['name'])
|
78
|
+
expect(user.protected_properties.map{|p| p.to_s}).to eql(['admin'])
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -90,37 +90,37 @@ describe "Model Attributes" do
|
|
90
90
|
|
91
91
|
it "should recognize protected properties" do
|
92
92
|
props = WithProtected.protected_properties.map { |prop| prop.name}
|
93
|
-
props.
|
94
|
-
props.
|
93
|
+
expect(props).not_to include("name")
|
94
|
+
expect(props).to include("admin")
|
95
95
|
end
|
96
96
|
|
97
97
|
it "should protect non-accessible properties set through new" do
|
98
98
|
user = WithProtected.new(:name => "will", :admin => true)
|
99
99
|
|
100
|
-
user.name.
|
101
|
-
user.admin.
|
100
|
+
expect(user.name).to eq("will")
|
101
|
+
expect(user.admin).to eq(false)
|
102
102
|
end
|
103
103
|
|
104
104
|
it "should protect non-accessible properties set through attributes=" do
|
105
105
|
user = WithProtected.new
|
106
106
|
user.attributes = {:name => "will", :admin => true}
|
107
107
|
|
108
|
-
user.name.
|
109
|
-
user.admin.
|
108
|
+
expect(user.name).to eq("will")
|
109
|
+
expect(user.admin).to eq(false)
|
110
110
|
end
|
111
111
|
|
112
112
|
it "should not modify the provided attribute hash" do
|
113
113
|
user = WithProtected.new
|
114
114
|
attrs = {:name => "will", :admin => true}
|
115
115
|
user.attributes = attrs
|
116
|
-
attrs[:admin].
|
117
|
-
attrs[:name].
|
116
|
+
expect(attrs[:admin]).to be_truthy
|
117
|
+
expect(attrs[:name]).to eql('will')
|
118
118
|
end
|
119
119
|
|
120
120
|
it "should provide correct accessible and protected property lists" do
|
121
121
|
user = WithProtected.new(:name => 'will', :admin => true)
|
122
|
-
user.accessible_properties.map{|p| p.to_s}.
|
123
|
-
user.protected_properties.map{|p| p.to_s}.
|
122
|
+
expect(user.accessible_properties.map{|p| p.to_s}).to eql(['name'])
|
123
|
+
expect(user.protected_properties.map{|p| p.to_s}).to eql(['admin'])
|
124
124
|
end
|
125
125
|
|
126
126
|
end
|
@@ -138,9 +138,9 @@ describe "Model Attributes" do
|
|
138
138
|
it 'should assume that any unspecified property is protected by default' do
|
139
139
|
user = WithBothAndUnspecified.new(:name => 'will', :admin => true, :phone => '555-1234')
|
140
140
|
|
141
|
-
user.name.
|
142
|
-
user.admin.
|
143
|
-
user.phone.
|
141
|
+
expect(user.name).to eq('will')
|
142
|
+
expect(user.admin).to eq(false)
|
143
|
+
expect(user.phone).to eq('unset phone number')
|
144
144
|
end
|
145
145
|
|
146
146
|
end
|
@@ -163,8 +163,8 @@ describe "Model Attributes" do
|
|
163
163
|
end
|
164
164
|
|
165
165
|
def verify_attrs(user)
|
166
|
-
user.name.
|
167
|
-
user.admin.
|
166
|
+
expect(user.name).to eq("will")
|
167
|
+
expect(user.admin).to eq(true)
|
168
168
|
end
|
169
169
|
|
170
170
|
it "Base#get should not strip protected attributes" do
|
@@ -180,7 +180,7 @@ describe "Model Attributes" do
|
|
180
180
|
it "Base#all should not strip protected attributes" do
|
181
181
|
# all creates a CollectionProxy
|
182
182
|
docs = WithProtected.all(:key => @user.id)
|
183
|
-
docs.length.
|
183
|
+
expect(docs.length).to eq(1)
|
184
184
|
reloaded = docs.first
|
185
185
|
verify_attrs reloaded
|
186
186
|
end
|
data/spec/unit/property_spec.rb
CHANGED
@@ -3,504 +3,113 @@ require 'spec_helper'
|
|
3
3
|
|
4
4
|
describe CouchRest::Model::Property do
|
5
5
|
|
6
|
-
before(:each) do
|
7
|
-
reset_test_db!
|
8
|
-
@card = Card.new(:first_name => "matt")
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should be accessible from the object" do
|
12
|
-
@card.properties.should be_an_instance_of(Array)
|
13
|
-
@card.properties.map{|p| p.name}.should include("first_name")
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should list object properties with values" do
|
17
|
-
@card.properties_with_values.should be_an_instance_of(Hash)
|
18
|
-
@card.properties_with_values["first_name"].should == "matt"
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should let you access a property value (getter)" do
|
22
|
-
@card.first_name.should == "matt"
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should let you set a property value (setter)" do
|
26
|
-
@card.last_name = "Aimonetti"
|
27
|
-
@card.last_name.should == "Aimonetti"
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should not let you set a property value if it's read only" do
|
31
|
-
lambda{@card.read_only_value = "test"}.should raise_error
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should let you use an alias for an attribute" do
|
35
|
-
@card.last_name = "Aimonetti"
|
36
|
-
@card.family_name.should == "Aimonetti"
|
37
|
-
@card.family_name.should == @card.last_name
|
38
|
-
end
|
39
|
-
|
40
|
-
it "should let you use an alias for a casted attribute" do
|
41
|
-
@card.cast_alias = Person.new(:name => ["Aimonetti"])
|
42
|
-
@card.cast_alias.name.should == ["Aimonetti"]
|
43
|
-
@card.calias.name.should == ["Aimonetti"]
|
44
|
-
card = Card.new(:first_name => "matt", :cast_alias => {:name => ["Aimonetti"]})
|
45
|
-
card.cast_alias.name.should == ["Aimonetti"]
|
46
|
-
card.calias.name.should == ["Aimonetti"]
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should raise error if property name coincides with model type key" do
|
50
|
-
lambda { Cat.property(Cat.model_type_key) }.should raise_error(/already used/)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should not raise error if property name coincides with model type key on non-model" do
|
54
|
-
lambda { Person.property(Article.model_type_key) }.should_not raise_error
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should be auto timestamped" do
|
58
|
-
@card.created_at.should be_nil
|
59
|
-
@card.updated_at.should be_nil
|
60
|
-
@card.save.should be_true
|
61
|
-
@card.created_at.should_not be_nil
|
62
|
-
@card.updated_at.should_not be_nil
|
63
|
-
end
|
64
|
-
|
65
|
-
describe "#as_couch_json" do
|
66
|
-
|
67
|
-
it "should provide a simple hash from model" do
|
68
|
-
@card.as_couch_json.class.should eql(Hash)
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should remove properties from Hash if value is nil" do
|
72
|
-
@card.last_name = nil
|
73
|
-
@card.as_couch_json.keys.include?('last_name').should be_false
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
describe "#as_json" do
|
79
|
-
|
80
|
-
it "should provide a simple hash from model" do
|
81
|
-
@card.as_json.class.should eql(Hash)
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should pass options to Active Support's as_json" do
|
85
|
-
@card.last_name = "Aimonetti"
|
86
|
-
@card.as_json(:only => 'last_name').should eql('last_name' => 'Aimonetti')
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
describe '#read_attribute' do
|
92
|
-
it "should let you use read_attribute method" do
|
93
|
-
@card.last_name = "Aimonetti"
|
94
|
-
@card.read_attribute(:last_name).should eql('Aimonetti')
|
95
|
-
@card.read_attribute('last_name').should eql('Aimonetti')
|
96
|
-
last_name_prop = @card.properties.find{|p| p.name == 'last_name'}
|
97
|
-
@card.read_attribute(last_name_prop).should eql('Aimonetti')
|
98
|
-
end
|
99
|
-
|
100
|
-
it 'should raise an error if the property does not exist' do
|
101
|
-
expect { @card.read_attribute(:this_property_should_not_exist) }.to raise_error(ArgumentError)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
describe '#write_attribute' do
|
106
|
-
it "should let you use write_attribute method" do
|
107
|
-
@card.write_attribute(:last_name, 'Aimonetti 1')
|
108
|
-
@card.last_name.should eql('Aimonetti 1')
|
109
|
-
@card.write_attribute('last_name', 'Aimonetti 2')
|
110
|
-
@card.last_name.should eql('Aimonetti 2')
|
111
|
-
last_name_prop = @card.properties.find{|p| p.name == 'last_name'}
|
112
|
-
@card.write_attribute(last_name_prop, 'Aimonetti 3')
|
113
|
-
@card.last_name.should eql('Aimonetti 3')
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'should raise an error if the property does not exist' do
|
117
|
-
expect { @card.write_attribute(:this_property_should_not_exist, 823) }.to raise_error(ArgumentError)
|
118
|
-
end
|
119
|
-
|
120
|
-
|
121
|
-
it "should let you use write_attribute on readonly properties" do
|
122
|
-
lambda {
|
123
|
-
@card.read_only_value = "foo"
|
124
|
-
}.should raise_error
|
125
|
-
@card.write_attribute(:read_only_value, "foo")
|
126
|
-
@card.read_only_value.should == 'foo'
|
127
|
-
end
|
128
|
-
|
129
|
-
it "should cast via write_attribute" do
|
130
|
-
@card.write_attribute(:cast_alias, {:name => ["Sam", "Lown"]})
|
131
|
-
@card.cast_alias.class.should eql(Person)
|
132
|
-
@card.cast_alias.name.last.should eql("Lown")
|
133
|
-
end
|
134
|
-
|
135
|
-
it "should not cast via write_attribute if property not casted" do
|
136
|
-
@card.write_attribute(:first_name, {:name => "Sam"})
|
137
|
-
@card.first_name.class.should eql(Hash)
|
138
|
-
@card.first_name[:name].should eql("Sam")
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
describe "mass updating attributes without property" do
|
143
|
-
|
144
|
-
describe "when mass_assign_any_attribute false" do
|
145
|
-
|
146
|
-
it "should not allow them to be set" do
|
147
|
-
@card.attributes = {:test => 'fooobar'}
|
148
|
-
@card['test'].should be_nil
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'should not allow them to be updated with update_attributes' do
|
152
|
-
@card.update_attributes(:test => 'fooobar')
|
153
|
-
@card['test'].should be_nil
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'should not have a different revision after update_attributes' do
|
157
|
-
@card.save
|
158
|
-
rev = @card.rev
|
159
|
-
@card.update_attributes(:test => 'fooobar')
|
160
|
-
@card.rev.should eql(rev)
|
161
|
-
end
|
162
|
-
|
163
|
-
it 'should not have a different revision after save' do
|
164
|
-
@card.save
|
165
|
-
rev = @card.rev
|
166
|
-
@card.attributes = {:test => 'fooobar'}
|
167
|
-
@card.save
|
168
|
-
@card.rev.should eql(rev)
|
169
|
-
end
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
describe "when mass_assign_any_attribute true" do
|
174
|
-
before(:each) do
|
175
|
-
# dup Card class so that no other tests are effected
|
176
|
-
card_class = Card.dup
|
177
|
-
card_class.class_eval do
|
178
|
-
mass_assign_any_attribute true
|
179
|
-
end
|
180
|
-
@card = card_class.new(:first_name => 'Sam')
|
181
|
-
end
|
182
|
-
|
183
|
-
it 'should allow them to be updated' do
|
184
|
-
@card.attributes = {:testing => 'fooobar'}
|
185
|
-
@card['testing'].should eql('fooobar')
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'should allow them to be updated with update_attributes' do
|
189
|
-
@card.update_attributes(:testing => 'fooobar')
|
190
|
-
@card['testing'].should eql('fooobar')
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'should have a different revision after update_attributes' do
|
194
|
-
@card.save
|
195
|
-
rev = @card.rev
|
196
|
-
@card.update_attributes(:testing => 'fooobar')
|
197
|
-
@card.rev.should_not eql(rev)
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'should have a different revision after save' do
|
201
|
-
@card.save
|
202
|
-
rev = @card.rev
|
203
|
-
@card.attributes = {:testing => 'fooobar'}
|
204
|
-
@card.save
|
205
|
-
@card.rev.should_not eql(rev)
|
206
|
-
end
|
207
|
-
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
describe "mass assignment protection" do
|
212
|
-
|
213
|
-
it "should not store protected attribute using mass assignment" do
|
214
|
-
cat_toy = CatToy.new(:name => "Zorro")
|
215
|
-
cat = Cat.create(:name => "Helena", :toys => [cat_toy], :favorite_toy => cat_toy, :number => 1)
|
216
|
-
cat.number.should be_nil
|
217
|
-
cat.number = 1
|
218
|
-
cat.save
|
219
|
-
cat.number.should == 1
|
220
|
-
end
|
221
|
-
|
222
|
-
it "should not store protected attribute when 'declare accessible poperties, assume all the rest are protected'" do
|
223
|
-
user = User.create(:name => "Marcos Tapajós", :admin => true)
|
224
|
-
user.admin.should be_nil
|
225
|
-
end
|
226
|
-
|
227
|
-
it "should not store protected attribute when 'declare protected properties, assume all the rest are accessible'" do
|
228
|
-
user = SpecialUser.create(:name => "Marcos Tapajós", :admin => true)
|
229
|
-
user.admin.should be_nil
|
230
|
-
end
|
231
|
-
|
232
|
-
end
|
233
|
-
|
234
|
-
describe "validation" do
|
235
|
-
before(:each) do
|
236
|
-
@invoice = Invoice.new(:client_name => "matt", :employee_name => "Chris", :location => "San Diego, CA")
|
237
|
-
end
|
238
|
-
|
239
|
-
it "should be able to be validated" do
|
240
|
-
@card.valid?.should == true
|
241
|
-
end
|
242
|
-
|
243
|
-
it "should let you validate the presence of an attribute" do
|
244
|
-
@card.first_name = nil
|
245
|
-
@card.should_not be_valid
|
246
|
-
@card.errors.should_not be_empty
|
247
|
-
@card.errors[:first_name].should == ["can't be blank"]
|
248
|
-
end
|
249
|
-
|
250
|
-
it "should let you look up errors for a field by a string name" do
|
251
|
-
@card.first_name = nil
|
252
|
-
@card.should_not be_valid
|
253
|
-
@card.errors['first_name'].should == ["can't be blank"]
|
254
|
-
end
|
255
|
-
|
256
|
-
it "should validate the presence of 2 attributes" do
|
257
|
-
@invoice.clear
|
258
|
-
@invoice.should_not be_valid
|
259
|
-
@invoice.errors.should_not be_empty
|
260
|
-
@invoice.errors[:client_name].should == ["can't be blank"]
|
261
|
-
@invoice.errors[:employee_name].should_not be_empty
|
262
|
-
end
|
263
|
-
|
264
|
-
it "should let you set an error message" do
|
265
|
-
@invoice.location = nil
|
266
|
-
@invoice.valid?
|
267
|
-
@invoice.errors[:location].should == ["Hey stupid!, you forgot the location"]
|
268
|
-
end
|
269
|
-
|
270
|
-
it "should validate before saving" do
|
271
|
-
@invoice.location = nil
|
272
|
-
@invoice.should_not be_valid
|
273
|
-
@invoice.save.should be_false
|
274
|
-
@invoice.should be_new
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
describe "properties of hash of casted models" do
|
281
|
-
it "should be able to assign a casted hash to a hash property" do
|
282
|
-
chain = KeyChain.new
|
283
|
-
keys = {"House" => "8==$", "Office" => "<>==U"}
|
284
|
-
chain.keys = keys
|
285
|
-
chain.keys = chain.keys
|
286
|
-
chain.keys.should == keys
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
describe "properties of array of casted models" do
|
291
|
-
|
292
|
-
before(:each) do
|
293
|
-
@course = Course.new :title => 'Test Course'
|
294
|
-
end
|
295
|
-
|
296
|
-
it "should allow attribute to be set from an array of objects" do
|
297
|
-
@course.questions = [Question.new(:q => "works?"), Question.new(:q => "Meaning of Life?")]
|
298
|
-
@course.questions.length.should eql(2)
|
299
|
-
end
|
300
|
-
|
301
|
-
it "should allow attribute to be set from an array of hashes" do
|
302
|
-
@course.questions = [{:q => "works?"}, {:q => "Meaning of Life?"}]
|
303
|
-
@course.questions.length.should eql(2)
|
304
|
-
@course.questions.last.q.should eql("Meaning of Life?")
|
305
|
-
@course.questions.last.class.should eql(Question) # typecasting
|
306
|
-
end
|
307
|
-
|
308
|
-
it "should allow attribute to be set from hash with ordered keys and objects" do
|
309
|
-
@course.questions = { '0' => Question.new(:q => "Test1"), '1' => Question.new(:q => 'Test2') }
|
310
|
-
@course.questions.length.should eql(2)
|
311
|
-
@course.questions.last.q.should eql('Test2')
|
312
|
-
@course.questions.last.class.should eql(Question)
|
313
|
-
end
|
314
|
-
|
315
|
-
it "should allow attribute to be set from hash with ordered keys and sub-hashes" do
|
316
|
-
@course.questions = { '10' => {:q => 'Test10'}, '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} }
|
317
|
-
@course.questions.length.should eql(3)
|
318
|
-
@course.questions.last.q.should eql('Test10')
|
319
|
-
@course.questions.last.class.should eql(Question)
|
320
|
-
end
|
321
|
-
|
322
|
-
it "should allow attribute to be set from hash with ordered keys and HashWithIndifferentAccess" do
|
323
|
-
# This is similar to what you'd find in an HTML POST parameters
|
324
|
-
hash = HashWithIndifferentAccess.new({ '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} })
|
325
|
-
@course.questions = hash
|
326
|
-
@course.questions.length.should eql(2)
|
327
|
-
@course.questions.last.q.should eql('Test2')
|
328
|
-
@course.questions.last.class.should eql(Question)
|
329
|
-
end
|
330
|
-
|
331
|
-
it "should allow attribute to be set from Hash subclass with ordered keys" do
|
332
|
-
ourhash = Class.new(HashWithIndifferentAccess)
|
333
|
-
hash = ourhash.new({ '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} })
|
334
|
-
@course.questions = hash
|
335
|
-
@course.questions.length.should eql(2)
|
336
|
-
@course.questions.last.q.should eql('Test2')
|
337
|
-
@course.questions.last.class.should eql(Question)
|
338
|
-
end
|
339
|
-
|
340
|
-
it "should raise an error if attempting to set single value for array type" do
|
341
|
-
lambda {
|
342
|
-
@course.questions = Question.new(:q => 'test1')
|
343
|
-
}.should raise_error(/Expecting an array/)
|
344
|
-
end
|
345
|
-
|
346
|
-
|
347
|
-
end
|
348
|
-
|
349
|
-
describe "a casted model retrieved from the database" do
|
350
|
-
before(:each) do
|
351
|
-
reset_test_db!
|
352
|
-
@cat = Cat.new(:name => 'Stimpy')
|
353
|
-
@cat.favorite_toy = CatToy.new(:name => 'Stinky')
|
354
|
-
@cat.toys << CatToy.new(:name => 'Feather')
|
355
|
-
@cat.toys << CatToy.new(:name => 'Mouse')
|
356
|
-
@cat.save
|
357
|
-
@cat = Cat.get(@cat.id)
|
358
|
-
end
|
359
|
-
|
360
|
-
describe "as a casted property" do
|
361
|
-
it "should already be casted_by its parent" do
|
362
|
-
@cat.favorite_toy.casted_by.should === @cat
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
describe "from a casted collection" do
|
367
|
-
it "should already be casted_by its parent" do
|
368
|
-
@cat.toys[0].casted_by.should === @cat
|
369
|
-
@cat.toys[1].casted_by.should === @cat
|
370
|
-
end
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
describe "nested models (not casted)" do
|
375
|
-
before(:each) do
|
376
|
-
reset_test_db!
|
377
|
-
@cat = ChildCat.new(:name => 'Stimpy')
|
378
|
-
@cat.mother = {:name => 'Stinky'}
|
379
|
-
@cat.siblings = [{:name => 'Feather'}, {:name => 'Felix'}]
|
380
|
-
@cat.save
|
381
|
-
@cat = ChildCat.get(@cat.id)
|
382
|
-
end
|
383
|
-
|
384
|
-
it "should correctly save single relation" do
|
385
|
-
@cat.mother.name.should eql('Stinky')
|
386
|
-
@cat.mother.casted_by.should eql(@cat)
|
387
|
-
end
|
388
|
-
|
389
|
-
it "should correctly save collection" do
|
390
|
-
@cat.siblings.first.name.should eql("Feather")
|
391
|
-
@cat.siblings.last.casted_by.should eql(@cat)
|
392
|
-
end
|
393
|
-
end
|
394
|
-
|
395
|
-
describe "Property Class" do
|
396
|
-
|
397
6
|
let :klass do
|
398
7
|
CouchRest::Model::Property
|
399
8
|
end
|
400
9
|
|
401
10
|
it "should provide name as string" do
|
402
11
|
property = CouchRest::Model::Property.new(:test, :type => String)
|
403
|
-
property.name.
|
404
|
-
property.to_s.
|
12
|
+
expect(property.name).to eql('test')
|
13
|
+
expect(property.to_s).to eql('test')
|
405
14
|
end
|
406
15
|
|
407
16
|
it "should provide name as a symbol" do
|
408
17
|
property = CouchRest::Model::Property.new(:test, :type => String)
|
409
|
-
property.name.to_sym.
|
410
|
-
property.to_sym.
|
18
|
+
expect(property.name.to_sym).to eql(:test)
|
19
|
+
expect(property.to_sym).to eql(:test)
|
411
20
|
end
|
412
21
|
|
413
22
|
it "should provide class from type" do
|
414
23
|
property = CouchRest::Model::Property.new(:test, :type => String)
|
415
|
-
property.type.
|
416
|
-
property.array.
|
24
|
+
expect(property.type).to eql(String)
|
25
|
+
expect(property.array).to be_falsey
|
417
26
|
end
|
418
27
|
|
419
28
|
it "should provide base class from type in array" do
|
420
29
|
property = CouchRest::Model::Property.new(:test, :type => [String])
|
421
|
-
property.type.
|
422
|
-
property.array.
|
30
|
+
expect(property.type).to eql(String)
|
31
|
+
expect(property.array).to be_truthy
|
423
32
|
end
|
424
33
|
|
425
34
|
it "should provide base class and set array type" do
|
426
35
|
property = CouchRest::Model::Property.new(:test, :type => String, :array => true)
|
427
|
-
property.type.
|
428
|
-
property.array.
|
36
|
+
expect(property.type).to eql(String)
|
37
|
+
expect(property.array).to be_truthy
|
429
38
|
end
|
430
39
|
|
431
40
|
it "should raise error if type as string requested" do
|
432
|
-
|
433
|
-
|
434
|
-
}.
|
41
|
+
expect {
|
42
|
+
CouchRest::Model::Property.new(:test, :type => 'String')
|
43
|
+
}.to raise_error(/Defining a property type as a String is not supported/)
|
435
44
|
end
|
436
45
|
|
437
46
|
it "should leave type nil and return class as nil also" do
|
438
47
|
property = CouchRest::Model::Property.new(:test, :type => nil)
|
439
|
-
property.type.
|
48
|
+
expect(property.type).to be_nil
|
440
49
|
end
|
441
50
|
|
442
51
|
it "should convert empty type array to [Object]" do
|
443
52
|
property = CouchRest::Model::Property.new(:test, :type => [])
|
444
|
-
property.type.
|
53
|
+
expect(property.type).to eql(Object)
|
445
54
|
end
|
446
55
|
|
447
56
|
it "should set init method option or leave as 'new'" do
|
448
57
|
# (bad example! Time already typecast)
|
449
58
|
property = CouchRest::Model::Property.new(:test, :type => Time)
|
450
|
-
property.init_method.
|
59
|
+
expect(property.init_method).to eql('new')
|
451
60
|
property = CouchRest::Model::Property.new(:test, :type => Time, :init_method => 'parse')
|
452
|
-
property.init_method.
|
61
|
+
expect(property.init_method).to eql('parse')
|
453
62
|
end
|
454
63
|
|
455
64
|
it "should set the allow_blank option to true by default" do
|
456
65
|
property = CouchRest::Model::Property.new(:test, :type => String)
|
457
|
-
property.allow_blank.
|
66
|
+
expect(property.allow_blank).to be_truthy
|
458
67
|
end
|
459
68
|
|
460
69
|
it "should allow setting of the allow_blank option to false" do
|
461
70
|
property = CouchRest::Model::Property.new(:test, :type => String, :allow_blank => false)
|
462
|
-
property.allow_blank.
|
71
|
+
expect(property.allow_blank).to be_falsey
|
463
72
|
end
|
464
73
|
|
465
74
|
it "should convert block to type" do
|
466
75
|
prop = klass.new(:test) do
|
467
76
|
property :testing
|
468
77
|
end
|
469
|
-
prop.array.
|
470
|
-
prop.type.
|
471
|
-
prop.type.class.
|
78
|
+
expect(prop.array).to be_falsey
|
79
|
+
expect(prop.type).not_to be_nil
|
80
|
+
expect(prop.type.class).to eql(Class)
|
472
81
|
obj = prop.type.new
|
473
|
-
obj.
|
82
|
+
expect(obj).to respond_to(:testing)
|
474
83
|
end
|
475
84
|
|
476
85
|
it "should convert block to type with array" do
|
477
86
|
prop = klass.new(:test, :array => true) do
|
478
87
|
property :testing
|
479
88
|
end
|
480
|
-
prop.type.
|
481
|
-
prop.type.class.
|
482
|
-
prop.array.
|
89
|
+
expect(prop.type).not_to be_nil
|
90
|
+
expect(prop.type.class).to eql(Class)
|
91
|
+
expect(prop.array).to be_truthy
|
483
92
|
end
|
484
93
|
|
485
94
|
describe "#build" do
|
486
95
|
it "should allow instantiation of new object" do
|
487
96
|
property = CouchRest::Model::Property.new(:test, :type => Date)
|
488
97
|
obj = property.build(2011, 05, 21)
|
489
|
-
obj.
|
98
|
+
expect(obj).to eql(Date.new(2011, 05, 21))
|
490
99
|
end
|
491
100
|
it "should use init_method if provided" do
|
492
101
|
property = CouchRest::Model::Property.new(:test, :type => Date, :init_method => 'parse')
|
493
102
|
obj = property.build("2011-05-21")
|
494
|
-
obj.
|
103
|
+
expect(obj).to eql(Date.new(2011, 05, 21))
|
495
104
|
end
|
496
105
|
it "should use init_method Proc if provided" do
|
497
106
|
property = CouchRest::Model::Property.new(:test, :type => Date, :init_method => Proc.new{|v| Date.parse(v)})
|
498
107
|
obj = property.build("2011-05-21")
|
499
|
-
obj.
|
108
|
+
expect(obj).to eql(Date.new(2011, 05, 21))
|
500
109
|
end
|
501
110
|
it "should raise error if no class" do
|
502
111
|
property = CouchRest::Model::Property.new(:test)
|
503
|
-
|
112
|
+
expect { property.build }.to raise_error(StandardError, /Cannot build/)
|
504
113
|
end
|
505
114
|
end
|
506
115
|
|
@@ -510,20 +119,20 @@ describe "Property Class" do
|
|
510
119
|
it "should cast a value" do
|
511
120
|
property = CouchRest::Model::Property.new(:test, :type => Date)
|
512
121
|
parent = double("FooObject")
|
513
|
-
property.cast(parent, "2010-06-16").
|
514
|
-
property.cast_value(parent, "2010-06-16").
|
122
|
+
expect(property.cast(parent, "2010-06-16")).to eql(Date.new(2010, 6, 16))
|
123
|
+
expect(property.cast_value(parent, "2010-06-16")).to eql(Date.new(2010, 6, 16))
|
515
124
|
end
|
516
125
|
|
517
126
|
it "should cast an array of values" do
|
518
127
|
property = CouchRest::Model::Property.new(:test, :type => [Date])
|
519
128
|
parent = double("FooObject")
|
520
|
-
property.cast(parent, ["2010-06-01", "2010-06-02"]).
|
129
|
+
expect(property.cast(parent, ["2010-06-01", "2010-06-02"])).to eql([Date.new(2010, 6, 1), Date.new(2010, 6, 2)])
|
521
130
|
end
|
522
131
|
|
523
132
|
it "should cast an array of values with array option" do
|
524
133
|
property = CouchRest::Model::Property.new(:test, :type => Date, :array => true)
|
525
134
|
parent = double("FooObject")
|
526
|
-
property.cast(parent, ["2010-06-01", "2010-06-02"]).
|
135
|
+
expect(property.cast(parent, ["2010-06-01", "2010-06-02"])).to eql([Date.new(2010, 6, 1), Date.new(2010, 6, 2)])
|
527
136
|
end
|
528
137
|
|
529
138
|
context "when allow_blank is false" do
|
@@ -533,25 +142,25 @@ describe "Property Class" do
|
|
533
142
|
|
534
143
|
it "should convert blank to nil" do
|
535
144
|
property = CouchRest::Model::Property.new(:test, :type => String, :allow_blank => false)
|
536
|
-
property.cast(parent, "").
|
145
|
+
expect(property.cast(parent, "")).to be_nil
|
537
146
|
end
|
538
147
|
|
539
148
|
it "should remove blank array entries" do
|
540
149
|
property = CouchRest::Model::Property.new(:test, :type => [String], :allow_blank => false)
|
541
|
-
property.cast(parent, ["", "foo"]).
|
150
|
+
expect(property.cast(parent, ["", "foo"])).to eql(["foo"])
|
542
151
|
end
|
543
152
|
end
|
544
153
|
|
545
154
|
it "should set a CastedArray on array of Objects" do
|
546
155
|
property = CouchRest::Model::Property.new(:test, :type => [Object])
|
547
156
|
parent = double("FooObject")
|
548
|
-
property.cast(parent, ["2010-06-01", "2010-06-02"]).class.
|
157
|
+
expect(property.cast(parent, ["2010-06-01", "2010-06-02"]).class).to eql(CouchRest::Model::CastedArray)
|
549
158
|
end
|
550
159
|
|
551
160
|
it "should set a CastedArray on array of Strings" do
|
552
161
|
property = CouchRest::Model::Property.new(:test, :type => [String])
|
553
162
|
parent = double("FooObject")
|
554
|
-
property.cast(parent, ["2010-06-01", "2010-06-02"]).class.
|
163
|
+
expect(property.cast(parent, ["2010-06-01", "2010-06-02"]).class).to eql(CouchRest::Model::CastedArray)
|
555
164
|
end
|
556
165
|
|
557
166
|
it "should allow instantion of model via CastedArray#build" do
|
@@ -559,11 +168,11 @@ describe "Property Class" do
|
|
559
168
|
parent = Article.new
|
560
169
|
ary = property.cast(parent, [])
|
561
170
|
obj = ary.build(2011, 05, 21)
|
562
|
-
ary.length.
|
563
|
-
ary.first.
|
171
|
+
expect(ary.length).to eql(1)
|
172
|
+
expect(ary.first).to eql(Date.new(2011, 05, 21))
|
564
173
|
obj = ary.build(2011, 05, 22)
|
565
|
-
ary.length.
|
566
|
-
ary.last.
|
174
|
+
expect(ary.length).to eql(2)
|
175
|
+
expect(ary.last).to eql(Date.new(2011, 05, 22))
|
567
176
|
end
|
568
177
|
|
569
178
|
it "should cast an object that provides an array" do
|
@@ -575,20 +184,20 @@ describe "Property Class" do
|
|
575
184
|
property = CouchRest::Model::Property.new(:test, :type => prop)
|
576
185
|
parent = double("FooClass")
|
577
186
|
cast = property.cast(parent, [1, 2])
|
578
|
-
cast.ary.
|
187
|
+
expect(cast.ary).to eql([1, 2])
|
579
188
|
end
|
580
189
|
|
581
190
|
it "should set parent as casted_by object in CastedArray" do
|
582
191
|
property = CouchRest::Model::Property.new(:test, :type => [Object])
|
583
192
|
parent = double("FooObject")
|
584
|
-
property.cast(parent, ["2010-06-01", "2010-06-02"]).casted_by.
|
193
|
+
expect(property.cast(parent, ["2010-06-01", "2010-06-02"]).casted_by).to eql(parent)
|
585
194
|
end
|
586
195
|
|
587
196
|
it "should set casted_by on new value" do
|
588
197
|
property = CouchRest::Model::Property.new(:test, :type => CatToy)
|
589
198
|
parent = double("CatObject")
|
590
199
|
cast = property.cast(parent, {:name => 'catnip'})
|
591
|
-
cast.casted_by.
|
200
|
+
expect(cast.casted_by).to eql(parent)
|
592
201
|
end
|
593
202
|
|
594
203
|
end
|