couchrest_model 1.0.0.beta8 → 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.
Files changed (44) hide show
  1. data/.gitignore +9 -0
  2. data/{spec/spec.opts → .rspec} +0 -1
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +77 -0
  5. data/README.md +144 -57
  6. data/Rakefile +12 -43
  7. data/VERSION +1 -0
  8. data/couchrest_model.gemspec +35 -0
  9. data/history.txt +23 -1
  10. data/init.rb +1 -0
  11. data/lib/couchrest/model/associations.rb +17 -1
  12. data/lib/couchrest/model/base.rb +5 -5
  13. data/lib/couchrest/model/casted_model.rb +2 -2
  14. data/lib/couchrest/model/class_proxy.rb +6 -0
  15. data/lib/couchrest/model/collection.rb +3 -0
  16. data/lib/couchrest/model/configuration.rb +51 -0
  17. data/lib/couchrest/model/design_doc.rb +2 -5
  18. data/lib/couchrest/model/document_queries.rb +20 -3
  19. data/lib/couchrest/model/persistence.rb +15 -1
  20. data/lib/couchrest/model/properties.rb +97 -23
  21. data/lib/couchrest/model/property.rb +5 -4
  22. data/lib/couchrest/model/property_protection.rb +71 -0
  23. data/lib/couchrest/model/typecast.rb +12 -7
  24. data/lib/couchrest/model/view.rb +190 -0
  25. data/lib/couchrest/model/views.rb +3 -3
  26. data/lib/couchrest/model.rb +1 -1
  27. data/lib/couchrest/railtie.rb +3 -2
  28. data/lib/couchrest_model.rb +7 -14
  29. data/lib/rails/generators/couchrest_model/model/model_generator.rb +2 -1
  30. data/spec/.gitignore +1 -0
  31. data/spec/couchrest/base_spec.rb +3 -3
  32. data/spec/couchrest/casted_model_spec.rb +63 -49
  33. data/spec/couchrest/class_proxy_spec.rb +6 -0
  34. data/spec/couchrest/configuration_spec.rb +78 -0
  35. data/spec/couchrest/persistence_spec.rb +10 -4
  36. data/spec/couchrest/{attribute_protection_spec.rb → property_protection_spec.rb} +29 -2
  37. data/spec/couchrest/property_spec.rb +61 -0
  38. data/spec/couchrest/subclass_spec.rb +2 -2
  39. data/spec/couchrest/view_spec.rb +6 -0
  40. data/spec/fixtures/more/article.rb +1 -1
  41. data/spec/spec_helper.rb +4 -3
  42. metadata +96 -32
  43. data/lib/couchrest/model/attribute_protection.rb +0 -74
  44. data/lib/couchrest/model/attributes.rb +0 -75
@@ -31,11 +31,11 @@ class WithCastedCallBackModel < Hash
31
31
  property :name
32
32
  property :run_before_validate
33
33
  property :run_after_validate
34
-
34
+
35
35
  before_validate do |object|
36
36
  object.run_before_validate = true
37
37
  end
38
- after_validate do |object|
38
+ after_validate do |object|
39
39
  object.run_after_validate = true
40
40
  end
41
41
  end
@@ -47,7 +47,7 @@ class CastedCallbackDoc < CouchRest::Model::Base
47
47
  end
48
48
 
49
49
  describe CouchRest::Model::CastedModel do
50
-
50
+
51
51
  describe "A non hash class including CastedModel" do
52
52
  it "should fail raising and include error" do
53
53
  lambda do
@@ -55,27 +55,27 @@ describe CouchRest::Model::CastedModel do
55
55
  include CouchRest::CastedModel
56
56
  property :name
57
57
  end
58
-
58
+
59
59
  end.should raise_error
60
60
  end
61
61
  end
62
-
62
+
63
63
  describe "isolated" do
64
64
  before(:each) do
65
65
  @obj = WithCastedModelMixin.new
66
66
  end
67
67
  it "should automatically include the property mixin and define getters and setters" do
