dm-core 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +27 -0
  4. data/LICENSE +20 -0
  5. data/{README.txt → README.rdoc} +14 -3
  6. data/Rakefile +23 -22
  7. data/VERSION +1 -0
  8. data/dm-core.gemspec +201 -10
  9. data/lib/dm-core.rb +32 -23
  10. data/lib/dm-core/adapters.rb +0 -1
  11. data/lib/dm-core/adapters/data_objects_adapter.rb +230 -151
  12. data/lib/dm-core/adapters/mysql_adapter.rb +7 -8
  13. data/lib/dm-core/adapters/oracle_adapter.rb +39 -59
  14. data/lib/dm-core/adapters/postgres_adapter.rb +0 -1
  15. data/lib/dm-core/adapters/sqlite3_adapter.rb +5 -0
  16. data/lib/dm-core/adapters/sqlserver_adapter.rb +114 -0
  17. data/lib/dm-core/adapters/yaml_adapter.rb +0 -5
  18. data/lib/dm-core/associations/many_to_many.rb +118 -56
  19. data/lib/dm-core/associations/many_to_one.rb +48 -21
  20. data/lib/dm-core/associations/one_to_many.rb +8 -30
  21. data/lib/dm-core/associations/one_to_one.rb +1 -5
  22. data/lib/dm-core/associations/relationship.rb +89 -97
  23. data/lib/dm-core/collection.rb +299 -184
  24. data/lib/dm-core/core_ext/enumerable.rb +28 -0
  25. data/lib/dm-core/core_ext/kernel.rb +0 -2
  26. data/lib/dm-core/migrations.rb +314 -170
  27. data/lib/dm-core/model.rb +97 -66
  28. data/lib/dm-core/model/descendant_set.rb +1 -1
  29. data/lib/dm-core/model/hook.rb +0 -3
  30. data/lib/dm-core/model/property.rb +7 -10
  31. data/lib/dm-core/model/relationship.rb +79 -26
  32. data/lib/dm-core/model/scope.rb +3 -4
  33. data/lib/dm-core/property.rb +152 -90
  34. data/lib/dm-core/property_set.rb +18 -37
  35. data/lib/dm-core/query.rb +452 -153
  36. data/lib/dm-core/query/conditions/comparison.rb +266 -173
  37. data/lib/dm-core/query/conditions/operation.rb +499 -57
  38. data/lib/dm-core/query/direction.rb +0 -3
  39. data/lib/dm-core/query/operator.rb +0 -4
  40. data/lib/dm-core/query/path.rb +10 -12
  41. data/lib/dm-core/query/sort.rb +4 -10
  42. data/lib/dm-core/repository.rb +10 -6
  43. data/lib/dm-core/resource.rb +343 -148
  44. data/lib/dm-core/spec/adapter_shared_spec.rb +17 -1
  45. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +277 -17
  46. data/lib/dm-core/support/chainable.rb +0 -2
  47. data/lib/dm-core/support/equalizer.rb +27 -3
  48. data/lib/dm-core/transaction.rb +75 -75
  49. data/lib/dm-core/type.rb +19 -5
  50. data/lib/dm-core/types/discriminator.rb +4 -4
  51. data/lib/dm-core/types/object.rb +2 -7
  52. data/lib/dm-core/types/paranoid_boolean.rb +8 -2
  53. data/lib/dm-core/types/paranoid_datetime.rb +8 -2
  54. data/lib/dm-core/version.rb +1 -1
  55. data/script/performance.rb +7 -7
  56. data/script/profile.rb +6 -6
  57. data/spec/lib/collection_helpers.rb +2 -2
  58. data/spec/lib/pending_helpers.rb +22 -3
  59. data/spec/lib/rspec_immediate_feedback_formatter.rb +1 -0
  60. data/spec/public/associations/many_to_many_spec.rb +6 -4
  61. data/spec/public/associations/many_to_one_spec.rb +10 -1
  62. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +39 -0
  63. data/spec/public/associations/one_to_many_spec.rb +4 -3
  64. data/spec/public/associations/one_to_one_spec.rb +19 -1
  65. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +45 -0
  66. data/spec/public/collection_spec.rb +4 -3
  67. data/spec/public/migrations_spec.rb +144 -0
  68. data/spec/public/model/relationship_spec.rb +115 -55
  69. data/spec/public/model_spec.rb +13 -13
  70. data/spec/public/property/object_spec.rb +106 -0
  71. data/spec/public/property_spec.rb +18 -14
  72. data/spec/public/resource_spec.rb +10 -1
  73. data/spec/public/sel_spec.rb +16 -49
  74. data/spec/public/setup_spec.rb +1 -1
  75. data/spec/public/shared/association_collection_shared_spec.rb +6 -14
  76. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  77. data/spec/public/shared/collection_shared_spec.rb +214 -217
  78. data/spec/public/shared/finder_shared_spec.rb +259 -365
  79. data/spec/public/shared/resource_shared_spec.rb +524 -248
  80. data/spec/public/transaction_spec.rb +27 -3
  81. data/spec/public/types/discriminator_spec.rb +1 -1
  82. data/spec/rcov.opts +6 -0
  83. data/spec/semipublic/adapters/sqlserver_adapter_spec.rb +17 -0
  84. data/spec/semipublic/associations/many_to_one_spec.rb +3 -20
  85. data/spec/semipublic/associations_spec.rb +2 -2
  86. data/spec/semipublic/collection_spec.rb +0 -32
  87. data/spec/semipublic/model_spec.rb +96 -0
  88. data/spec/semipublic/property_spec.rb +3 -3
  89. data/spec/semipublic/query/conditions/comparison_spec.rb +1719 -0
  90. data/spec/semipublic/query/conditions/operation_spec.rb +1292 -0
  91. data/spec/semipublic/query_spec.rb +1285 -144
  92. data/spec/semipublic/resource_spec.rb +0 -24
  93. data/spec/semipublic/shared/resource_shared_spec.rb +103 -38
  94. data/spec/spec.opts +1 -1
  95. data/spec/spec_helper.rb +15 -6
  96. data/tasks/ci.rake +1 -0
  97. data/tasks/metrics.rake +37 -0
  98. data/tasks/spec.rake +41 -0
  99. data/tasks/yard.rake +9 -0
  100. data/tasks/yardstick.rake +19 -0
  101. metadata +99 -29
  102. data/CONTRIBUTING +0 -51
  103. data/FAQ +0 -93
  104. data/History.txt +0 -27
  105. data/MIT-LICENSE +0 -22
  106. data/Manifest.txt +0 -121
  107. data/QUICKLINKS +0 -11
  108. data/SPECS +0 -35
  109. data/TODO +0 -1
  110. data/spec/semipublic/query/conditions_spec.rb +0 -528
  111. data/tasks/ci.rb +0 -24
  112. data/tasks/dm.rb +0 -58
  113. data/tasks/doc.rb +0 -17
  114. data/tasks/gemspec.rb +0 -23
  115. data/tasks/hoe.rb +0 -45
  116. data/tasks/install.rb +0 -18
