datamapper-dm-core 0.9.11 → 0.10.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 (192) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -39
  5. data/Manifest.txt +67 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +16 -15
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/dm-core.gemspec +11 -15
  12. data/lib/dm-core/adapters/abstract_adapter.rb +182 -185
  13. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  14. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  15. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  16. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  19. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  20. data/lib/dm-core/adapters.rb +135 -16
  21. data/lib/dm-core/associations/many_to_many.rb +372 -90
  22. data/lib/dm-core/associations/many_to_one.rb +220 -73
  23. data/lib/dm-core/associations/one_to_many.rb +319 -255
  24. data/lib/dm-core/associations/one_to_one.rb +66 -53
  25. data/lib/dm-core/associations/relationship.rb +560 -158
  26. data/lib/dm-core/collection.rb +1104 -381
  27. data/lib/dm-core/core_ext/kernel.rb +12 -0
  28. data/lib/dm-core/core_ext/symbol.rb +10 -0
  29. data/lib/dm-core/identity_map.rb +4 -34
  30. data/lib/dm-core/migrations.rb +1283 -0
  31. data/lib/dm-core/model/descendant_set.rb +81 -0
  32. data/lib/dm-core/model/hook.rb +45 -0
  33. data/lib/dm-core/model/is.rb +32 -0
  34. data/lib/dm-core/model/property.rb +248 -0
  35. data/lib/dm-core/model/relationship.rb +335 -0
  36. data/lib/dm-core/model/scope.rb +90 -0
  37. data/lib/dm-core/model.rb +570 -369
  38. data/lib/dm-core/property.rb +753 -280
  39. data/lib/dm-core/property_set.rb +141 -98
  40. data/lib/dm-core/query/conditions/comparison.rb +814 -0
  41. data/lib/dm-core/query/conditions/operation.rb +247 -0
  42. data/lib/dm-core/query/direction.rb +43 -0
  43. data/lib/dm-core/query/operator.rb +42 -0
  44. data/lib/dm-core/query/path.rb +102 -0
  45. data/lib/dm-core/query/sort.rb +45 -0
  46. data/lib/dm-core/query.rb +974 -492
  47. data/lib/dm-core/repository.rb +147 -107
  48. data/lib/dm-core/resource.rb +644 -429
  49. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  50. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  51. data/lib/dm-core/support/chainable.rb +20 -0
  52. data/lib/dm-core/support/deprecate.rb +12 -0
  53. data/lib/dm-core/support/equalizer.rb +23 -0
  54. data/lib/dm-core/support/logger.rb +13 -0
  55. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  56. data/lib/dm-core/transaction.rb +333 -92
  57. data/lib/dm-core/type.rb +98 -60
  58. data/lib/dm-core/types/boolean.rb +1 -1
  59. data/lib/dm-core/types/discriminator.rb +34 -20
  60. data/lib/dm-core/types/object.rb +7 -4
  61. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  62. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  63. data/lib/dm-core/types/serial.rb +3 -3
  64. data/lib/dm-core/types/text.rb +3 -4
  65. data/lib/dm-core/version.rb +1 -1
  66. data/lib/dm-core.rb +106 -110
  67. data/script/performance.rb +102 -109
  68. data/script/profile.rb +169 -38
  69. data/spec/lib/adapter_helpers.rb +105 -0
  70. data/spec/lib/collection_helpers.rb +18 -0
  71. data/spec/lib/counter_adapter.rb +34 -0
  72. data/spec/lib/pending_helpers.rb +27 -0
  73. data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
  74. data/spec/public/associations/many_to_many_spec.rb +193 -0
  75. data/spec/public/associations/many_to_one_spec.rb +73 -0
  76. data/spec/public/associations/one_to_many_spec.rb +77 -0
  77. data/spec/public/associations/one_to_one_spec.rb +156 -0
  78. data/spec/public/collection_spec.rb +65 -0
  79. data/spec/public/model/relationship_spec.rb +924 -0
  80. data/spec/public/model_spec.rb +159 -0
  81. data/spec/public/property_spec.rb +829 -0
  82. data/spec/public/resource_spec.rb +71 -0
  83. data/spec/public/sel_spec.rb +44 -0
  84. data/spec/public/setup_spec.rb +145 -0
  85. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  86. data/spec/public/shared/collection_shared_spec.rb +1723 -0
  87. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  88. data/spec/public/shared/resource_shared_spec.rb +924 -0
  89. data/spec/public/shared/sel_shared_spec.rb +112 -0
  90. data/spec/public/transaction_spec.rb +129 -0
  91. data/spec/public/types/discriminator_spec.rb +130 -0
  92. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  93. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  94. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  95. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  96. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  97. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  99. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  100. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  101. data/spec/semipublic/associations_spec.rb +177 -0
  102. data/spec/semipublic/collection_spec.rb +142 -0
  103. data/spec/semipublic/property_spec.rb +61 -0
  104. data/spec/semipublic/query/conditions_spec.rb +528 -0
  105. data/spec/semipublic/query/path_spec.rb +443 -0
  106. data/spec/semipublic/query_spec.rb +2626 -0
  107. data/spec/semipublic/resource_spec.rb +47 -0
  108. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  109. data/spec/spec.opts +3 -1
  110. data/spec/spec_helper.rb +80 -57
  111. data/tasks/ci.rb +19 -31
  112. data/tasks/dm.rb +43 -48
  113. data/tasks/doc.rb +8 -11
  114. data/tasks/gemspec.rb +5 -5
  115. data/tasks/hoe.rb +15 -16
  116. data/tasks/install.rb +8 -10
  117. metadata +72 -93
  118. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  119. data/lib/dm-core/associations.rb +0 -207
  120. data/lib/dm-core/auto_migrations.rb +0 -105
  121. data/lib/dm-core/dependency_queue.rb +0 -32
  122. data/lib/dm-core/hook.rb +0 -11
  123. data/lib/dm-core/is.rb +0 -16
  124. data/lib/dm-core/logger.rb +0 -232
  125. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  126. data/lib/dm-core/migrator.rb +0 -29
  127. data/lib/dm-core/scope.rb +0 -58
  128. data/lib/dm-core/support/array.rb +0 -13
  129. data/lib/dm-core/support/assertions.rb +0 -8
  130. data/lib/dm-core/support/errors.rb +0 -23
  131. data/lib/dm-core/support/kernel.rb +0 -11
  132. data/lib/dm-core/support/symbol.rb +0 -41
  133. data/lib/dm-core/support.rb +0 -7
  134. data/lib/dm-core/type_map.rb +0 -80
  135. data/lib/dm-core/types.rb +0 -19
  136. data/script/all +0 -4
  137. data/spec/integration/association_spec.rb +0 -1382
  138. data/spec/integration/association_through_spec.rb +0 -203
  139. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  140. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  141. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  142. data/spec/integration/auto_migrations_spec.rb +0 -413
  143. data/spec/integration/collection_spec.rb +0 -1073
  144. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  145. data/spec/integration/dependency_queue_spec.rb +0 -46
  146. data/spec/integration/model_spec.rb +0 -197
  147. data/spec/integration/mysql_adapter_spec.rb +0 -85
  148. data/spec/integration/postgres_adapter_spec.rb +0 -731
  149. data/spec/integration/property_spec.rb +0 -253
  150. data/spec/integration/query_spec.rb +0 -514
  151. data/spec/integration/repository_spec.rb +0 -61
  152. data/spec/integration/resource_spec.rb +0 -513
  153. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  154. data/spec/integration/sti_spec.rb +0 -273
  155. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  156. data/spec/integration/transaction_spec.rb +0 -75
  157. data/spec/integration/type_spec.rb +0 -275
  158. data/spec/lib/logging_helper.rb +0 -18
  159. data/spec/lib/mock_adapter.rb +0 -27
  160. data/spec/lib/model_loader.rb +0 -100
  161. data/spec/lib/publicize_methods.rb +0 -28
  162. data/spec/models/content.rb +0 -16
  163. data/spec/models/vehicles.rb +0 -34
  164. data/spec/models/zoo.rb +0 -48
  165. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  166. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  167. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  168. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  169. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  170. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  171. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  172. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  173. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  174. data/spec/unit/associations/relationship_spec.rb +0 -71
  175. data/spec/unit/associations_spec.rb +0 -242
  176. data/spec/unit/auto_migrations_spec.rb +0 -111
  177. data/spec/unit/collection_spec.rb +0 -182
  178. data/spec/unit/data_mapper_spec.rb +0 -35
  179. data/spec/unit/identity_map_spec.rb +0 -126
  180. data/spec/unit/is_spec.rb +0 -80
  181. data/spec/unit/migrator_spec.rb +0 -33
  182. data/spec/unit/model_spec.rb +0 -321
  183. data/spec/unit/naming_conventions_spec.rb +0 -36
  184. data/spec/unit/property_set_spec.rb +0 -90
  185. data/spec/unit/property_spec.rb +0 -753
  186. data/spec/unit/query_spec.rb +0 -571
  187. data/spec/unit/repository_spec.rb +0 -93
  188. data/spec/unit/resource_spec.rb +0 -649
  189. data/spec/unit/scope_spec.rb +0 -142
  190. data/spec/unit/transaction_spec.rb +0 -493
  191. data/spec/unit/type_map_spec.rb +0 -114
  192. data/spec/unit/type_spec.rb +0 -119