68
68
  @obj.name = 'Matt'
69
- @obj.name.should == 'Matt'
69
+ @obj.name.should == 'Matt'
70
70
  end
71
-
71
+
72
72
  it "should allow override of default" do
73
73
  @obj = WithCastedModelMixin.new(:name => 'Eric', :details => {'color' => 'orange'})
74
74
  @obj.name.should == 'Eric'
75
75
  @obj.details['color'].should == 'orange'
76
76
  end
77
77
  end
78
-
78
+
79
79
  describe "casted as an attribute, but without a value" do
80
80
  before(:each) do
81
81
  @obj = DummyModel.new
@@ -99,22 +99,22 @@ describe CouchRest::Model::CastedModel do
99
99
  @obj.sub_models.first.title.should eql('test')
100
100
  end
101
101
  end
102
-
102
+
103
103
  describe "casted as attribute" do
104
104
  before(:each) do
105
105
  casted = {:name => 'not whatever'}
106
106
  @obj = DummyModel.new(:casted_attribute => {:name => 'whatever', :casted_attribute => casted})
107
107
  @casted_obj = @obj.casted_attribute
108
108
  end
109
-
109
+
110
110
  it "should be available from its parent" do
111
111
  @casted_obj.should be_an_instance_of(WithCastedModelMixin)
112
112
  end
113
-
113
+
114
114
  it "should have the getters defined" do
115
115
  @casted_obj.name.should == 'whatever'
116
116
  end
117
-
117
+
118
118
  it "should know who casted it" do
119
119
  @casted_obj.casted_by.should == @obj
120
120
  end
@@ -126,27 +126,32 @@ describe CouchRest::Model::CastedModel do
126
126
  it "should return nil for the unknown attribute" do
127
127
  @casted_obj["unknown"].should be_nil
128
128
  end
129
-
129
+
130
130
  it "should return {} for the hash attribute" do
131
131
  @casted_obj.details.should == {}
132
132
  end
133
-
133
+
134
134
  it "should cast its own attributes" do
135
135
  @casted_obj.casted_attribute.should be_instance_of(WithCastedModelMixin)
136
136
  end
137
+
138
+ it "should raise an error if save or update_attributes called" do
139
+ expect { @casted_obj.casted_attribute.save }.to raise_error(NoMethodError)
140
+ expect { @casted_obj.casted_attribute.update_attributes(:name => "Fubar") }.to raise_error(NoMethodError)
141
+ end
137
142
  end
138
-
143
+
139
144
  describe "casted as an array of a different type" do
140
145
  before(:each) do
141
146
  @obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé'])
142
147
  end
143
-
148
+
144
149
  it "should cast the array properly" do
145
150
  @obj.keywords.should be_an_instance_of(Array)
146
151
  @obj.keywords.first.should == 'couch'
147
152
  end
148
153
  end
149
-
154
+
150
155
  describe "update attributes without saving" do
151
156
  before(:each) do
152
157
  @question = Question.new(:q => "What is your quest?", :a => "To seek the Holy Grail")
@@ -158,20 +163,20 @@ describe CouchRest::Model::CastedModel do
158
163
  @question['q'].should == "What is your favorite color?"
159
164
  @question.a.should == "Blue"
160
165
  end
161
-
166
+
162
167
  it "should also work for attributes= alias" do
163
168
  @question.respond_to?(:attributes=).should be_true
164
169
  @question.attributes = {:q => "What is your favorite color?", 'a' => "Blue"}
165
170
  @question['q'].should == "What is your favorite color?"
166
171
  @question.a.should == "Blue"
167
172
  end
168
-
173
+
169
174
  it "should flip out if an attribute= method is missing" do
170
175
  lambda {
171
176
  @q.update_attributes_without_saving('foo' => "something", :a => "No green")
172
177
  }.should raise_error(NoMethodError)
173
178
  end
174
-
179
+
175
180
  it "should not change any attributes if there is an error" do