@@ -25,10 +25,10 @@ share_examples_for 'A public Resource' do
25
25
  it { @user.should respond_to(method) }
26
26
 
27
27
  describe "##{method}" do
28
- describe 'when comparing to the same object' do
28
+ describe 'when comparing to the same resource' do
29
29
  before :all do
30
30
  @other = @user
31
- @return = @user.send(method, @other)
31
+ @return = @user.__send__(method, @other)
32
32
  end
33
33
 
34
34
  it 'should return true' do
@@ -36,10 +36,10 @@ share_examples_for 'A public Resource' do
36
36
  end
37
37
  end
38
38
 
39
- describe 'when comparing to an object that does not respond to model' do
39
+ describe 'when comparing to an resource that does not respond to resource methods' do
40
40
  before :all do
41
41
  @other = Object.new
42
- @return = @user.send(method, @other)
42
+ @return = @user.__send__(method, @other)
43
43
  end
44
44
 
45
45
  it 'should return false' do
@@ -49,9 +49,9 @@ share_examples_for 'A public Resource' do
49
49
 
50
50
  describe 'when comparing to a resource with the same properties, but the model is a subclass' do
51
51
  before :all do
52
- rescue_if 'TODO', @skip do
52
+ rescue_if @skip do
53
53
  @other = @author_model.new(@user.attributes)
54
- @return = @user.send(method, @other)
54
+ @return = @user.__send__(method, @other)
55
55
  end
56
56
  end
57
57
 
@@ -62,9 +62,9 @@ share_examples_for 'A public Resource' do
62
62
 
63
63
  describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
64
64
  before :all do
65
- rescue_if 'TODO', @skip do
65
+ rescue_if @skip do
66
66
  @other = @user_model.get(*@user.key)
67
- @return = @user.send(method, @other)
67
+ @return = @user.__send__(method, @other)
68
68
  end
69
69
  end
70
70
 
@@ -75,10 +75,10 @@ share_examples_for 'A public Resource' do
75
75
 
76
76
  describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
77
77
  before :all do
78
- rescue_if 'TODO', @skip do
78
+ rescue_if @skip do
79
79
  @user.age = 20
80
80
  @other = @user_model.get(*@user.key)
81
- @return = @user.send(method, @other)
81
+ @return = @user.__send__(method, @other)
82
82
  end
83
83
  end
84
84
 
@@ -89,9 +89,9 @@ share_examples_for 'A public Resource' do
89
89
 
90
90
  describe 'when comparing to a resource with the same properties' do
91
91
  before :all do
92
- rescue_if 'TODO', @skip do
92
+ rescue_if @skip do
93
93
  @other = @user_model.new(@user.attributes)
94
- @return = @user.send(method, @other)
94
+ @return = @user.__send__(method, @other)
95
95
  end
96
96
  end
97
97
 
@@ -103,9 +103,9 @@ share_examples_for 'A public Resource' do
103
103
  with_alternate_adapter do
104
104
  describe 'when comparing to a resource with a different repository, but the same properties' do
105
105
  before :all do
106
- rescue_if 'TODO', @skip do
106
+ rescue_if @skip do
107
107
  @other = @alternate_repository.scope { @user_model.create(@user.attributes) }
108
- @return = @user.send(method, @other)
108
+ @return = @user.__send__(method, @other)
109
109
  end
110
110
  end
111
111
 
@@ -122,7 +122,7 @@ share_examples_for 'A public Resource' do
122
122
  describe '#<=>' do
123
123
  describe 'when the default order properties are equal with another resource' do
124
124
  before :all do
125
- rescue_if 'TODO', @skip do
125
+ rescue_if @skip do
126
126
  @other = @user_model.new(:name => 'dbussink')
127
127
  @return = @user <=> @other
128
128
  end
@@ -135,7 +135,7 @@ share_examples_for 'A public Resource' do
135
135
 
136
136
  describe 'when the default order property values are sorted before another resource' do
137
137
  before :all do
138
- rescue_if 'TODO', @skip do
138
+ rescue_if @skip do
139
139
  @other = @user_model.new(:name => 'c')
140
140
  @return = @user <=> @other
141
141
  end
@@ -148,7 +148,7 @@ share_examples_for 'A public Resource' do
148
148
 
149
149
  describe 'when the default order property values are sorted after another resource' do
150
150
  before :all do
151
- rescue_if 'TODO', @skip do
151
+ rescue_if @skip do
152
152
  @other = @user_model.new(:name => 'e')
153
153
  @return = @user <=> @other
154
154
  end
@@ -187,11 +187,23 @@ share_examples_for 'A public Resource' do
187
187
  it { @user.should respond_to(:attributes) }
188
188
 
189
189
  describe '#attributes' do
190
+ describe 'with a new resource' do
191
+ before :all do
192
+ rescue_if @skip do
193
+ @user = @user.model.new
194
+ end
195
+ end
190
196
 
191
- it 'should return the expected values' do
192
- @user.attributes.only(:name, :description, :age).should == { :name => 'dbussink', :description => 'Test', :age => 25 }
197
+ it 'should return the expected values' do
198
+ @user.attributes.should == {}
199
+ end
193
200
  end
194
201
 
202
+ describe 'with a saved resource' do
203
+ it 'should return the expected values' do
204
+ @user.attributes.only(:name, :description, :age).should == { :name => 'dbussink', :description => 'Test', :age => 25 }
205
+ end
206
+ end
195
207
  end