@@ -0,0 +1,924 @@
1
+ share_examples_for 'A public Resource' do
2
+ before :all do
3
+ @no_join = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter) ||
4
+ defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
5
+
6
+ relationship = @user_model.relationships[:referrer]
7
+ @one_to_one_through = relationship.kind_of?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
8
+
9
+ @skip = @no_join && @one_to_one_through
10
+ end
11
+
12
+ before :all do
13
+ unless @skip
14
+ %w[ @user_model @user @comment_model ].each do |ivar|
15
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
16
+ end
17
+ end
18
+ end
19
+
20
+ before do
21
+ pending if @skip
22
+ end
23
+
24
+ [ :==, :=== ].each do |method|
25
+ it { @user.should respond_to(method) }
26
+
27
+ describe "##{method}" do
28
+ describe 'when comparing to the same object' do
29
+ before :all do
30
+ @other = @user
31
+ @return = @user.send(method, @other)
32
+ end
33
+
34
+ it 'should return true' do
35
+ @return.should be_true
36
+ end
37
+ end
38
+
39
+ describe 'when comparing to an object that does not respond to model' do
40
+ before :all do
41
+ @other = Object.new
42
+ @return = @user.send(method, @other)
43
+ end
44
+
45
+ it 'should return false' do
46
+ @return.should be_false
47
+ end
48
+ end
49
+
50
+ describe 'when comparing to a resource with the same properties, but the model is a subclass' do
51
+ before :all do
52
+ rescue_if 'TODO', @skip do
53
+ @other = @author_model.new(@user.attributes)
54
+ @return = @user.send(method, @other)
55
+ end
56
+ end
57
+
58
+ it 'should return true' do
59
+ @return.should be_true
60
+ end
61
+ end
62
+
63
+ describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
64
+ before :all do
65
+ rescue_if 'TODO', @skip do
66
+ @other = @user_model.get(*@user.key)
67
+ @return = @user.send(method, @other)
68
+ end
69
+ end
70
+
71
+ it 'should return true' do
72
+ @return.should be_true
73
+ end
74
+ end
75
+
76
+ describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
77
+ before :all do
78
+ rescue_if 'TODO', @skip do
79
+ @user.age = 20
80
+ @other = @user_model.get(*@user.key)
81
+ @return = @user.send(method, @other)
82
+ end
83
+ end
84
+
85
+ it 'should return false' do
86
+ @return.should be_false
87
+ end
88
+ end
89
+
90
+ describe 'when comparing to a resource with the same properties' do
91
+ before :all do
92
+ rescue_if 'TODO', @skip do
93
+ @other = @user_model.new(@user.attributes)
94
+ @return = @user.send(method, @other)
95
+ end
96
+ end
97
+
98
+ it 'should return true' do
99
+ @return.should be_true
100
+ end
101
+ end
102
+
103
+ with_alternate_adapter do
104
+ describe 'when comparing to a resource with a different repository, but the same properties' do
105
+ before :all do
106
+ rescue_if 'TODO', @skip do
107
+ @other = @alternate_repository.scope { @user_model.create(@user.attributes) }
108
+ @return = @user.send(method, @other)
109
+ end
110
+ end
111
+
112
+ it 'should return true' do
113
+ @return.should be_true
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ it { @user.should respond_to(:<=>) }
121
+
122
+ describe '#<=>' do
123
+ describe 'when the default order properties are equal with another resource' do
124
+ before :all do
125
+ rescue_if 'TODO', @skip do
126
+ @other = @user_model.new(:name => 'dbussink')
127
+ @return = @user <=> @other
128
+ end
129
+ end
130
+
131
+ it 'should return 0' do
132
+ @return.should == 0
133
+ end
134
+ end
135
+
136
+ describe 'when the default order property values are sorted before another resource' do
137
+ before :all do
138
+ rescue_if 'TODO', @skip do
139
+ @other = @user_model.new(:name => 'c')
140
+ @return = @user <=> @other
141
+ end
142
+ end
143
+
144
+ it 'should return 1' do
145
+ @return.should == 1
146
+ end
147
+ end
148
+
149
+ describe 'when the default order property values are sorted after another resource' do
150
+ before :all do
151
+ rescue_if 'TODO', @skip do
152
+ @other = @user_model.new(:name => 'e')
153
+ @return = @user <=> @other
154
+ end
155
+ end
156
+
157
+ it 'should return -1' do
158
+ @return.should == -1
159
+ end
160
+ end
161
+
162
+ describe 'when comparing an unrelated type of Object' do
163
+ it 'should raise an exception' do
164
+ lambda { @user <=> @comment_model.new }.should raise_error(ArgumentError, "Cannot compare a #{@comment_model} instance with a #{@user_model} instance")
165
+ end
166
+ end
167
+ end
168
+
169
+ it { @user.should respond_to(:attribute_get) }
170
+
171
+ describe '#attribute_get' do
172
+
173
+ it { @user.attribute_get(:name).should == 'dbussink' }
174
+
175
+ end
176
+
177
+ it { @user.should respond_to(:attribute_set) }
178
+
179
+ describe '#attribute_set' do
180
+
181
+ before { @user.attribute_set(:name, 'dkubb') }
182
+
183
+ it { @user.name.should == 'dkubb' }
184
+
185
+ end
186
+
187
+ it { @user.should respond_to(:attributes) }
188
+
189
+ describe '#attributes' do
190
+
191
+ it 'should return the expected values' do
192
+ @user.attributes.only(:name, :description, :age).should == { :name => 'dbussink', :description => 'Test', :age => 25 }
193
+ end
194
+
195
+ end
196
+
197
+ it { @user.should respond_to(:attributes=) }
198
+
199
+ describe '#attributes=' do
200
+ describe 'when a public mutator is specified' do
201
+ before :all do
202
+ rescue_if 'TODO', @skip do
203
+ @user.attributes = { :name => 'dkubb' }
204
+ end
205
+ end
206
+
207
+ it 'should set the value' do
208
+ @user.name.should eql('dkubb')
209
+ end
210
+ end
211
+
212
+ describe 'when a non-public mutator is specified' do
213
+ it 'should raise an exception' do
214
+ lambda {
215
+ @user.attributes = { :admin => true }
216
+ }.should raise_error(ArgumentError, "The attribute \'admin\' is not accessible in #{@user_model}")
217
+ end
218
+ end
219
+ end
220
+
221
+ [ :destroy, :destroy! ].each do |method|
222
+ it { @user.should respond_to(:destroy) }
223
+
224
+ describe "##{method}" do
225
+ describe 'on a single object' do
226
+ before :all do
227
+ @resource = @user_model.create(:name => 'hacker', :age => 20, :comment => @comment)
228
+
229
+ @return = @resource.send(method)
230
+ end
231
+
232
+ it 'should successfully remove a resource' do
233
+ @return.should be_true
234
+ end
235
+
236
+ it 'should freeze the destroyed resource' do
237
+ @resource.should be_frozen
238
+ end
239
+
240
+ it 'should not be able to remove an already removed resource' do
241
+ @resource.destroy.should be_false
242
+ end
243
+
244
+ it 'should remove object from persistent storage' do
245
+ @user_model.get(*@resource.key).should be_nil
246
+ end
247
+ end
248
+
249
+ describe 'with has relationship resources' do
250
+ it 'should raise an exception'
251
+ end
252
+ end
253
+ end
254
+
255
+ it { @user.should respond_to(:eql?) }
256
+
257
+ describe '#eql?' do
258
+ describe 'when comparing to the same object' do
259
+ before :all do
260
+ @other = @user
261
+ @return = @user.eql?(@other)
262
+ end
263
+
264
+ it 'should return true' do
265
+ @return.should be_true
266
+ end
267
+ end
268
+
269
+ describe 'when comparing to an object that does not respond to model' do
270
+ before :all do
271
+ @other = Object.new
272
+ @return = @user.eql?(@other)
273
+ end
274
+
275
+ it 'should return false' do
276
+ @return.should be_false
277
+ end
278
+ end
279
+
280
+ describe 'when comparing to a resource with the same properties, but the model is a subclass' do
281
+ before :all do
282
+ rescue_if 'TODO', @skip do
283
+ @other = @author_model.new(@user.attributes)
284
+ @return = @user.eql?(@other)
285
+ end
286
+ end
287
+
288
+ it 'should return false' do
289
+ @return.should be_false
290
+ end
291
+ end
292
+
293
+ describe 'when comparing to a resource with a different key' do
294
+ before :all do
295
+ @other = @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment)
296
+ @return = @user.eql?(@other)
297
+ end
298
+
299
+ it 'should return false' do
300
+ @return.should be_false
301
+ end
302
+ end
303
+
304
+ describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
305
+ before :all do
306
+ rescue_if 'TODO', @skip do
307
+ @other = @user_model.get(*@user.key)
308
+ @return = @user.eql?(@other)
309
+ end
310
+ end
311
+
312
+ it 'should return true' do
313
+ @return.should be_true
314
+ end
315
+ end
316
+
317
+ describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
318
+ before :all do
319
+ rescue_if 'TODO', @skip do
320
+ @user.age = 20
321
+ @other = @user_model.get(*@user.key)
322
+ @return = @user.eql?(@other)
323
+ end
324
+ end
325
+
326
+ it 'should return false' do
327
+ @return.should be_false
328
+ end
329
+ end
330
+
331
+ describe 'when comparing to a resource with the same properties' do
332
+ before :all do
333
+ rescue_if 'TODO', @skip do
334
+ @other = @user_model.new(@user.attributes)
335
+ @return = @user.eql?(@other)
336
+ end
337
+ end
338
+
339
+ it 'should return true' do
340
+ @return.should be_true
341
+ end
342
+ end
343
+
344
+ with_alternate_adapter do
345
+ describe 'when comparing to a resource with a different repository, but the same properties' do
346
+ before :all do
347
+ rescue_if 'TODO', @skip do
348
+ @other = @alternate_repository.scope { @user_model.create(@user.attributes) }
349
+ @return = @user.eql?(@other)
350
+ end
351
+ end
352
+
353
+ it 'should return true' do
354
+ @return.should be_true
355
+ end
356
+ end
357
+ end
358
+ end
359
+
360
+ it { @user.should respond_to(:inspect) }
361
+
362
+ describe '#inspect' do
363
+
364
+ before :all do
365
+ rescue_if 'TODO', @skip do
366
+ @user = @user_model.get(*@user.key)
367
+ @inspected = @user.inspect
368
+ end
369
+ end
370
+
371
+ it { @inspected.should match(/^#<#{@user_model}/) }
372
+
373
+ it { @inspected.should match(/name="dbussink"/) }
374
+
375
+ it { @inspected.should match(/age=25/) }
376
+
377
+ it { @inspected.should match(/description=<not loaded>/) }
378
+
379
+ end
380
+
381
+ it { @user.should respond_to(:key) }
382
+
383
+ describe '#key' do
384
+
385
+ before :all do
386
+ rescue_if 'TODO', @skip do
387
+ @key = @user.key
388
+ @user.name = 'dkubb'
389
+ end
390
+ end
391
+
392
+ it { @key.should be_kind_of(Array) }
393
+
394
+ it 'should always return the key value persisted in the back end' do
395
+ @key.first.should eql("dbussink")
396
+ end
397
+
398
+ it { @user.key.should eql(@key) }
399
+
400
+ end
401
+
402
+ it { @user.should respond_to(:new?) }
403
+
404
+ describe '#new?' do
405
+
406
+ describe 'on an existing record' do
407
+
408
+ it { @user.should_not be_new }
409
+
410
+ end
411
+
412
+ describe 'on a new record' do
413
+
414
+ before { @user = @user_model.new }
415
+
416
+ it { @user.should be_new }
417
+
418
+ end
419
+
420
+ end
421
+
422
+ it { @user.should respond_to(:reload) }
423
+
424
+ describe '#reload' do
425
+
426
+ describe 'for a single object' do
427
+
428
+ before :all do
429
+ rescue_if 'TODO', @skip do
430
+ @user.name = 'dkubb'
431
+ @user.description = 'test'
432
+ @user.reload
433
+ end
434
+ end
435
+
436
+ it { @user.name.should eql('dbussink') }
437
+
438
+ it 'should also reload previously loaded attributes' do
439
+ @user.attribute_loaded?(:description).should be_true
440
+ end
441
+ end
442
+
443
+ describe 'for when the object is changed outside another object' do
444
+
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
452
+ end
453
+
454
+ it 'should reload the object from the data store' do
455
+ @user.description.should eql('Changed')
456
+ end
457
+
458
+ end
459
+
460
+ end
461
+
462
+ it { @user.should respond_to(:save) }
463
+
464
+ describe '#save' do
465
+
466
+ describe 'on a new, not dirty object' do
467
+
468
+ before :all do
469
+ @user = @user_model.new
470
+ @return = @user.save
471
+ end
472
+
473
+ it 'should return false' do
474
+ @return.should be_false
475
+ end
476
+
477
+ end
478
+
479
+ describe 'on a not new, not dirty object' do
480
+
481
+ it 'should return true even when resource is not dirty' do
482
+ @user.save.should be_true
483
+ end
484
+
485
+ end
486
+
487
+ describe 'on a not new, dirty object' do
488
+
489
+ before :all do
490
+ rescue_if 'TODO', @skip do
491
+ @user.age = 26
492
+ @return = @user.save
493
+ end
494
+ end
495
+
496
+ it 'should save a resource succesfully when dirty' do
497
+ @return.should be_true
498
+ end
499
+
500
+ it 'should actually store the changes to persistent storage' do
501
+ @user.attributes.should == @user.reload.attributes
502
+ end
503
+ end
504
+
505
+ describe 'on a dirty invalid object' do
506
+ before :all do
507
+ rescue_if 'TODO', @skip do
508
+ @user.name = nil
509
+ end
510
+ end
511
+
512
+ it 'should not save an invalid resource' do
513
+ @user.save.should be_false
514
+ end
515
+ end
516
+
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
524
+ end
525
+ end
526
+
527
+ it 'should save resource' do
528
+ pending_if 'TODO', !@user.respond_to?(:comments) do
529
+ @return.should be_true
530
+ end
531
+ end
532
+
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
536
+ end
537
+ end
538
+
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)
542
+ end
543
+ end
544
+
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
548
+ end
549
+ end
550
+
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)
554
+ end
555
+ end
556
+
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
560
+ end
561
+ end
562
+
563
+ end
564
+
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?")
571
+
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
576
+ end
577
+ end
578
+
579
+ it 'should save the dirty resources' do
580
+ pending_if 'TODO', !@user.respond_to?(:comments) do
581
+ @return.should be_true
582
+ end
583
+ end
584
+
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
588
+ end
589
+ end
590
+
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
594
+ end
595
+ end
596
+
597
+ end
598
+
599
+ describe 'with a new dependency' do
600
+
601
+ before :all do
602
+ @first_comment = @comment_model.new(:body => "DM is great!")
603
+ @first_comment.user = @user_model.new(:name => 'dkubb')
604
+ end
605
+
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
609
+ end
610
+ end
611
+
612
+ end
613
+
614
+ describe 'with a dirty dependency' do
615
+ before :all do
616
+ rescue_if 'TODO', @skip do
617
+ @user.name = 'dbussink-the-second'
618
+
619
+ @first_comment = @comment_model.new(:body => 'DM is great!')
620
+ @first_comment.user = @user
621
+
622
+ @return = @first_comment.save
623
+ end
624
+ end
625
+
626
+ it 'should succesfully save the object' do
627
+ @return.should be_true
628
+ end
629
+
630
+ it 'should not have a dirty dependency' do
631
+ @user.should_not be_dirty
632
+ end
633
+
634
+ it 'should succesfully save the dependency' do
635
+ @user.attributes.should == @user_model.get(*@user.key).attributes
636
+ end
637
+
638
+ end
639
+
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')
645
+
646
+ @article.save
647
+ end
648
+ end
649
+
650
+ it 'should not be dirty' do
651
+ pending_if 'TODO', !@article.respond_to?(:paragraphs) do
652
+ @article.should_not be_dirty
653
+ end
654
+ end
655
+
656
+ it 'should not be dirty' do
657
+ pending_if 'TODO', !@article.respond_to?(:paragraphs) do
658
+ @paragraph.should_not be_dirty
659
+ end
660
+ end
661
+
662
+ it 'should set the related object' do
663
+ pending_if 'TODO', !@article.respond_to?(:paragraphs) do
664
+ @paragraph.article.should == @article
665
+ end
666
+ end
667
+
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
671
+ end
672
+ end
673
+ end
674
+
675
+ describe 'with a dirty object with a changed key' do
676
+
677
+ before :all do
678
+ rescue_if 'TODO', @skip do
679
+ @original_key = @user.key
680
+ @user.name = 'dkubb'
681
+ @return = @user.save
682
+ end
683
+ end
684
+
685
+ it 'should save a resource succesfully when dirty' do
686
+ @return.should be_true
687
+ end
688
+
689
+ it 'should actually store the changes to persistent storage' do
690
+ @user.name.should == @user.reload.name
691
+ end
692
+
693
+ it 'should update the identity map' do
694
+ @user.repository.identity_map(@user_model).should have_key(%w[ dkubb ])
695
+ end
696
+
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
700
+
701
+ end
702
+
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)
708
+
709
+ @response = @child.save
710
+ end
711
+
712
+ it 'should return true' do
713
+ @response.should be_true
714
+ end
715
+
716
+ it 'should save the child' do
717
+ @child.should be_saved
718
+ end
719
+
720
+ it 'should save the parent' do
721
+ @parent.should be_saved
722
+ end
723
+
724
+ it 'should save the grandparent' do
725
+ @grandparent.should be_saved
726
+ end
727
+
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
731
+ end
732
+ end
733
+
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
737
+ end
738
+ end
739
+
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
743
+ end
744
+ end
745
+ end
746
+
747
+ end
748
+
749
+ it { @user.should respond_to(:saved?) }
750
+
751
+ describe '#saved?' do
752
+
753
+ describe 'on an existing record' do
754
+
755
+ it { @user.should be_saved }
756
+
757
+ end
758
+
759
+ describe 'on a new record' do
760
+
761
+ before { @user = @user_model.new }
762
+
763
+ it { @user.should_not be_saved }
764
+
765
+ end
766
+
767
+ end
768
+
769
+ [ :update, :update! ].each do |method|
770
+ it { @user.should respond_to(method) }
771
+
772
+ describe "##{method}" do
773
+ describe 'with no arguments' do
774
+ before :all do
775
+ rescue_if 'TODO', @skip do
776
+ @return = @user.send(method)
777
+ end
778
+ end
779
+
780
+ it 'should return true' do
781
+ @return.should be_true
782
+ end
783
+ end
784
+
785
+ describe 'with attributes' do
786
+ before :all do
787
+ rescue_if 'TODO', @skip do
788
+ @attributes = { :description => 'Changed' }
789
+ @return = @user.send(method, @attributes)
790
+ end
791
+ end
792
+
793
+ it 'should return true' do
794
+ @return.should be_true
795
+ end
796
+
797
+ it 'should update attributes of Resource' do
798
+ @attributes.each { |key, value| @user.send(key).should == value }
799
+ end
800
+
801
+ it 'should persist the changes' do
802
+ resource = @user_model.get(*@user.key)
803
+ @attributes.each { |key, value| resource.send(key).should == value }
804
+ end
805
+ end
806
+
807
+ describe 'with attributes where one is a parent association' do
808
+ before :all do
809
+ rescue_if 'Use table aliases to avoid ambiguous named in query', @one_to_one_through do
810
+ @attributes = { :referrer => @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment) }
811
+ @return = @user.send(method, @attributes)
812
+ end
813
+ end
814
+
815
+ it 'should return true' do
816
+ pending_if 'TODO', @one_to_one_through do
817
+ @return.should be_true
818
+ end
819
+ end
820
+
821
+ 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 }
824
+ end
825
+ end
826
+
827
+ it 'should persist the changes' do
828
+ pending_if 'TODO', @one_to_one_through do
829
+ resource = @user_model.get(*@user.key)
830
+ @attributes.each { |key, value| resource.send(key).should == value }
831
+ end
832
+ end
833
+ end
834
+
835
+ describe 'with attributes where a value is nil for a property that does not allow nil' do
836
+ before :all do
837
+ rescue_if 'TODO', @skip do
838
+ @return = @user.send(method, :name => nil)
839
+ end
840
+ end
841
+
842
+ it 'should return false' do
843
+ @return.should be_false
844
+ end
845
+
846
+ it 'should not persist the changes' do
847
+ @user.reload.name.should_not be_nil
848
+ end
849
+ end
850
+
851
+ describe 'on a dirty resource' do
852
+ before :all do
853
+ rescue_if 'TODO', @skip do
854
+ @user.age = 99
855
+ end
856
+ end
857
+
858
+ it 'should raise an exception' do
859
+ lambda {
860
+ @user.send(method, :admin => true)
861
+ }.should raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a dirty resource")
862
+ end
863
+ end
864
+ end
865
+ end
866
+
867
+ describe 'invalid resources' do
868
+ before do
869
+ class ::EmptyObject
870
+ include DataMapper::Resource
871
+ end
872
+
873
+ class ::KeylessObject
874
+ include DataMapper::Resource
875
+ property :name, String
876
+ end
877
+ end
878
+
879
+ it 'should raise an error for a resource without attributes' do
880
+ lambda { EmptyObject.new }.should raise_error
881
+ end
882
+
883
+ it 'should raise an error for a resource without a key' do
884
+ lambda { KeylessObject.new }.should raise_error
885
+ end
886
+
887
+ after do
888
+ # clean out invalid models so that global model cleanup
889
+ # does not throw an exception when working with models
890
+ # in an invalid state
891
+ [ EmptyObject, KeylessObject ].each do |model|
892
+ Object.send(:remove_const, model.name.to_sym)
893
+ DataMapper::Model.descendants.delete(model)
894
+ end
895
+ end
896
+ end
897
+
898
+ describe 'lazy loading' do
899
+ before :all do
900
+ rescue_if 'TODO', @skip do
901
+ @user.name = 'dkubb'
902
+ @user.age = 33
903
+ @user.summary = 'Programmer'
904
+
905
+ # lazy load the description
906
+ @user.description
907
+ end
908
+ end
909
+
910
+ it 'should not overwrite dirty attribute' do
911
+ @user.age.should == 33
912
+ end
913
+
914
+ it 'should not overwrite dirty lazy attribute' do
915
+ @user.summary.should == 'Programmer'
916
+ end
917
+
918
+ it 'should not overwrite dirty key' do
919
+ pending do
920
+ @user.name.should == 'dkubb'
921
+ end
922
+ end
923
+ end
924
+ end