176
181
  lambda {
177
182
  @q.update_attributes_without_saving('foo' => "something", :a => "No green")
@@ -179,8 +184,9 @@ describe CouchRest::Model::CastedModel do
179
184
  @question.q.should == "What is your quest?"
180
185
  @question.a.should == "To seek the Holy Grail"
181
186
  end
187
+
182
188
  end
183
-
189
+
184
190
  describe "saved document with casted models" do
185
191
  before(:each) do
186
192
  reset_test_db!
@@ -188,24 +194,24 @@ describe CouchRest::Model::CastedModel do
188
194
  @obj.save.should be_true
189
195
  @obj = DummyModel.get(@obj.id)
190
196
  end
191
-
197
+
192
198
  it "should be able to load with the casted models" do
193
199
  casted_obj = @obj.casted_attribute
194
200
  casted_obj.should_not be_nil
195
201
  casted_obj.should be_an_instance_of(WithCastedModelMixin)
196
202
  end
197
-
203
+
198
204
  it "should have defined getters for the casted model" do
199
205
  casted_obj = @obj.casted_attribute
200
206
  casted_obj.name.should == "whatever"
201
207
  end
202
-
208
+
203
209
  it "should have defined setters for the casted model" do
204
210
  casted_obj = @obj.casted_attribute
205
211
  casted_obj.name = "test"
206
212
  casted_obj.name.should == "test"
207
213
  end
208
-
214
+
209
215
  it "should retain an override of a casted model attribute's default" do
210
216
  casted_obj = @obj.casted_attribute
211
217
  casted_obj.details['color'] = 'orange'
@@ -213,7 +219,7 @@ describe CouchRest::Model::CastedModel do
213
219
  casted_obj = DummyModel.get(@obj.id).casted_attribute
214
220
  casted_obj.details['color'].should == 'orange'
215
221
  end
216
-
222
+
217
223
  end
218
224
 
219
225
  describe "saving document with array of casted models and validation" do
@@ -238,7 +244,7 @@ describe CouchRest::Model::CastedModel do
238
244
  @cat.should_not be_valid
239
245
  @cat.save.should be_false
240
246
  end
241
-
247
+
242
248
  it "should not fail if the casted model doesn't have validation" do
243
249
  Cat.property :masters, [Person], :default => []
244
250
  Cat.validates_presence_of :name
@@ -248,7 +254,7 @@ describe CouchRest::Model::CastedModel do
248
254
  cat.should be_valid
249
255
  end
250
256
  end
251
-
257
+
252
258
  describe "calling valid?" do
253
259
  before :each do
254
260
  @cat = Cat.new
@@ -259,7 +265,7 @@ describe CouchRest::Model::CastedModel do
259
265
  @cat.toys << @toy2
260
266
  @cat.toys << @toy3
261
267
  end
262
-
268
+
263
269
  describe "on the top document" do
264
270
  it "should put errors on all invalid casted models" do
265
271
  @cat.should_not be_valid
@@ -268,10 +274,10 @@ describe CouchRest::Model::CastedModel do
268
274
  @toy2.errors.should_not be_empty
269
275
  @toy3.errors.should_not be_empty
270
276
  end
271
-
277
+
272
278
  it "should not put errors on valid casted models" do
273
279
  @toy1.name = "Feather"
274
- @toy2.name = "Twine"
280
+ @toy2.name = "Twine"
275
281
  @cat.should_not be_valid
276
282
  @cat.errors.should_not be_empty
277
283
  @toy1.errors.should be_empty
@@ -279,7 +285,7 @@ describe CouchRest::Model::CastedModel do
279
285
  @toy3.errors.should_not be_empty
280
286
  end
281
287
  end
282
-
288
+
283
289
  describe "on a casted model property" do
284
290
  it "should only validate itself" do
285
291
  @toy1.should_not be_valid
@@ -289,7 +295,7 @@ describe CouchRest::Model::CastedModel do
289
295
  @toy3.errors.should be_empty
290
296
  end
291
297
  end
292
-
298
+
293
299
  describe "on a casted model inside a casted collection" do
294
300
  it "should only validate itself" do
295
301
  @toy2.should_not be_valid
@@ -300,7 +306,7 @@ describe CouchRest::Model::CastedModel do
300
306
  end
301
307
  end
302
308
  end
303
-
309
+
304
310
  describe "calling new? on a casted model" do
305
311
  before :each do
306
312
  reset_test_db!
@@ -309,18 +315,18 @@ describe CouchRest::Model::CastedModel do
309
315
  @cat.favorite_toy = @favorite_toy
310
316
  @cat.toys << CatToy.new(:name => 'Fuzzy Stick')
311
317
  end
312
-
318
+
313
319
  it "should be true on new" do
314
320
  CatToy.new.should be_new
315
321
  CatToy.new.new_record?.should be_true
316
322
  end
317
-
323
+
318
324
  it "should be true after assignment" do
319
325
  @cat.should be_new
320
326
  @cat.favorite_toy.should be_new
321
327
  @cat.toys.first.should be_new
322
328
  end
323
-
329
+
324
330
  it "should not be true after create or save" do
325
331
  @cat.create
326
332
  @cat.save
@@ -328,14 +334,14 @@ describe CouchRest::Model::CastedModel do
328
334
  @cat.toys.first.casted_by.should eql(@cat)
329
335
  @cat.toys.first.should_not be_new
330
336
  end
331
-
337
+
332
338
  it "should not be true after get from the database" do
333
339
  @cat.save
334
340
  @cat = Cat.get(@cat.id)
335
341
  @cat.favorite_toy.should_not be_new
336
342
  @cat.toys.first.should_not be_new
337
343
  end
338
-
344
+
339
345
  it "should still be true after a failed create or save" do
340
346
  @cat.name = nil
341
347
  @cat.create.should be_false
@@ -344,7 +350,7 @@ describe CouchRest::Model::CastedModel do
344
350
  @cat.toys.first.should be_new
345
351
  end
346
352
  end
347
-
353
+
348
354
  describe "calling base_doc from a nested casted model" do
349
355
  before :each do
350
356
  @course = Course.new(:title => 'Science 101')
@@ -357,7 +363,15 @@ describe CouchRest::Model::CastedModel do
357
363
  @cat.favorite_toy = @toy1
358
364
  @cat.toys << @toy2
359
365
  end
360
-
366
+
367
+ it 'should let you copy over casted arrays' do
368
+ question = Question.new
369
+ @course.questions << question
370
+ new_course = Course.new
371
+ new_course.questions = @course.questions
372
+ new_course.questions.should include(question)
373
+ end
374
+
361
375
  it "should reference the top document for" do
362
376
  @course.base_doc.should === @course
363
377
  @professor.casted_by.should === @course
@@ -366,19 +380,19 @@ describe CouchRest::Model::CastedModel do
366
380
  @toy1.base_doc.should === @course
367
381
  @toy2.base_doc.should === @course
368
382
  end
369
-
383
+
370
384
  it "should call setter on top document" do
371
385
  @toy1.base_doc.should_not be_nil
372
386
  @toy1.base_doc.title = 'Tom Foolery'
373
387
  @course.title.should == 'Tom Foolery'
374
388
  end
375
-
389
+
376
390
  it "should return nil if not yet casted" do
377
391
  person = Person.new
378
392
  person.base_doc.should == nil
379
393
  end
380
394
  end
381
-
395
+
382
396
  describe "calling base_doc.save from a nested casted model" do
383
397
  before :each do
384
398
  reset_test_db!
@@ -386,13 +400,13 @@ describe CouchRest::Model::CastedModel do
386
400
  @toy = CatToy.new
387
401
  @cat.favorite_toy = @toy
388
402
  end
389
-
403
+
390
404
  it "should not save parent document when casted model is invalid" do
391
405
  @toy.should_not be_valid
392
406
  @toy.base_doc.save.should be_false
393
407
  lambda{@toy.base_doc.save!}.should raise_error
394
408
  end
395
-
409
+
396
410
  it "should save parent document when nested casted model is valid" do
397
411
  @toy.name = "Mr Squeaks"
398
412
  @toy.should be_valid
@@ -400,14 +414,14 @@ describe CouchRest::Model::CastedModel do
400
414
  lambda{@toy.base_doc.save!}.should_not raise_error
401
415
  end
402
416
  end
403
-
417
+
404
418
  describe "callbacks" do
405
419
  before(:each) do
406
420
  @doc = CastedCallbackDoc.new
407
421
  @model = WithCastedCallBackModel.new
408
422
  @doc.callback_model = @model
409
423
  end
410
-
424
+
411
425
  describe "validate" do
412
426
  it "should run before_validate before validating" do
413
427
  @model.run_before_validate.should be_nil
@@ -418,7 +432,7 @@ describe CouchRest::Model::CastedModel do
418
432
  @model.run_after_validate.should be_nil
419
433
  @model.should be_valid
420
434
  @model.run_after_validate.should be_true
421
- end
435
+ end
422
436
  end
423
437
  end
424
438
  end
@@ -87,6 +87,12 @@ describe "Proxy Class" do
87
87
  u = @us.first
88
88
  u.title.should =~ /\A...\z/
89
89
  end
90
+
91
+ it "should get last" do
92
+ u = @us.last
93
+ u.title.should == "aaa"
94
+ end
95
+
90
96
  it "should set database on first retreived document" do
91
97
  u = @us.first
92
98
  u.database.should === DB
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../../spec_helper', __FILE__)
3
+ require File.join(FIXTURE_PATH, 'more', 'cat')
4
+
5
+ describe CouchRest::Model::Base do
6
+
7
+ before do
8
+ @class = Class.new(CouchRest::Model::Base)
9
+ end
10
+
11
+ describe '.configure' do
12
+ it "should set a configuration parameter" do
13
+ @class.add_config :foo_bar
14
+ @class.configure do |config|
15
+ config.foo_bar = 'monkey'
16
+ end
17
+ @class.foo_bar.should == 'monkey'
18
+ end
19
+ end
20
+
21
+ describe '.add_config' do
22
+
23
+ it "should add a class level accessor" do
24
+ @class.add_config :foo_bar
25
+ @class.foo_bar = 'foo'
26
+ @class.foo_bar.should == 'foo'
27
+ end
28
+
29
+ ['foo', :foo, 45, ['foo', :bar]].each do |val|
30
+ it "should be inheritable for a #{val.class}" do
31
+ @class.add_config :foo_bar
32
+ @child_class = Class.new(@class)
33
+
34
+ @class.foo_bar = val
35
+ @class.foo_bar.should == val
36
+ @child_class.foo_bar.should == val
37
+
38
+ @child_class.foo_bar = "bar"
39
+ @child_class.foo_bar.should == "bar"
40
+
41
+ @class.foo_bar.should == val
42
+ end
43
+ end
44
+
45
+
46
+ it "should add an instance level accessor" do
47
+ @class.add_config :foo_bar
48
+ @class.foo_bar = 'foo'
49
+ @class.new.foo_bar.should == 'foo'
50
+ end
51
+
52
+ it "should add a convenient in-class setter" do
53
+ @class.add_config :foo_bar
54
+ @class.foo_bar "monkey"
55
+ @class.foo_bar.should == "monkey"
56
+ end
57
+ end
58
+
59
+ describe "General examples" do
60
+
61
+ before(:all) do
62
+ @default_model_key = 'model'
63
+ end
64
+
65
+
66
+ it "should be possible to override on class using configure method" do
67
+ default_model_key = Cat.model_type_key
68
+ Cat.instance_eval do
69
+ model_type_key 'cat-type'
70
+ end
71
+ CouchRest::Model::Base.model_type_key.should eql(default_model_key)
72
+ Cat.model_type_key.should eql('cat-type')
73
+ cat = Cat.new
74
+ cat.model_type_key.should eql('cat-type')
75
+ end
76
+ end
77
+
78
+ end
@@ -25,7 +25,7 @@ describe "Model Persistence" do
25
25
  end
26
26
 
27
27
  it "should instantialize document of different type" do
28
- doc = Article.create_from_database({'_id' => 'testitem2', '_rev' => 123, 'couchrest-type' => 'WithTemplateAndUniqueID', 'name' => 'my test'})
28
+ doc = Article.create_from_database({'_id' => 'testitem2', '_rev' => 123, Article.model_type_key => 'WithTemplateAndUniqueID', 'name' => 'my test'})
29
29
  doc.class.should eql(WithTemplateAndUniqueID)
30
30
  end
31
31
 
@@ -114,7 +114,7 @@ describe "Model Persistence" do
114
114
  end
115
115
 
116
116
  it "should set the type" do
117
- @sobj['couchrest-type'].should == 'Basic'
117
+ @sobj[@sobj.model_type_key].should == 'Basic'
118
118
  end
119
119
 
120
120
  it "should accept true or false on save for validation" do
@@ -282,11 +282,17 @@ describe "Model Persistence" do
282
282
  foundart = Article.get 'matt aimonetti'
283
283
  foundart.should be_nil
284
284
  end
285
+ it "should return nil if a blank id is requested" do
286
+ Article.get("").should be_nil
287
+ end
285
288
  it "should raise an error if `get!` is used and the document doesn't exist" do
286
- lambda{foundart = Article.get!('matt aimonetti')}.should raise_error
289
+ expect{ Article.get!('matt aimonetti') }.to raise_error
290
+ end
291
+ it "should raise an error if `get!` is requested with a blank id" do
292
+ expect{ Article.get!("") }.to raise_error
287
293
  end
288
294
  it "should raise an error if `find!` is used and the document doesn't exist" do
289
- lambda{foundart = Article.find!('matt aimonetti')}.should raise_error
295
+ expect{ Article.find!('matt aimonetti') }.to raise_error
290
296
  end
291
297
  end
292
298
 
@@ -34,6 +34,12 @@ describe "Model Attributes" do
34
34
  user.name.should == "will"
35
35
  user.phone.should == "555-5555"
36
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
37
43
  end
38
44
 
39
45
  describe "Model Base", "accessible flag" do
@@ -65,6 +71,12 @@ describe "Model Attributes" do
65
71
  user.name.should == "will"
66
72
  user.admin.should == false
67
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
68
80
  end
69
81
 
70
82
  describe "Model Base", "protected flag" do
@@ -96,6 +108,21 @@ describe "Model Attributes" do
96
108
  user.name.should == "will"
97
109
  user.admin.should == false
98
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
+
99
126
  end
100
127
 
101
128
  describe "Model Base", "mixing protected and accessible flags" do
@@ -107,7 +134,6 @@ describe "Model Attributes" do
107
134
  end
108
135
 
109
136
  it { expect { WithBothAndUnspecified.new }.to_not raise_error }
110
- it { expect { WithBothAndUnspecified.new(nil) }.to_not raise_error }
111
137
 
112
138
  it 'should assume that any unspecified property is protected by default' do
113
139
  user = WithBothAndUnspecified.new(:name => 'will', :admin => true, :phone => '555-1234')
@@ -116,6 +142,7 @@ describe "Model Attributes" do
116
142
  user.admin.should == false
117
143
  user.phone.should == 'unset phone number'
118
144
  end
145
+
119
146
  end
120
147
 
121
148
  describe "from database" do
@@ -151,7 +178,7 @@ describe "Model Attributes" do
151
178
  it "Base#all should not strip protected attributes" do
152
179
  # all creates a CollectionProxy
153
180
  docs = WithProtected.all(:key => @user.id)
154
- docs.size.should == 1
181
+ docs.length.should == 1
155
182
  reloaded = docs.first
156
183
  verify_attrs reloaded
157
184
  end
@@ -22,6 +22,11 @@ describe "Model properties" do
22
22
  @card.properties.map{|p| p.name}.should include("first_name")
23
23
  end
24
24
 
25
+ it "should list object properties with values" do
26
+ @card.properties_with_values.should be_an_instance_of(Hash)
27
+ @card.properties_with_values["first_name"].should == "matt"
28
+ end
29
+
25
30
  it "should let you access a property value (getter)" do
26
31
  @card.first_name.should == "matt"
27
32
  end
@@ -88,6 +93,7 @@ describe "Model properties" do
88
93
  expect { @card.write_attribute(:this_property_should_not_exist, 823) }.to raise_error(ArgumentError)
89
94
  end
90
95
 
96
+
91
97
  it "should let you use write_attribute on readonly properties" do
92
98
  lambda {
93
99
  @card.read_only_value = "foo"
@@ -109,6 +115,34 @@ describe "Model properties" do
109
115
  end
110
116
  end
111
117
 
118
+ describe "mass updating attributes without property" do
119
+
120
+ describe "when mass_assign_any_attribute false" do
121
+
122
+ it "should not allow them to be set" do
123
+ @card.attributes = {:test => 'fooobar'}
124
+ @card['test'].should be_nil
125
+ end
126
+
127
+ end
128
+
129
+ describe "when mass_assign_any_attribute true" do
130
+ before(:each) do
131
+ # dup Card class so that no other tests are effected
132
+ card_class = Card.dup
133
+ card_class.class_eval do
134
+ mass_assign_any_attribute true
135
+ end
136
+ @card = card_class.new(:first_name => 'Sam')
137
+ end
138
+
139
+ it 'should allow them to be updated' do
140
+ @card.attributes = {:test => 'fooobar'}
141
+ @card['test'].should eql('fooobar')
142
+ end
143
+ end
144
+ end
145
+
112
146
 
113
147
  describe "mass assignment protection" do
114
148
 
@@ -291,12 +325,28 @@ describe "Model properties" do
291
325
  @course['estimate'].should eql(-24.35)
292
326
  end
293
327
 
328
+ it 'return float of a number with commas instead of points for decimals' do
329
+ @course.estimate = '23,35'
330
+ @course['estimate'].should eql(23.35)
331
+ end
332
+
333
+ it "should handle numbers with commas and points" do
334
+ @course.estimate = '1,234.00'
335
+ @course.estimate.should eql(1234.00)
336
+ end
337
+
338
+ it "should handle a mis-match of commas and points and maintain the last one" do
339
+ @course.estimate = "1,232.434.123,323"
340
+ @course.estimate.should eql(1232434123.323)
341
+ end
342
+
294
343
  [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
295
344
  it "does not typecast non-numeric value #{value.inspect}" do
296
345
  @course.estimate = value
297
346
  @course['estimate'].should equal(value)
298
347
  end
299
348
  end
349
+
300
350
  end
301
351
 
302
352
  describe 'when type primitive is a Integer' do
@@ -571,6 +621,16 @@ describe "Model properties" do
571
621
  @course['ends_at'].min.should eql(t.min)
572
622
  @course['ends_at'].sec.should eql(t.sec)
573
623
  end
624
+ it 'parses the string without offset' do
625
+ t = Time.now
626
+ @course.ends_at = t.strftime("%Y-%m-%d %H:%M:%S")
627
+ @course['ends_at'].year.should eql(t.year)
628
+ @course['ends_at'].month.should eql(t.month)
629
+ @course['ends_at'].day.should eql(t.day)
630
+ @course['ends_at'].hour.should eql(t.hour)
631
+ @course['ends_at'].min.should eql(t.min)
632
+ @course['ends_at'].sec.should eql(t.sec)
633
+ end
574
634
  end
575
635
 
576
636
  it 'does not typecast non-time values' do
@@ -814,3 +874,4 @@ describe "Property Class" do
814
874
  end
815
875
 
816
876
  end
877
+