196
208
 
197
209
  it { @user.should respond_to(:attributes=) }
@@ -199,7 +211,7 @@ share_examples_for 'A public Resource' do
199
211
  describe '#attributes=' do
200
212
  describe 'when a public mutator is specified' do
201
213
  before :all do
202
- rescue_if 'TODO', @skip do
214
+ rescue_if @skip do
203
215
  @user.attributes = { :name => 'dkubb' }
204
216
  end
205
217
  end
@@ -222,26 +234,26 @@ share_examples_for 'A public Resource' do
222
234
  it { @user.should respond_to(:destroy) }
223
235
 
224
236
  describe "##{method}" do
225
- describe 'on a single object' do
237
+ describe 'on a single resource' do
226
238
  before :all do
227
239
  @resource = @user_model.create(:name => 'hacker', :age => 20, :comment => @comment)
228
240
 
229
- @return = @resource.send(method)
241
+ @return = @resource.__send__(method)
230
242
  end
231
243
 
232
244
  it 'should successfully remove a resource' do
233
245
  @return.should be_true
234
246
  end
235
247
 
236
- it 'should freeze the destroyed resource' do
237
- @resource.should be_frozen
248
+ it 'should mark the destroyed resource as readonly' do
249
+ @resource.should be_readonly
238
250
  end
239
251
 
240
252
  it "should return true when calling #{method} on a destroyed resource" do
241
- @resource.send(method).should be_true
253
+ @resource.__send__(method).should be_true
242
254
  end
243
255
 
244
- it 'should remove object from persistent storage' do
256
+ it 'should remove resource from persistent storage' do
245
257
  @user_model.get(*@resource.key).should be_nil
246
258
  end
247
259
  end
@@ -252,10 +264,131 @@ share_examples_for 'A public Resource' do
252
264
  end
253
265
  end
254
266
 
267
+ it { @user.should respond_to(:dirty?) }
268
+
269
+ describe '#dirty?' do
270
+ describe 'on a record, with dirty attributes' do
271
+ before { @user.age = 100 }
272
+
273
+ it { @user.should be_dirty }
274
+ end
275
+
276
+ describe 'on a record, with no dirty attributes, and dirty parents' do
277
+ before :all do
278
+ rescue_if @skip do
279
+ @user.should_not be_dirty
280
+
281
+ parent = @user.parent = @user_model.new(:name => 'Parent')
282
+ parent.should be_dirty
283
+ end
284
+ end
285
+
286
+ it { @user.should be_dirty }
287
+ end
288
+
289
+ describe 'on a record, with no dirty attributes, and dirty children' do
290
+ before :all do
291
+ rescue_if @skip do
292
+ @user.should_not be_dirty
293
+
294
+ child = @user.children.new(:name => 'Child')
295
+ child.should be_dirty
296
+ end
297
+ end
298
+
299
+ it { @user.should be_dirty }
300
+ end
301
+
302
+ describe 'on a record, with no dirty attributes, and dirty siblings' do
303
+ before :all do
304
+ rescue_if @skip do
305
+ @user.should_not be_dirty
306
+
307
+ parent = @user_model.create(:name => 'Parent', :comment => @comment)
308
+ parent.should_not be_dirty
309
+
310
+ @user.update(:parent => parent)
311
+ @user.should_not be_dirty
312
+
313
+ sibling = parent.children.new(:name => 'Sibling')
314
+ sibling.should be_dirty
315
+ parent.should be_dirty
316
+ end
317
+ end
318
+
319
+ it { @user.should_not be_dirty }
320
+ end
321
+
322
+ describe 'on a saved record, with no dirty attributes' do
323
+ it { @user.should_not be_dirty }
324
+ end
325
+
326
+ describe 'on a new record, with no dirty attributes, no default attributes, and no identity field' do
327
+ before { @user = @user_model.new }
328
+
329
+ it { @user.should_not be_dirty }
330
+ end
331
+
332
+ describe 'on a new record, with no dirty attributes, no default attributes, and an identity field' do
333
+ before { @comment = @comment_model.new }
334
+
335
+ it { @comment.should be_dirty }
336
+ end
337
+
338
+ describe 'on a new record, with no dirty attributes, default attributes, and no identity field' do
339
+ before { @default = Default.new }
340
+
341
+ it { @default.should be_dirty }
342
+ end
343
+
344
+ describe 'on a record with itself as a parent (circular dependency)' do
345
+ before :all do
346
+ rescue_if @skip do
347
+ @user.parent = @user
348
+ end
349
+ end
350
+
351
+ it 'should not raise an exception' do
352
+ lambda {
353
+ @user.dirty?.should be_true
354
+ }.should_not raise_error(SystemStackError)
355
+ end
356
+ end
357
+
358
+ describe 'on a record with itself as a child (circular dependency)' do
359
+ before :all do
360
+ rescue_if @skip do
361
+ @user.children = [ @user ]
362
+ end
363
+ end
364
+
365
+ it 'should not raise an exception' do
366
+ lambda {
367
+ @user.dirty?.should be_true
368
+ }.should_not raise_error(SystemStackError)
369
+ end
370
+ end
371
+
372
+ describe 'on a record with a parent as a child (circular dependency)' do
373
+ before :all do
374
+ rescue_if @skip do
375
+ @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
376
+ @user.save.should be_true
377
+ end
378
+ end
379
+
380
+ it 'should not raise an exception' do
381
+ lambda {
382
+ @user.dirty?.should be_true
383
+ }.should_not raise_error(SystemStackError)
384
+ end
385
+ end
386
+ end
387
+
255
388
  it { @user.should respond_to(:eql?) }
256
389
 
257
390
  describe '#eql?' do
258
- describe 'when comparing to the same object' do
391
+ describe 'when comparing to the same resource' do
259
392
  before :all do
260
393
  @other = @user
261
394
  @return = @user.eql?(@other)
@@ -266,7 +399,7 @@ share_examples_for 'A public Resource' do
266
399
  end
267
400
  end
268
401
 
269
- describe 'when comparing to an object that does not respond to model' do
402
+ describe 'when comparing to an resource that does not respond to model' do
270
403
  before :all do
