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
@@ -1,649 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
-
3
- describe DataMapper::Resource do
4
-
5
- load_models_for_metaphor :zoo
6
-
7
- describe '#attributes' do
8
- it 'should return a hash of attribute-names and values' do
9
- zoo = Zoo.new
10
- zoo.name = "San Francisco"
11
- zoo.description = "This is a pretty awesome zoo"
12
- zoo.attributes.should == {
13
- :name => "San Francisco", :description => "This is a pretty awesome zoo",
14
- :id => nil, :inception => nil, :open => false, :size => nil, :mission => nil
15
- }
16
- end
17
-
18
- it "should return a hash with all nil values if the instance is new and has no default values" do
19
- Species.new.attributes.should == { :id => nil, :name => nil }
20
- end
21
-
22
- it 'should not include private attributes' do
23
- Species.new.attributes.should == { :id => nil, :name => nil }
24
- end
25
- end
26
-
27
- describe "#attributes=" do
28
- before(:each) do
29
- @zoo = Zoo.new(:name => "San Francisco", :size => 10)
30
- @zoo.attributes = {:size => 12 }
31
- end
32
- it "should change a public property" do
33
- @zoo.size.should == 12
34
- end
35
- it "should raise when attempting to change a property with a non-public writer" do
36
- lambda { @zoo.attributes = {:mission => "Just keep some odd
37
- critters, y'know?" } }.should raise_error
38
- end
39
- end
40
-
41
- # ---------- REPOSITORY WRITE METHODS ---------------
42
-
43
- describe '#save' do
44
-
45
- describe 'with a new resource' do
46
- it 'should set defaults before create'
47
- it 'should create when dirty'
48
- it 'should create when non-dirty, and it has a serial key'
49
- end
50
-
51
- describe 'with an existing resource' do
52
- it 'should update'
53
- end
54
-
55
- end
56
- end
57
-
58
- # ---------- Old specs... BOOOOOOOOOO ---------------
59
-
60
- describe DataMapper::Resource do
61
- before(:each) do
62
- Object.send(:remove_const, :Planet) if defined?(Planet)
63
- class ::Planet
64
- include DataMapper::Resource
65
-
66
- storage_names[:legacy] = "dying_planets"
67
-
68
- property :id, Integer, :key => true
69
- property :name, String
70
- property :age, Integer
71
- property :core, String, :accessor => :private
72
- property :type, Discriminator
73
- property :data, Object, :track => :get
74
-
75
- repository(:legacy) do
76
- property :cowabunga, String
77
- end
78
-
79
- def age
80
- attribute_get(:age)
81
- end
82
-
83
- def to_s
84
- name
85
- end
86
- end
87
-
88
- Object.send(:remove_const, :Phone) if defined?(Phone)
89
- class ::Phone
90
- include DataMapper::Resource
91
-
92
- property :name, String, :key => true
93
- property :awesomeness, Integer
94
- end
95
-
96
- Object.send(:remove_const, :Fruit) if defined?(Fruit)
97
- class ::Fruit
98
- include DataMapper::Resource
99
-
100
- property :id, Integer, :key => true
101
- property :name, String
102
- end
103
-
104
- Object.send(:remove_const, :Grain) if defined?(Grain)
105
- class ::Grain
106
- include DataMapper::Resource
107
-
108
- property :id, Serial
109
- property :name, String, :default => 'wheat'
110
- end
111
-
112
- Object.send(:remove_const, :Vegetable) if defined?(Vegetable)
113
- class ::Vegetable
114
- include DataMapper::Resource
115
-
116
- property :id, Serial
117
- property :name, String
118
- end
119
-
120
- Object.send(:remove_const, :Banana) if defined?(Banana)
121
- class ::Banana < Fruit
122
- property :type, Discriminator
123
- end
124
-
125
- Object.send(:remove_const, :Cyclist) if defined?(Cyclist)
126
- class ::Cyclist
127
- include DataMapper::Resource
128
- property :id, Serial
129
- property :victories, Integer
130
- end
131
-
132
- Fruit.auto_migrate!
133
- Planet.auto_migrate!
134
- Cyclist.auto_migrate!
135
- end
136
-
137
- it 'should provide #save' do
138
- Planet.new.should respond_to(:save)
139
- end
140
-
141
- describe '#save' do
142
- before(:each) do
143
- @adapter = repository(:default).adapter
144
- end
145
-
146
- describe 'with a new resource' do
147
- it 'should set defaults before create' do
148
- resource = Grain.new
149
-
150
- resource.should_not be_dirty
151
- resource.should be_new_record
152
- resource.instance_variable_get('@name').should be_nil
153
-
154
- @adapter.should_receive(:create).with([ resource ]).and_return(1)
155
-
156
- resource.save.should be_true
157
-
158
- resource.instance_variable_get('@name').should == 'wheat'
159
- end
160
-
161
- it 'should create when dirty' do
162
- resource = Vegetable.new(:id => 1, :name => 'Potato')
163
-
164
- resource.should be_dirty
165
- resource.should be_new_record
166
-
167
- @adapter.should_receive(:create).with([ resource ]).and_return(1)
168
-
169
- resource.save.should be_true
170
- end
171
-
172
- it 'should create when non-dirty, and it has a serial key' do
173
- resource = Vegetable.new
174
-
175
- resource.should_not be_dirty
176
- resource.should be_new_record
177
- resource.model.key.any? { |p| p.serial? }.should be_true
178
-
179
- @adapter.should_receive(:create).with([ resource ]).and_return(1)
180
-
181
- resource.save.should be_true
182
- end
183
-
184
- it 'should not create when non-dirty, and is has a non-serial key' do
185
- resource = Fruit.new
186
-
187
- resource.should_not be_dirty
188
- resource.should be_new_record
189
- resource.model.key.any? { |p| p.serial? }.should be_false
190
-
191
- resource.save.should be_false
192
- end
193
-
194
- it 'should return true even if the object is not dirty' do
195
- resource = Cyclist.new
196
- resource.victories = "0 victories"
197
- resource.save.should be_true
198
-
199
- resource.should_not be_dirty
200
- resource.should_not be_new_record
201
- resource.save.should be_true
202
- end
203
-
204
- describe 'for integer fields' do
205
-
206
- it "should save strings without digits as nil" do
207
- resource = Cyclist.new
208
- resource.victories = "none"
209
- resource.save.should be_true
210
- resource.victories.should be_nil
211
- end
212
-
213
- it "should save strings beginning with non-digits as nil" do
214
- resource = Cyclist.new
215
- resource.victories = "almost 5"
216
- resource.save.should be_true
217
- resource.victories.should be_nil
218
- end
219
-
220
- it 'should save strings beginning with negative numbers as that number' do
221
- resource = Cyclist.new
222
- resource.victories = "-4 victories"
223
- resource.save.should be_true
224
- resource.victories.should == -4
225
- end
226
-
227
- it 'should save strings beginning with 0 as 0' do
228
- resource = Cyclist.new
229
- resource.victories = "0 victories"
230
- resource.save.should be_true
231
- resource.victories.should == 0
232
- end
233
-
234
- it 'should save strings beginning with positive numbers as that number' do
235
- resource = Cyclist.new
236
- resource.victories = "23 victories"
237
- resource.save.should be_true
238
- resource.victories.should == 23
239
- end
240
-
241
- end
242
-
243
- end
244
-
245
- describe 'with an existing resource' do
246
- it 'should update' do
247
- resource = Vegetable.new(:name => 'Potato')
248
- resource.instance_variable_set('@new_record', false)
249
-
250
- resource.should be_dirty
251
- resource.should_not be_new_record
252
-
253
- @adapter.should_receive(:update).with(resource.dirty_attributes, resource.to_query).and_return(1)
254
-
255
- resource.save.should be_true
256
- end
257
- end
258
- end
259
-
260
- it "should be able to overwrite to_s" do
261
- Planet.new(:name => 'Mercury').to_s.should == 'Mercury'
262
- end
263
-
264
- describe "storage names" do
265
- it "should use its class name by default" do
266
- Planet.storage_name.should == "planets"
267
- end
268
-
269
- it "should allow changing using #default_storage_name" do
270
- Planet.class_eval <<-EOF.margin
271
- @storage_names.clear
272
- def self.default_storage_name
273
- "Superplanet"
274
- end
275
- EOF
276
-
277
- Planet.storage_name.should == "superplanets"
278
- Planet.class_eval <<-EOF.margin
279
- @storage_names.clear
280
- def self.default_storage_name
281
- self.name
282
- end
283
- EOF
284
- end
285
- end
286
-
287
- it "should require a key" do
288
- lambda do
289
- DataMapper::Model.new("stuff") do
290
- property :name, String
291
- end.new
292
- end.should raise_error(DataMapper::IncompleteResourceError)
293
- end
294
-
295
- it "should hold repository-specific properties" do
296
- Planet.properties(:legacy).should have_property(:cowabunga)
297
- Planet.properties.should_not have_property(:cowabunga)
298
- end
299
-
300
- it "should track the classes that include it" do
301
- DataMapper::Resource.descendants.clear
302
- klass = Class.new { include DataMapper::Resource }
303
- DataMapper::Resource.descendants.should == Set.new([klass])
304
- end
305
-
306
- it "should return an instance of the created object" do
307
- Planet.create(:name => 'Venus', :age => 1_000_000, :id => 42).should be_a_kind_of(Planet)
308
- end
309
-
310
- it 'should provide persistance methods' do
311
- planet = Planet.new
312
- planet.should respond_to(:new_record?)
313
- planet.should respond_to(:save)
314
- planet.should respond_to(:destroy)
315
- end
316
-
317
- it "should have attributes" do
318
- attributes = { :name => 'Jupiter', :age => 1_000_000, :id => 42, :type => Planet, :data => nil }
319
- jupiter = Planet.new(attributes)
320
- jupiter.attributes.should == attributes
321
- end
322
-
323
- it "should be able to set attributes" do
324
- attributes = { :name => 'Jupiter', :age => 1_000_000, :id => 42, :type => Planet, :data => nil }
325
- jupiter = Planet.new(attributes)
326
- jupiter.attributes.should == attributes
327
-
328
- new_attributes = attributes.merge( :age => 2_500_000 )
329
- jupiter.attributes = new_attributes
330
- jupiter.attributes.should == new_attributes
331
- end
332
-
333
- it "should be able to set attributes using update_attributes" do
334
- attributes = { :name => 'Jupiter', :age => 1_000_000, :id => 42, :type => Planet, :data => nil }
335
- jupiter = Planet.new(attributes)
336
- jupiter.attributes.should == attributes
337
-
338
- new_age = { :age => 3_700_000 }
339
- jupiter.update_attributes(new_age).should be_true
340
- jupiter.age.should == 3_700_000
341
- jupiter.attributes.should == attributes.merge(new_age)
342
- end
343
-
344
- # Illustrates a possible controller situation, where an expected params
345
- # key does not exist.
346
- it "update_attributes(nil) should raise an exception" do
347
- hincapie = Cyclist.new
348
- params = {}
349
- lambda {
350
- hincapie.update_attributes(params[:does_not_exist])
351
- }.should raise_error(ArgumentError)
352
- end
353
-
354
- it "update_attributes(:not_a_hash) should raise an exception" do
355
- hincapie = Cyclist.new
356
- lambda {
357
- hincapie.update_attributes(:not_a_hash).should be_false
358
- }.should raise_error(ArgumentError)
359
- end
360
-
361
- # :core is a private accessor so Ruby should raise NameError
362
- it "should not be able to set private attributes" do
363
- lambda {
364
- jupiter = Planet.new({ :core => "Molten Metal" })
365
- }.should raise_error(ArgumentError)
366
- end
367
-
368
- it "should not mark attributes dirty if they are similar after update" do
369
- jupiter = Planet.new(:name => 'Jupiter', :age => 1_000_000, :id => 42, :data => { :a => "Yeah!" })
370
- jupiter.save.should be_true
371
-
372
- # discriminator will be set automatically
373
- jupiter.type.should == Planet
374
-
375
- jupiter.attributes = { :name => 'Jupiter', :age => 1_000_000, :data => { :a => "Yeah!" } }
376
-
377
- jupiter.attribute_dirty?(:name).should be_false
378
- jupiter.attribute_dirty?(:age).should be_false
379
- jupiter.attribute_dirty?(:core).should be_false
380
- jupiter.attribute_dirty?(:data).should be_false
381
-
382
- jupiter.dirty?.should be_false
383
- end
384
-
385
- it "should not mark attributes dirty if they are similar after typecasting" do
386
- jupiter = Planet.new(:name => 'Jupiter', :age => 1_000_000, :id => 42, :type => Planet)
387
- jupiter.save.should be_true
388
- jupiter.dirty?.should be_false
389
-
390
- jupiter.age = '1_000_000'
391
- jupiter.attribute_dirty?(:age).should be_false
392
- jupiter.dirty?.should be_false
393
- end
394
-
395
- it "should track attributes" do
396
-
397
- # So attribute tracking is a feature of the Resource,
398
- # not the Property. Properties are class-level declarations.
399
- # Instance-level operations like this happen in Resource with methods
400
- # and ivars it sets up. Like a @dirty_attributes Array for example to
401
- # track dirty attributes.
402
-
403
- mars = Planet.new :name => 'Mars'
404
- # #attribute_loaded? and #attribute_dirty? are a bit verbose,
405
- # but I like the consistency and grouping of the methods.
406
-
407
- # initialize-set values are dirty as well. DM sets ivars
408
- # directly when materializing, so an ivar won't exist
409
- # if the value wasn't loaded by DM initially. Touching that
410
- # ivar at all will declare it, so at that point it's loaded.
411
- # This means #attribute_loaded?'s implementation could be very
412
- # similar (if not identical) to:
413
- # def attribute_loaded?(name)
414
- # instance_variable_defined?("@#{name}")
415
- # end
416
- mars.attribute_loaded?(:name).should be_true
417
- mars.attribute_dirty?(:id).should be_false
418
- mars.attribute_dirty?(:name).should be_true
419
- mars.attribute_loaded?(:age).should be_false
420
- mars.attribute_dirty?(:data).should be_false
421
-
422
- mars.age.should be_nil
423
-
424
- # So accessing a value should ensure it's loaded.
425
- # XXX: why? if the @ivar isn't set, which it wouldn't be in this
426
- # case because mars is a new_record?, then perhaps it should return
427
- # false
428
- # mars.attribute_loaded?(:age).should be_true
429
-
430
- # A value should be able to be both loaded and nil.
431
- mars.age.should be_nil
432
-
433
- # Unless you call #[]= it's not dirty.
434
- mars.attribute_dirty?(:age).should be_false
435
-
436
- mars.age = 30
437
- mars.data = { :a => "Yeah!" }
438
-
439
- # Obviously. :-)
440
- mars.attribute_dirty?(:age).should be_true
441
- mars.attribute_dirty?(:data).should be_true
442
- end
443
-
444
- it "should mark the key as dirty, if it is a natural key and has been set" do
445
- phone = Phone.new
446
- phone.name = 'iPhone'
447
- phone.attribute_dirty?(:name).should be_true
448
- end
449
-
450
- it 'should return the dirty attributes' do
451
- pluto = Planet.new(:name => 'Pluto', :age => 500_000)
452
- pluto.attribute_dirty?(:name).should be_true
453
- pluto.attribute_dirty?(:age).should be_true
454
- end
455
-
456
- it 'should overwite old dirty attributes with new ones' do
457
- pluto = Planet.new(:name => 'Pluto', :age => 500_000)
458
- pluto.dirty_attributes.size.should == 2
459
- pluto.attribute_dirty?(:name).should be_true
460
- pluto.attribute_dirty?(:age).should be_true
461
- pluto.name = "pluto"
462
- pluto.dirty_attributes.size.should == 2
463
- pluto.attribute_dirty?(:name).should be_true
464
- pluto.attribute_dirty?(:age).should be_true
465
- end
466
-
467
- it 'should provide a key' do
468
- Planet.new.should respond_to(:key)
469
- end
470
-
471
- it 'should store and retrieve default values' do
472
- Planet.property(:satellite_count, Integer, :default => 0)
473
- # stupid example but it's reliable and works
474
- Planet.property(:orbit_period, Float, :default => lambda { |r,p| p.name.to_s.length })
475
- earth = Planet.new(:name => 'Earth')
476
- earth.satellite_count.should == 0
477
- earth.orbit_period.should == 12
478
- earth.satellite_count = 2
479
- earth.satellite_count.should == 2
480
- earth.orbit_period = 365.26
481
- earth.orbit_period.should == 365.26
482
- end
483
-
484
- describe "#reload_attributes" do
485
- it 'should call collection.reload if not a new record' do
486
- planet = Planet.new(:name => 'Omicron Persei VIII')
487
- planet.stub!(:new_record?).and_return(false)
488
-
489
- collection = mock('collection')
490
- collection.should_receive(:reload).with(:fields => [:name]).once
491
-
492
- planet.stub!(:collection).and_return(collection)
493
- planet.reload_attributes(:name)
494
- end
495
-
496
- it 'should not call collection.reload if no attributes are provided to reload' do
497
- planet = Planet.new(:name => 'Omicron Persei VIII')
498
- planet.stub!(:new_record?).and_return(false)
499
-
500
- collection = mock('collection')
501
- collection.should_not_receive(:reload)
502
-
503
- planet.stub!(:collection).and_return(collection)
504
- planet.reload_attributes
505
- end
506
-
507
- it 'should not call collection.reload if the record is new' do
508
- lambda {
509
- Planet.new(:name => 'Omicron Persei VIII').reload_attributes(:name)
510
- }.should_not raise_error
511
-
512
- planet = Planet.new(:name => 'Omicron Persei VIII')
513
- planet.should_not_receive(:collection)
514
- planet.reload_attributes(:name)
515
- end
516
- end
517
-
518
- describe '#reload' do
519
- it 'should call #reload_attributes with the currently loaded attributes' do
520
- planet = Planet.new(:name => 'Omicron Persei VIII', :age => 1)
521
- planet.stub!(:new_record?).and_return(false)
522
-
523
- planet.should_receive(:reload_attributes).with(:name, :age).once
524
-
525
- planet.reload
526
- end
527
-
528
- it 'should call #reload on the parent and child associations' do
529
- planet = Planet.new(:name => 'Omicron Persei VIII', :age => 1)
530
- planet.stub!(:new_record?).and_return(false)
531
-
532
- child_association = mock('child assoc')
533
- child_association.should_receive(:reload).once.and_return(true)
534
-
535
- parent_association = mock('parent assoc')
536
- parent_association.should_receive(:reload).once.and_return(true)
537
-
538
- planet.stub!(:child_associations).and_return([child_association])
539
- planet.stub!(:parent_associations).and_return([parent_association])
540
- planet.stub!(:reload_attributes).and_return(planet)
541
-
542
- planet.reload
543
- end
544
-
545
- it 'should not do anything if the record is new' do
546
- planet = Planet.new(:name => 'Omicron Persei VIII', :age => 1)
547
- planet.should_not_receive(:reload_attributes)
548
- planet.reload
549
- end
550
- end
551
-
552
- describe 'when retrieving by key' do
553
- it 'should return the corresponding object' do
554
- m = mock("planet")
555
- Planet.should_receive(:get).with(1).and_return(m)
556
-
557
- Planet.get!(1).should == m
558
- end
559
-
560
- it 'should raise an error if not found' do
561
- Planet.should_receive(:get).and_return(nil)
562
-
563
- lambda do
564
- Planet.get!(1)
565
- end.should raise_error(DataMapper::ObjectNotFoundError)
566
- end
567
- end
568
-
569
- describe "inheritance" do
570
- before(:all) do
571
- class ::Media
572
- include DataMapper::Resource
573
-
574
- storage_names[:default] = 'media'
575
- storage_names[:west_coast] = 'm3d1a'
576
-
577
- property :name, String, :key => true
578
- end
579
-
580
- class ::NewsPaper < Media
581
-
582
- storage_names[:east_coast] = 'mother'
583
-
584
- property :rating, Integer
585
- end
586
- end
587
-
588
- it 'should inherit storage_names' do
589
- NewsPaper.storage_name(:default).should == 'media'
590
- NewsPaper.storage_name(:west_coast).should == 'm3d1a'
591
- NewsPaper.storage_name(:east_coast).should == 'mother'
592
- Media.storage_name(:east_coast).should == 'medium'
593
- end
594
-
595
- it 'should inherit properties' do
596
- Media.properties.should have(1).entries
597
- NewsPaper.properties.should have(2).entries
598
- end
599
- end
600
-
601
- describe "Single-table Inheritance" do
602
- before(:all) do
603
- class ::Plant
604
- include DataMapper::Resource
605
-
606
- property :id, Integer, :key => true
607
- property :length, Integer
608
-
609
- def calculate(int)
610
- int ** 2
611
- end
612
-
613
- def length=(len)
614
- attribute_set(:length, calculate(len))
615
- end
616
- end
617
-
618
- class ::HousePlant < Plant
619
- def calculate(int)
620
- int ** 3
621
- end
622
- end
623
-
624
- class ::PoisonIvy < Plant
625
- def length=(len)
626
- attribute_set(:length, len - 1)
627
- end
628
- end
629
- end
630
-
631
- it "should be able to overwrite getters" do
632
- @p = Plant.new
633
- @p.length = 3
634
- @p.length.should == 9
635
- end
636
-
637
- it "should pick overwritten methods" do
638
- @hp = HousePlant.new
639
- @hp.length = 3
640
- @hp.length.should == 27
641
- end
642
-
643
- it "should pick overwritten setters" do
644
- @pi = PoisonIvy.new
645
- @pi.length = 3
646
- @pi.length.should == 2
647
- end
648
- end
649
- end