couchrest_model 1.0.0.beta8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+