271
404
  @other = Object.new
272
405
  @return = @user.eql?(@other)
@@ -279,7 +412,7 @@ share_examples_for 'A public Resource' do
279
412
 
280
413
  describe 'when comparing to a resource with the same properties, but the model is a subclass' do
281
414
  before :all do
282
- rescue_if 'TODO', @skip do
415
+ rescue_if @skip do
283
416
  @other = @author_model.new(@user.attributes)
284
417
  @return = @user.eql?(@other)
285
418
  end
@@ -292,8 +425,8 @@ share_examples_for 'A public Resource' do
292
425
 
293
426
  describe 'when comparing to a resource with a different key' do
294
427
  before :all do
295
- @other = @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment)
296
- @return = @user.eql?(@other)
428
+ @other = @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment)
429
+ @return = @user.eql?(@other)
297
430
  end
298
431
 
299
432
  it 'should return false' do
@@ -303,7 +436,7 @@ share_examples_for 'A public Resource' do
303
436
 
304
437
  describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
305
438
  before :all do
306
- rescue_if 'TODO', @skip do
439
+ rescue_if @skip do
307
440
  @other = @user_model.get(*@user.key)
308
441
  @return = @user.eql?(@other)
309
442
  end
@@ -316,7 +449,7 @@ share_examples_for 'A public Resource' do
316
449
 
317
450
  describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
318
451
  before :all do
319
- rescue_if 'TODO', @skip do
452
+ rescue_if @skip do
320
453
  @user.age = 20
321
454
  @other = @user_model.get(*@user.key)
322
455
  @return = @user.eql?(@other)
@@ -330,7 +463,7 @@ share_examples_for 'A public Resource' do
330
463
 
331
464
  describe 'when comparing to a resource with the same properties' do
332
465
  before :all do
333
- rescue_if 'TODO', @skip do
466
+ rescue_if @skip do
334
467
  @other = @user_model.new(@user.attributes)
335
468
  @return = @user.eql?(@other)
336
469
  end
@@ -344,7 +477,7 @@ share_examples_for 'A public Resource' do
344
477
  with_alternate_adapter do
345
478
  describe 'when comparing to a resource with a different repository, but the same properties' do
346
479
  before :all do
347
- rescue_if 'TODO', @skip do
480
+ rescue_if @skip do
348
481
  @other = @alternate_repository.scope { @user_model.create(@user.attributes) }
349
482
  @return = @user.eql?(@other)
350
483
  end
@@ -362,7 +495,7 @@ share_examples_for 'A public Resource' do
362
495
  describe '#inspect' do
363
496
 
364
497
  before :all do
365
- rescue_if 'TODO', @skip do
498
+ rescue_if @skip do
366
499
  @user = @user_model.get(*@user.key)
367
500
  @inspected = @user.inspect
368
501
  end
@@ -383,7 +516,7 @@ share_examples_for 'A public Resource' do
383
516
  describe '#key' do
384
517
 
385
518
  before :all do
386
- rescue_if 'TODO', @skip do
519
+ rescue_if @skip do
387
520
  @key = @user.key
388
521
  @user.name = 'dkubb'
389
522
  end
@@ -422,328 +555,471 @@ share_examples_for 'A public Resource' do
422
555
  it { @user.should respond_to(:reload) }
423
556
 
424
557
  describe '#reload' do
558
+ before do
559
+ # reset the user for each spec
560
+ rescue_if(@skip) do
561
+ @user.update(:name => 'dbussink', :age => 25, :description => 'Test')
562
+ end
563
+ end
425
564
 
426
- describe 'for a single object' do
565
+ subject { rescue_if(@skip) { @user.reload } }
427
566
 
428
- before :all do
429
- rescue_if 'TODO', @skip do
430
- @user.name = 'dkubb'
431
- @user.description = 'test'
432
- @user.reload
433
- end
567
+ describe 'on a resource not persisted' do
568
+ before do
569
+ @user.attributes = { :description => 'Changed' }
434
570
  end
435
571
 
436
- it { @user.name.should eql('dbussink') }
572
+ it { should be_kind_of(DataMapper::Resource) }
437
573
 
438
- it 'should also reload previously loaded attributes' do
439
- @user.attribute_loaded?(:description).should be_true
440
- end
441
- end
574
+ it { should equal(@user) }
442
575
 
443
- describe 'for when the object is changed outside another object' do
576
+ it { should be_clean }
444
577
 
445
- before :all do
446
- rescue_if 'TODO', @skip do
447
- @user2 = @user.dup
448
- @user2.description = 'Changed'
449
- @user2.save
450
- @user.reload
451
- end
578
+ it 'reset the changed attributes' do
579
+ method(:subject).should change(@user, :description).from('Changed').to('Test')
452
580
  end
581
+ end
453
582
 
454
- it 'should reload the object from the data store' do
455
- @user.description.should eql('Changed')
583
+ describe 'on a resource where the key is changed, but not persisted' do
584
+ before do
585
+ @user.attributes = { :name => 'dkubb' }
456
586
  end
457
587
 
588
+ it { should be_kind_of(DataMapper::Resource) }
589
+
590
+ it { should equal(@user) }
591
+
592
+ it { should be_clean }
593
+
594
+ it 'reset the changed attributes' do
595
+ method(:subject).should change(@user, :name).from('dkubb').to('dbussink')
596
+ end
458
597
  end
459
598
 
460
- end
599
+ describe 'on a resource that is changed outside another resource' do
600
+ before do
601
+ rescue_if @skip do
602
+ @user.dup.update(:description => 'Changed')
603
+ end
604
+ end
461
605
 
462
- it { @user.should respond_to(:save) }
606
+ it { should be_kind_of(DataMapper::Resource) }
463
607
 
464
- describe '#save' do
608
+ it { should equal(@user) }
465
609
 
466
- describe 'on a new, not dirty object' do
610
+ it { should be_clean }
467
611
 
468
- before :all do
469
- @user = @user_model.new
470
- @return = @user.save
612
+ it 'should reload the resource from the data store' do
613
+ method(:subject).should change(@user, :description).from('Test').to('Changed')
471
614
  end
615
+ end
472
616
 
473
- it 'should return false' do
474
- @return.should be_false
617
+ describe 'on an anonymous resource' do
618
+ before do
619
+ rescue_if @skip do
620
+ @user = @user.class.first(:fields => [ :description ])
621
+ @user.description.should == 'Test'
622
+ end
475
623
  end
476
624
 
477
- end
625
+ it { should be_kind_of(DataMapper::Resource) }
478
626
 
479
- describe 'on a not new, not dirty object' do
627
+ it { should equal(@user) }
480
628
 
481
- it 'should return true even when resource is not dirty' do
482
- @user.save.should be_true
483
- end
629
+ it { should be_clean }
484
630
 
631
+ it 'should not reload any attributes' do
632
+ method(:subject).should_not change(@user, :attributes)
633
+ end
485
634
  end
635
+ end
486
636
 
487
- describe 'on a not new, dirty object' do
637
+ it { @user.should respond_to(:readonly?) }
488
638
 
639
+ describe '#readonly?' do
640
+ describe 'on a new resource' do
489
641
  before :all do
490
- rescue_if 'TODO', @skip do
491
- @user.age = 26
492
- @return = @user.save
642
+ rescue_if @skip do
643
+ @user = @user.model.new
493
644
  end
494
645
  end
495
646
 
496
- it 'should save a resource succesfully when dirty' do
497
- @return.should be_true
647
+ it 'should return false' do
648
+ @user.readonly?.should be_false
649
+ end
650
+ end
651
+
652
+ describe 'on a saved resource' do
653
+ before :all do
654
+ rescue_if @skip do
655
+ @user.should be_saved
656
+ end
498
657
  end
499
658
 
500
- it 'should actually store the changes to persistent storage' do
501
- @user.attributes.should == @user.reload.attributes
659
+ it 'should return false' do
660
+ @user.readonly?.should be_false
502
661
  end
503
662
  end
504
663
 
505
- describe 'on a dirty invalid object' do
664
+ describe 'on a destroyed resource' do
506
665
  before :all do
507
- rescue_if 'TODO', @skip do
508
- @user.name = nil
666
+ rescue_if @skip do
667
+ @user.destroy.should be_true
509
668
  end
510
669
  end
511
670
 
512
- it 'should not save an invalid resource' do
513
- @user.save.should be_false
671
+ it 'should return true' do
672
+ @user.readonly?.should be_true
514
673
  end
515
674
  end
516
675
 
517
- describe 'with new resources in a has relationship' do
518
- before do
519
- rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
520
- @initial_comments = @user.comments.size
521
- @first_comment = @user.comments.new(:body => "DM is great!")
522
- @second_comment = @comment_model.new(:user => @user, :body => "is it really?")
523
- @return = @user.save
676
+ describe 'on an anonymous resource' do
677
+ before :all do
678
+ rescue_if @skip do
679
+ # load the user without a key
680
+ @user = @user.model.first(:fields => @user_model.properties - @user_model.key)
524
681
  end
525
682
  end
526
683
 
527
- it 'should save resource' do
528
- pending_if 'TODO', !@user.respond_to?(:comments) do
529
- @return.should be_true
530
- end
684
+ it 'should return true' do
685
+ @user.readonly?.should be_true
531
686
  end
687
+ end
688
+ end
689
+
690
+ [ :save, :save! ].each do |method|
691
+ it { @user.should respond_to(method) }
532
692
 
533
- it 'should save the first resource created through new' do
534
- pending_if 'TODO', !@user.respond_to?(:comments) do
535
- @first_comment.new?.should be_false
693
+ describe "##{method}" do
694
+ describe 'on a new, not dirty resource' do
695
+ before :all do
696
+ @user = @user_model.new
697
+ @return = @user.__send__(method)
536
698
  end
537
- end
538
699
 
539
- it 'should save the correct foreign key for the first resource' do
540
- pending_if 'TODO', !@user.respond_to?(:comments) do
541
- @first_comment.user.should eql(@user)
700
+ it 'should return false' do
701
+ @return.should be_false
542
702
  end
543
703
  end
544
704
 
545
- it 'should save the second resource created through the constructor' do
546
- pending "Changing a belongs_to parent should add the object to the correct association" do
547
- @second_comment.new?.should be_false
705
+ describe 'on a not new, not dirty resource' do
706
+ it 'should return true even when resource is not dirty' do
707
+ @user.__send__(method).should be_true
548
708
  end
549
709
  end
550
710
 
551
- it 'should save the correct foreign key for the second resource' do
552
- pending_if 'TODO', !@user.respond_to?(:comments) do
553
- @second_comment.user.should eql(@user)
711
+ describe 'on a not new, dirty resource' do
712
+ before :all do
713
+ rescue_if @skip do
714
+ @user.age = 26
715
+ @return = @user.__send__(method)
716
+ end
717
+ end
718
+
719
+ it 'should save a resource succesfully when dirty' do
720
+ @return.should be_true
721
+ end
722
+
723
+ it 'should actually store the changes to persistent storage' do
724
+ @user.attributes.should == @user.reload.attributes
554
725
  end
555
726
  end
556
727
 
557
- it 'should create 2 extra resources in persistent storage' do
558
- pending "Changing a belongs_to parent should add the object to the correct association" do
559
- @user.comments.size.should == @initial_comments + 2
728
+ describe 'on a dirty invalid resource' do
729
+ before :all do
730
+ rescue_if @skip do
731
+ @user.name = nil
732
+ end
733
+ end
734
+
735
+ it 'should not save an invalid resource' do
736
+ @user.__send__(method).should be_false
560
737
  end
561
738
  end
562
739
 
563
- end
740
+ describe 'with new resources in a has relationship' do
741
+ before do
742
+ rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
743
+ @initial_comments = @user.comments.size
744
+ @first_comment = @user.comments.new(:body => "DM is great!")
745
+ @second_comment = @comment_model.new(:user => @user, :body => "is it really?")
746
+ @return = @user.__send__(method)
747
+ end
748
+ end
564
749
 
565
- describe 'with dirty resources in a has relationship' do
566
- before :all do
567
- rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
568
- @initial_comments = @user.comments.size
569
- @first_comment = @user.comments.create(:body => "DM is great!")
570
- @second_comment = @comment_model.create(:user => @user, :body => "is it really?")
750
+ it 'should save resource' do
751
+ pending_if !@user.respond_to?(:comments) do
752
+ @return.should be_true
753
+ end
754
+ end
571
755
 
572
- @first_comment.body = "It still has rough edges"
573
- @second_comment.body = "But these cool specs help fixing that"
574
- @second_comment.user = @user_model.create(:name => 'dkubb')
575
- @return = @user.save
756
+ it 'should save the first resource created through new' do
757
+ pending_if !@user.respond_to?(:comments) do
758
+ @first_comment.new?.should be_false
759
+ end
576
760
  end
577
- end
578
761
 
579
- it 'should save the dirty resources' do
580
- pending_if 'TODO', !@user.respond_to?(:comments) do
581
- @return.should be_true
762
+ it 'should save the correct foreign key for the first resource' do
763
+ pending_if !@user.respond_to?(:comments) do
764
+ @first_comment.user.should eql(@user)
765
+ end
766
+ end
767
+
768
+ it 'should save the second resource created through the constructor' do
769
+ pending "Changing a belongs_to parent should add the resource to the correct association" do
770
+ @second_comment.new?.should be_false
771
+ end
582
772
  end
583
- end
584
773
 
585
- it 'should have saved the first child resource' do
586
- pending_if 'TODO', !@user.respond_to?(:comments) do
587
- @first_comment.should_not be_dirty
774
+ it 'should save the correct foreign key for the second resource' do
775
+ pending_if !@user.respond_to?(:comments) do
776
+ @second_comment.user.should eql(@user)
777
+ end
588
778
  end
589
- end
590
779
 
591
- it 'should not have saved the second child resource' do
592
- pending_if 'TODO', !@user.respond_to?(:comments) do
593
- @second_comment.should be_dirty
780
+ it 'should create 2 extra resources in persistent storage' do
781
+ pending "Changing a belongs_to parent should add the resource to the correct association" do
782
+ @user.comments.size.should == @initial_comments + 2
783
+ end
594
784
  end
595
785
  end
596
786
 
597
- end
787
+ describe 'with dirty resources in a has relationship' do
788
+ before :all do
789
+ rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
790
+ @first_comment = @user.comments.create(:body => 'DM is great!')
791
+ @second_comment = @comment_model.create(:user => @user, :body => 'is it really?')
598
792
 
599
- describe 'with a new dependency' do
793
+ @first_comment.body = 'It still has rough edges'
794
+ @second_comment.body = 'But these cool specs help fixing that'
795
+ @second_comment.user = @user_model.create(:name => 'dkubb')
600
796
 
601
- before :all do
602
- @first_comment = @comment_model.new(:body => "DM is great!")
603
- @first_comment.user = @user_model.new(:name => 'dkubb')
797
+ @return = @user.__send__(method)
798
+ end
799
+ end
800
+
801
+ it 'should return true' do
802
+ pending_if !@user.respond_to?(:comments) do
803
+ @return.should be_true
804
+ end
805
+ end
806
+
807
+ it 'should not be dirty' do
808
+ @user.should_not be_dirty
809
+ end
810
+
811
+ it 'should have saved the first child resource' do
812
+ pending_if !@user.respond_to?(:comments) do
813
+ @first_comment.model.get(*@first_comment.key).body.should == 'It still has rough edges'
814
+ end
815
+ end
816
+
817
+ it 'should not have saved the second child resource' do
818
+ pending_if !@user.respond_to?(:comments) do
819
+ @second_comment.model.get(*@second_comment.key).body.should == 'is it really?'
820
+ end
821
+ end
604
822
  end
605
823
 
606
- it 'should not raise an exception when saving the resource' do
607
- pending do
608
- lambda { @first_comment.save.should be_false }.should_not raise_error
824
+ describe 'with a new dependency' do
825
+ before :all do
826
+ @first_comment = @comment_model.new(:body => "DM is great!")
827
+ @first_comment.user = @user_model.new(:name => 'dkubb')
828
+ end
829
+
830
+ it 'should not raise an exception when saving the resource' do
831
+ pending do
832
+ lambda { @first_comment.send(method).should be_false }.should_not raise_error
833
+ end
609
834
  end
610
835
  end
611
836
 
612
- end
837
+ describe 'with a dirty dependency' do
838
+ before :all do
839
+ rescue_if @skip do
840
+ @user.name = 'dbussink-the-second'
613
841
 
614
- describe 'with a dirty dependency' do
615
- before :all do
616
- rescue_if 'TODO', @skip do
617
- @user.name = 'dbussink-the-second'
842
+ @first_comment = @comment_model.new(:body => 'DM is great!')
843
+ @first_comment.user = @user
618
844
 
619
- @first_comment = @comment_model.new(:body => 'DM is great!')
620
- @first_comment.user = @user
845
+ @return = @first_comment.__send__(method)
846
+ end
847
+ end
621
848
 
622
- @return = @first_comment.save
849
+ it 'should succesfully save the resource' do
850
+ @return.should be_true
623
851
  end
624
- end
625
852
 
626
- it 'should succesfully save the object' do
627
- @return.should be_true
628
- end
853
+ it 'should not have a dirty dependency' do
854
+ @user.should_not be_dirty
855
+ end
629
856
 
630
- it 'should not have a dirty dependency' do
631
- @user.should_not be_dirty
857
+ it 'should succesfully save the dependency' do
858
+ @user.attributes.should == @user_model.get(*@user.key).attributes
859
+ end
632
860
  end
633
861
 
634
- it 'should succesfully save the dependency' do
635
- @user.attributes.should == @user_model.get(*@user.key).attributes
636
- end
862
+ describe 'with a new resource and new relations' do
863
+ before :all do
864
+ @article = @article_model.new(:body => "Main")
865
+ rescue_if 'TODO: fix for one to one association', (!@article.respond_to?(:paragraphs)) do
866
+ @paragraph = @article.paragraphs.new(:text => 'Content')
637
867
 
638
- end
868
+ @article.__send__(method)
869
+ end
870
+ end
639
871
 
640
- describe 'with a new object and new relations' do
641
- before :all do
642
- @article = @article_model.new(:body => "Main")
643
- rescue_if 'TODO: fix for one to one association', (!@article.respond_to?(:paragraphs)) do
644
- @paragraph = @article.paragraphs.new(:text => 'Content')
872
+ it 'should not be dirty' do
873
+ pending_if !@article.respond_to?(:paragraphs) do
874
+ @article.should_not be_dirty
875
+ end
876
+ end
645
877
 
646
- @article.save
878
+ it 'should not be dirty' do
879
+ pending_if !@article.respond_to?(:paragraphs) do
880
+ @paragraph.should_not be_dirty
881
+ end
647
882
  end
648
- end
649
883
 
650
- it 'should not be dirty' do
651
- pending_if 'TODO', !@article.respond_to?(:paragraphs) do
652
- @article.should_not be_dirty
884
+ it 'should set the related resource' do
885
+ pending_if !@article.respond_to?(:paragraphs) do
886
+ @paragraph.article.should == @article
887
+ end
653
888
  end
654
- end
655
889
 
656
- it 'should not be dirty' do
657
- pending_if 'TODO', !@article.respond_to?(:paragraphs) do
658
- @paragraph.should_not be_dirty
890
+ it 'should set the foreign key properly' do
891
+ pending_if !@article.respond_to?(:paragraphs) do
892
+ @paragraph.article_id.should == @article.id
893
+ end
659
894
  end
660
895
  end
661
896
 
662
- it 'should set the related object' do
663
- pending_if 'TODO', !@article.respond_to?(:paragraphs) do
664
- @paragraph.article.should == @article
897
+ describe 'with a dirty resource with a changed key' do
898
+ before :all do
899
+ rescue_if @skip do
900
+ @original_key = @user.key
901
+ @user.name = 'dkubb'
902
+ @return = @user.__send__(method)
903
+ end
665
904
  end
666
- end
667
905
 
668
- it 'should set the foreign key properly' do
669
- pending_if 'TODO', !@article.respond_to?(:paragraphs) do
670
- @paragraph.article_id.should == @article.id
906
+ it 'should save a resource succesfully when dirty' do
907
+ @return.should be_true
671
908
  end
672
- end
673
- end
674
909
 
675
- describe 'with a dirty object with a changed key' do
910
+ it 'should actually store the changes to persistent storage' do
911
+ @user.name.should == @user.reload.name
912
+ end
676
913
 
677
- before :all do
678
- rescue_if 'TODO', @skip do
679
- @original_key = @user.key
680
- @user.name = 'dkubb'
681
- @return = @user.save
914
+ it 'should update the identity map' do
915
+ @user.repository.identity_map(@user_model).should have_key(%w[ dkubb ])
682
916
  end
683
- end
684
917
 
685
- it 'should save a resource succesfully when dirty' do
686
- @return.should be_true
918
+ it 'should remove the old entry from the identity map' do
919
+ @user.repository.identity_map(@user_model).should_not have_key(@original_key)
920
+ end
687
921
  end
688
922
 
689
- it 'should actually store the changes to persistent storage' do
690
- @user.name.should == @user.reload.name
691
- end
923
+ describe 'on a new resource with unsaved parent and grandparent' do
924
+ before :all do
925
+ @grandparent = @user_model.new(:name => 'dkubb', :comment => @comment)
926
+ @parent = @user_model.new(:name => 'ashleymoran', :comment => @comment, :referrer => @grandparent)
927
+ @child = @user_model.new(:name => 'mrship', :comment => @comment, :referrer => @parent)
692
928
 
693
- it 'should update the identity map' do
694
- @user.repository.identity_map(@user_model).should have_key(%w[ dkubb ])
695
- end
929
+ @response = @child.__send__(method)
930
+ end
696
931
 
697
- it 'should remove the old entry from the identity map' do
698
- @user.repository.identity_map(@user_model).should_not have_key(@original_key)
699
- end
932
+ it 'should return true' do
933
+ @response.should be_true
934
+ end
700
935
 
701
- end
936
+ it 'should save the child' do
937
+ @child.should be_saved
938
+ end
702
939
 
703
- describe 'on a new object with unsaved parent and grandparent' do
704
- before :all do
705
- @grandparent = @user_model.new(:name => 'dkubb', :comment => @comment)
706
- @parent = @user_model.new(:name => 'ashleymoran', :comment => @comment, :referrer => @grandparent)
707
- @child = @user_model.new(:name => 'mrship', :comment => @comment, :referrer => @parent)
940
+ it 'should save the parent' do
941
+ @parent.should be_saved
942
+ end
708
943
 
709
- @response = @child.save
710
- end
944
+ it 'should save the grandparent' do
945
+ @grandparent.should be_saved
946
+ end
711
947
 
712
- it 'should return true' do
713
- @response.should be_true
714
- end
948
+ it 'should relate the child to the parent' do
949
+ pending_if @one_to_one_through do
950
+ @child.model.get(*@child.key).referrer.should == @parent
951
+ end
952
+ end
715
953
 
716
- it 'should save the child' do
717
- @child.should be_saved
718
- end
954
+ it 'should relate the parent to the grandparent' do
955
+ pending_if @one_to_one_through do
956
+ @parent.model.get(*@parent.key).referrer.should == @grandparent
957
+ end
958
+ end
719
959
 
720
- it 'should save the parent' do
721
- @parent.should be_saved
960
+ it 'should relate the grandparent to nothing' do
961
+ pending_if @one_to_one_through do
962
+ @grandparent.model.get(*@grandparent.key).referrer.should be_nil
963
+ end
964
+ end
722
965
  end
723
966
 
724
- it 'should save the grandparent' do
725
- @grandparent.should be_saved
967
+ describe 'on a destroyed resource' do
968
+ before :all do
969
+ rescue_if @skip do
970
+ @user.destroy
971
+ end
972
+ end
973
+
974
+ it 'should raise an exception' do
975
+ lambda {
976
+ @user.__send__(method)
977
+ }.should raise_error(DataMapper::PersistenceError, "#{@user.model}##{method} cannot be called on a destroyed resource")
978
+ end
726
979
  end
727
980
 
728
- it 'should relate the child to the parent' do
729
- pending_if 'TODO', @one_to_one_through do
730
- @child.model.get(*@child.key).referrer.should == @parent
981
+ describe 'on a record with itself as a parent (circular dependency)' do
982
+ before :all do
983
+ rescue_if @skip do
984
+ @user.parent = @user
985
+ end
986
+ end
987
+
988
+ it 'should not raise an exception' do
989
+ lambda {
990
+ @user.__send__(method).should be_true
991
+ }.should_not raise_error(SystemStackError)
731
992
  end
732
993
  end
733
994
 
734
- it 'should relate the parent to the grandparent' do
735
- pending_if 'TODO', @one_to_one_through do
736
- @parent.model.get(*@parent.key).referrer.should == @grandparent
995
+ describe 'on a record with itself as a child (circular dependency)' do
996
+ before :all do
997
+ rescue_if @skip do
998
+ @user.children = [ @user ]
999
+ end
1000
+ end
1001
+
1002
+ it 'should not raise an exception' do
1003
+ lambda {
1004
+ @user.__send__(method).should be_true
1005
+ }.should_not raise_error(SystemStackError)
737
1006
  end
738
1007
  end
739
1008
 
740
- it 'should relate the grandparent to nothing' do
741
- pending_if 'TODO', @one_to_one_through do
742
- @grandparent.model.get(*@grandparent.key).referrer.should be_nil
1009
+ describe 'on a record with a parent as a child (circular dependency)' do
1010
+ before :all do
1011
+ rescue_if @skip do
1012
+ @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
1013
+ end
1014
+ end
1015
+
1016
+ it 'should not raise an exception' do
1017
+ lambda {
1018
+ @user.__send__(method).should be_true
1019
+ }.should_not raise_error(SystemStackError)
743
1020
  end
744
1021
  end
745
1022
  end
746
-
747
1023
  end
748
1024
 
749
1025
  it { @user.should respond_to(:saved?) }
@@ -772,8 +1048,8 @@ share_examples_for 'A public Resource' do
772
1048
  describe "##{method}" do
773
1049
  describe 'with no arguments' do
774
1050
  before :all do
775
- rescue_if 'TODO', @skip do
776
- @return = @user.send(method)
1051
+ rescue_if @skip do
1052
+ @return = @user.__send__(method)
777
1053
  end
778
1054
  end
779
1055
 
@@ -784,9 +1060,9 @@ share_examples_for 'A public Resource' do
784
1060
 
785
1061
  describe 'with attributes' do
786
1062
  before :all do
787
- rescue_if 'TODO', @skip do
1063
+ rescue_if @skip do
788
1064
  @attributes = { :description => 'Changed' }
789
- @return = @user.send(method, @attributes)
1065
+ @return = @user.__send__(method, @attributes)
790
1066
  end
791
1067
  end
792
1068
 
@@ -795,12 +1071,12 @@ share_examples_for 'A public Resource' do
795
1071
  end
796
1072
 
797
1073
  it 'should update attributes of Resource' do
798
- @attributes.each { |key, value| @user.send(key).should == value }
1074
+ @attributes.each { |key, value| @user.__send__(key).should == value }
799
1075
  end
800
1076
 
801
1077
  it 'should persist the changes' do
802
1078
  resource = @user_model.get(*@user.key)
803
- @attributes.each { |key, value| resource.send(key).should == value }
1079
+ @attributes.each { |key, value| resource.__send__(key).should == value }
804
1080
  end
805
1081
  end
806
1082
 
@@ -808,34 +1084,34 @@ share_examples_for 'A public Resource' do
808
1084
  before :all do
809
1085
  rescue_if 'Use table aliases to avoid ambiguous named in query', @one_to_one_through do
810
1086
  @attributes = { :referrer => @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment) }
811
- @return = @user.send(method, @attributes)
1087
+ @return = @user.__send__(method, @attributes)
812
1088
  end
813
1089
  end
814
1090
 
815
1091
  it 'should return true' do
816
- pending_if 'TODO', @one_to_one_through do
1092
+ pending_if @one_to_one_through do
817
1093
  @return.should be_true
818
1094
  end
819
1095
  end
820
1096
 
821
1097
  it 'should update attributes of Resource' do
822
- pending_if 'TODO', @one_to_one_through do
823
- @attributes.each { |key, value| @user.send(key).should == value }
1098
+ pending_if @one_to_one_through do
1099
+ @attributes.each { |key, value| @user.__send__(key).should == value }
824
1100
  end
825
1101
  end
826
1102
 
827
1103
  it 'should persist the changes' do
828
- pending_if 'TODO', @one_to_one_through do
1104
+ pending_if @one_to_one_through do
829
1105
  resource = @user_model.get(*@user.key)
830
- @attributes.each { |key, value| resource.send(key).should == value }
1106
+ @attributes.each { |key, value| resource.__send__(key).should == value }
831
1107
  end
832
1108
  end
833
1109
  end
834
1110
 
835
1111
  describe 'with attributes where a value is nil for a property that does not allow nil' do
836
1112
  before :all do
837
- rescue_if 'TODO', @skip do
838
- @return = @user.send(method, :name => nil)
1113
+ rescue_if @skip do
1114
+ @return = @user.__send__(method, :name => nil)
839
1115
  end
840
1116
  end
841
1117
 
@@ -850,14 +1126,14 @@ share_examples_for 'A public Resource' do
850
1126
 
851
1127
  describe 'on a dirty resource' do
852
1128
  before :all do
853
- rescue_if 'TODO', @skip do
1129
+ rescue_if @skip do
854
1130
  @user.age = 99
855
1131
  end
856
1132
  end
857
1133
 
858
1134
  it 'should raise an exception' do
859
1135
  lambda {
860
- @user.send(method, :admin => true)
1136
+ @user.__send__(method, :admin => true)
861
1137
  }.should raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a dirty resource")
862
1138
  end
863
1139
  end
@@ -897,7 +1173,7 @@ share_examples_for 'A public Resource' do
897
1173
 
898
1174
  describe 'lazy loading' do
899
1175
  before :all do
900
- rescue_if 'TODO', @skip do
1176
+ rescue_if @skip do
901
1177
  @user.name = 'dkubb'
902
1178
  @user.age = 33
903
1179
  @user.summary = 'Programmer'