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,829 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
3
+
4
+ describe DataMapper::Property do
5
+
6
+ # define the model prior to supported_by
7
+ before :all do
8
+ class ::Track
9
+ include DataMapper::Resource
10
+
11
+ property :id, Serial
12
+ property :artist, String, :lazy => false, :index => :artist_album
13
+ property :title, String, :field => 'name', :index => true
14
+ property :album, String, :index => :artist_album
15
+ property :musicbrainz_hash, String, :unique => true, :unique_index => true
16
+ end
17
+
18
+ class ::Image
19
+ include DataMapper::Resource
20
+
21
+ property :md5hash, String, :key => true, :length => 32
22
+ property :title, String, :nullable => false, :unique => true
23
+ property :description, Text, :length => 1..1024, :lazy => [ :detail ]
24
+
25
+ property :format, String, :default => 'jpeg'
26
+ # WxH, stored as a dumped Ruby pair
27
+ property :size, Object
28
+ property :filesize, Float
29
+ property :width, Integer
30
+ property :quality, BigDecimal
31
+
32
+ property :taken_on, Date
33
+ property :taken_at, Time, :default => lambda { |resource, property| Time.now }
34
+ property :retouched_at, DateTime
35
+ property :type, Class
36
+ property :visible, Boolean, :default => true
37
+ end
38
+ end
39
+
40
+ supported_by :all do
41
+ describe '#field' do
42
+ it 'returns @field value if it is present' do
43
+ Track.properties[:title].field.should eql('name')
44
+ end
45
+
46
+ it 'returns field for specific repository when it is present'
47
+
48
+ it 'sets field value using field naming convention on first reference'
49
+ end
50
+
51
+ describe '#custom?' do
52
+ it 'is true for custom type fields (not provided by dm-core)'
53
+
54
+ it 'is false for core type fields (provided by dm-core)'
55
+ end
56
+
57
+ describe '#default_for' do
58
+ it 'returns default value for non-callables' do
59
+ Image.properties[:format].default_for(Image.new).should == 'jpeg'
60
+ end
61
+
62
+ it 'returns result of a call for callable values' do
63
+ Image.properties[:taken_at].default_for(Image.new).year.should == Time.now.year
64
+ end
65
+ end
66
+
67
+ describe '#eql?' do
68
+ it 'is true for properties with the same model and name' do
69
+ Track.properties[:title].should eql(Track.properties[:title])
70
+ end
71
+
72
+
73
+ it 'is false for properties of different models' do
74
+ Track.properties[:title].should_not eql(Image.properties[:title])
75
+ end
76
+
77
+ it 'is false for properties with different names' do
78
+ Track.properties[:title].should_not eql(Track.properties[:id])
79
+ end
80
+ end
81
+
82
+ describe '#get' do
83
+ before :all do
84
+ @image = Image.create(:md5hash => '5268f0f3f452844c79843e820f998869',
85
+ :title => 'Rome at the sunset',
86
+ :description => 'Just wow')
87
+
88
+ @image.should be_saved
89
+
90
+ @image = Image.first(:fields => [ :md5hash, :title ], :md5hash => @image.md5hash)
91
+ end
92
+
93
+ it 'triggers loading for lazy loaded properties' do
94
+ Image.properties[:description].get(@image)
95
+ Image.properties[:description].loaded?(@image).should be(true)
96
+ end
97
+
98
+ it 'assigns loaded value to @ivar' do
99
+ Image.properties[:description].get(@image)
100
+ @image.instance_variable_get(:@description).should == 'Just wow'
101
+ end
102
+
103
+ it 'sets default value for new records with nil value' do
104
+ Image.properties[:format].get(@image).should == 'jpeg'
105
+ end
106
+
107
+ it 'returns property value' do
108
+ Image.properties[:description].get(@image).should == 'Just wow'
109
+ end
110
+ end
111
+
112
+ describe '#get!' do
113
+ before :all do
114
+ @image = Image.new
115
+
116
+ # now some dark Ruby magic
117
+ @image.instance_variable_set(:@description, 'Is set by magic')
118
+ end
119
+
120
+ it 'gets instance variable value from the resource directly' do
121
+ # if you know a better way to test direct instance variable access,
122
+ # go ahead and make changes to this example
123
+ Image.properties[:description].get!(@image).should == 'Is set by magic'
124
+ end
125
+ end
126
+
127
+ describe '#index' do
128
+ it 'returns true when property has an index' do
129
+ Track.properties[:title].index.should be_true
130
+ end
131
+
132
+ it 'returns index name when property has a named index' do
133
+ Track.properties[:album].index.should eql(:artist_album)
134
+ end
135
+
136
+ it 'returns nil when property has no index' do
137
+ Track.properties[:musicbrainz_hash].index.should be_nil
138
+ end
139
+ end
140
+
141
+ describe '#initialize' do
142
+ describe 'when tracking strategy is explicitly given' do
143
+ it 'uses tracking strategy from options'
144
+ end
145
+
146
+ describe 'when custom type has tracking stragegy' do
147
+ it 'uses tracking strategy from type'
148
+ end
149
+ end
150
+
151
+ describe '#inspect' do
152
+ before :all do
153
+ @str = Track.properties[:title].inspect
154
+ end
155
+
156
+ it 'features model name' do
157
+ @str.should =~ /@model=Track/
158
+ end
159
+
160
+ it 'features property name' do
161
+ @str.should =~ /@name=:title/
162
+ end
163
+ end
164
+
165
+ describe '#key?' do
166
+ describe 'returns true when property is a ' do
167
+ it 'serial key' do
168
+ Track.properties[:id].key?.should be_true
169
+ end
170
+ it 'natural key' do
171
+ Image.properties[:md5hash].key?.should be_true
172
+ end
173
+ end
174
+
175
+ it 'returns true when property is a part of composite key'
176
+
177
+ it 'returns false when property does not relate to a key' do
178
+ Track.properties[:title].key?.should be_false
179
+ end
180
+ end
181
+
182
+ describe '#lazy?' do
183
+ it 'returns true when property is lazy loaded' do
184
+ Image.properties[:description].lazy?.should be_true
185
+ end
186
+
187
+ it 'returns false when property is not lazy loaded' do
188
+ Track.properties[:artist].lazy?.should be_false
189
+ end
190
+ end
191
+
192
+ describe '#length' do
193
+ it 'returns upper bound for Range values' do
194
+ Image.properties[:description].length.should eql(1024)
195
+ end
196
+
197
+ it 'returns value as is for integer values' do
198
+ Image.properties[:md5hash].length.should eql(32)
199
+ end
200
+ end
201
+
202
+ describe '#min' do
203
+ describe 'when :min and :max options not provided to constructor' do
204
+ before do
205
+ @property = Image.property(:integer_with_nil_min, Integer)
206
+ end
207
+
208
+ it 'should be nil' do
209
+ @property.min.should be_nil
210
+ end
211
+ end
212
+
213
+ describe 'when :min option not provided to constructor, but :max is provided' do
214
+ before do
215
+ @property = Image.property(:integer_with_default_min, Integer, :max => 1)
216
+ end
217
+
218
+ it 'should be the default value' do
219
+ @property.min.should == 0
220
+ end
221
+ end
222
+
223
+ describe 'when :min and :max options provided to constructor' do
224
+ before do
225
+ @min = 1
226
+ @property = Image.property(:integer_with_explicit_min, Integer, :min => @min, :max => 2)
227
+ end
228
+
229
+ it 'should be the expected value' do
230
+ @property.min.should == @min
231
+ end
232
+ end
233
+ end
234
+
235
+ describe '#max' do
236
+ describe 'when :min and :max options not provided to constructor' do
237
+ before do
238
+ @property = Image.property(:integer_with_nil_max, Integer)
239
+ end
240
+
241
+ it 'should be nil' do
242
+ @property.max.should be_nil
243
+ end
244
+ end
245
+
246
+ describe 'when :max option not provided to constructor, but :min is provided' do
247
+ before do
248
+ @property = Image.property(:integer_with_default_max, Integer, :min => 1)
249
+ end
250
+
251
+ it 'should be the default value' do
252
+ @property.max.should == 2**31-1
253
+ end
254
+ end
255
+
256
+ describe 'when :min and :max options provided to constructor' do
257
+ before do
258
+ @max = 2
259
+ @property = Image.property(:integer_with_explicit_max, Integer, :min => 1, :max => @max)
260
+ end
261
+
262
+ it 'should be the expected value' do
263
+ @property.max.should == @max
264
+ end
265
+ end
266
+ end
267
+
268
+ describe '#nullable?' do
269
+ it 'returns true when property can accept nil as its value' do
270
+ Track.properties[:artist].nullable?.should be_true
271
+ end
272
+
273
+ it 'returns false when property nil value is prohibited for this property' do
274
+ Image.properties[:title].nullable?.should be_false
275
+ end
276
+ end
277
+
278
+ describe '#serial?' do
279
+ it 'returns true when property is serial (auto incrementing)' do
280
+ Track.properties[:id].serial?.should be_true
281
+ end
282
+
283
+ it 'returns false when property is NOT serial (auto incrementing)' do
284
+ Image.properties[:md5hash].serial?.should be_false
285
+ end
286
+ end
287
+
288
+ # What's going on here:
289
+ #
290
+ # we first set original value and make an assertion on it
291
+ # then we try to set it again, which clears original value
292
+ # (since original value is set, property is no longer dirty)
293
+ describe '#set_original_value' do
294
+ before :all do
295
+ @image = Image.create(:md5hash => '5268f0f3f452844c79843e820f998869',
296
+ :title => 'Rome at the sunset',
297
+ :description => 'Just wow')
298
+ @image.reload
299
+ @property = Image.properties[:title]
300
+ end
301
+
302
+ describe 'when value changes' do
303
+ before :all do
304
+ @property.set_original_value(@image, 'Rome at the sunset')
305
+ end
306
+
307
+ it 'sets original value of the property' do
308
+ @image.original_attributes[@property].should == 'Rome at the sunset'
309
+ end
310
+ end
311
+
312
+ describe 'when value stays the same' do
313
+ before :all do
314
+ @property.set_original_value(@image, 'Rome at the sunset')
315
+ end
316
+
317
+ it 'only sets original value when it has changed' do
318
+ @property.set_original_value(@image, 'Rome at the sunset')
319
+ @image.original_attributes[@property].should be_blank
320
+ end
321
+ end
322
+ end
323
+
324
+ describe '#set' do
325
+ before :all do
326
+ # keep in mind we must run these examples with a
327
+ # saved model instance
328
+ @image = Image.create(:md5hash => '5268f0f3f452844c79843e820f998869',
329
+ :title => 'Rome at the sunset',
330
+ :description => 'Just wow')
331
+ @image.reload
332
+ @property = Image.properties[:title]
333
+ end
334
+
335
+ it 'triggers lazy loading for given resource'
336
+
337
+ it 'type casts given value' do
338
+ # set it to a float
339
+ @property.set(@image, 1.0)
340
+ # get a string that has been typecasted
341
+ @image.title.should == '1.0'
342
+ end
343
+
344
+ it 'stores original value' do
345
+ @property.set(@image, 'Updated value')
346
+ @image.original_attributes[@property].should == 'Rome at the sunset'
347
+ end
348
+
349
+ it 'sets new property value' do
350
+ @property.set(@image, 'Updated value')
351
+ @image.title.should == 'Updated value'
352
+ end
353
+ end
354
+
355
+ describe '#set!' do
356
+ before :all do
357
+ @image = Image.new(:md5hash => '5268f0f3f452844c79843e820f998869',
358
+ :title => 'Rome at the sunset',
359
+ :description => 'Just wow')
360
+
361
+ @property = Image.properties[:title]
362
+ end
363
+
364
+ it 'directly sets instance variable on given resource' do
365
+ @property.set!(@image, 'Set with dark Ruby magic')
366
+ @image.title.should == 'Set with dark Ruby magic'
367
+ end
368
+ end
369
+
370
+ describe '#typecast' do
371
+ describe "when type is able to do typecasting on it's own" do
372
+ it 'delegates all the work to the type'
373
+ end
374
+
375
+ describe 'when value is nil' do
376
+ it 'returns value unchanged' do
377
+ Image.properties[:size].typecast(nil).should be(nil)
378
+ end
379
+ end
380
+
381
+ describe 'when value is a Ruby primitive' do
382
+ it 'returns value unchanged' do
383
+ Image.properties[:size].typecast([3200, 2400]).should == [3200, 2400]
384
+ end
385
+ end
386
+
387
+ describe 'when type primitive is a String' do
388
+ before :all do
389
+ @property = Image.properties[:title]
390
+ end
391
+
392
+ it 'returns same value if a string' do
393
+ @value = '1.0'
394
+ @property.typecast(@value).should equal(@value)
395
+ end
396
+
397
+ it 'returns string representation of the new value' do
398
+ @property.typecast(1.0).should eql('1.0')
399
+ end
400
+ end
401
+
402
+ describe 'when type primitive is a Float' do
403
+ before :all do
404
+ @property = Image.properties[:filesize]
405
+ end
406
+
407
+ it 'returns same value if a float' do
408
+ @value = 24.0
409
+ @property.typecast(@value).should equal(@value)
410
+ end
411
+
412
+ it 'returns float representation of a zero string integer' do
413
+ @property.typecast('0').should eql(0.0)
414
+ end
415
+
416
+ it 'returns float representation of a positive string integer' do
417
+ @property.typecast('24').should eql(24.0)
418
+ end
419
+
420
+ it 'returns float representation of a negative string integer' do
421
+ @property.typecast('-24').should eql(-24.0)
422
+ end
423
+
424
+ it 'returns float representation of a zero string float' do
425
+ @property.typecast('0.0').should eql(0.0)
426
+ end
427
+
428
+ it 'returns float representation of a positive string float' do
429
+ @property.typecast('24.35').should eql(24.35)
430
+ end
431
+
432
+ it 'returns float representation of a negative string float' do
433
+ @property.typecast('-24.35').should eql(-24.35)
434
+ end
435
+
436
+ it 'returns float representation of a zero string float, with no leading digits' do
437
+ @property.typecast('.0').should eql(0.0)
438
+ end
439
+
440
+ it 'returns float representation of a positive string float, with no leading digits' do
441
+ @property.typecast('.41').should eql(0.41)
442
+ end
443
+
444
+ it 'returns float representation of a zero integer' do
445
+ @property.typecast(0).should eql(0.0)
446
+ end
447
+
448
+ it 'returns float representation of a positive integer' do
449
+ @property.typecast(24).should eql(24.0)
450
+ end
451
+
452
+ it 'returns float representation of a negative integer' do
453
+ @property.typecast(-24).should eql(-24.0)
454
+ end
455
+
456
+ it 'returns float representation of a zero decimal' do
457
+ @property.typecast(BigDecimal('0.0')).should eql(0.0)
458
+ end
459
+
460
+ it 'returns float representation of a positive decimal' do
461
+ @property.typecast(BigDecimal('24.35')).should eql(24.35)
462
+ end
463
+
464
+ it 'returns float representation of a negative decimal' do
465
+ @property.typecast(BigDecimal('-24.35')).should eql(-24.35)
466
+ end
467
+
468
+ [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
469
+ it "does not typecast non-numeric value #{value.inspect}" do
470
+ @property.typecast(value).should equal(value)
471
+ end
472
+ end
473
+ end
474
+
475
+ describe 'when type primitive is a Integer' do
476
+ before :all do
477
+ @property = Image.properties[:width]
478
+ end
479
+
480
+ it 'returns same value if an integer' do
481
+ @value = 24
482
+ @property.typecast(@value).should equal(@value)
483
+ end
484
+
485
+ it 'returns integer representation of a zero string integer' do
486
+ @property.typecast('0').should eql(0)
487
+ end
488
+
489
+ it 'returns integer representation of a positive string integer' do
490
+ @property.typecast('24').should eql(24)
491
+ end
492
+
493
+ it 'returns integer representation of a negative string integer' do
494
+ @property.typecast('-24').should eql(-24)
495
+ end
496
+
497
+ it 'returns integer representation of a zero string float' do
498
+ @property.typecast('0.0').should eql(0)
499
+ end
500
+
501
+ it 'returns integer representation of a positive string float' do
502
+ @property.typecast('24.35').should eql(24)
503
+ end
504
+
505
+ it 'returns integer representation of a negative string float' do
506
+ @property.typecast('-24.35').should eql(-24)
507
+ end
508
+
509
+ it 'returns integer representation of a zero string float, with no leading digits' do
510
+ @property.typecast('.0').should eql(0)
511
+ end
512
+
513
+ it 'returns integer representation of a positive string float, with no leading digits' do
514
+ @property.typecast('.41').should eql(0)
515
+ end
516
+
517
+ it 'returns integer representation of a zero float' do
518
+ @property.typecast(0.0).should eql(0)
519
+ end
520
+
521
+ it 'returns integer representation of a positive float' do
522
+ @property.typecast(24.35).should eql(24)
523
+ end
524
+
525
+ it 'returns integer representation of a negative float' do
526
+ @property.typecast(-24.35).should eql(-24)
527
+ end
528
+
529
+ it 'returns integer representation of a zero decimal' do
530
+ @property.typecast(BigDecimal('0.0')).should eql(0)
531
+ end
532
+
533
+ it 'returns integer representation of a positive decimal' do
534
+ @property.typecast(BigDecimal('24.35')).should eql(24)
535
+ end
536
+
537
+ it 'returns integer representation of a negative decimal' do
538
+ @property.typecast(BigDecimal('-24.35')).should eql(-24)
539
+ end
540
+
541
+ [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
542
+ it "does not typecast non-numeric value #{value.inspect}" do
543
+ @property.typecast(value).should equal(value)
544
+ end
545
+ end
546
+ end
547
+
548
+ describe 'when type primitive is a BigDecimal' do
549
+ before :all do
550
+ @property = Image.properties[:quality]
551
+ end
552
+
553
+ it 'returns same value if a decimal' do
554
+ @value = BigDecimal('24.0')
555
+ @property.typecast(@value).should equal(@value)
556
+ end
557
+
558
+ it 'returns decimal representation of a zero string integer' do
559
+ @property.typecast('0').should eql(BigDecimal('0.0'))
560
+ end
561
+
562
+ it 'returns decimal representation of a positive string integer' do
563
+ @property.typecast('24').should eql(BigDecimal('24.0'))
564
+ end
565
+
566
+ it 'returns decimal representation of a negative string integer' do
567
+ @property.typecast('-24').should eql(BigDecimal('-24.0'))
568
+ end
569
+
570
+ it 'returns decimal representation of a zero string float' do
571
+ @property.typecast('0.0').should eql(BigDecimal('0.0'))
572
+ end
573
+
574
+ it 'returns decimal representation of a positive string float' do
575
+ @property.typecast('24.35').should eql(BigDecimal('24.35'))
576
+ end
577
+
578
+ it 'returns decimal representation of a negative string float' do
579
+ @property.typecast('-24.35').should eql(BigDecimal('-24.35'))
580
+ end
581
+
582
+ it 'returns decimal representation of a zero string float, with no leading digits' do
583
+ @property.typecast('.0').should eql(BigDecimal('0.0'))
584
+ end
585
+
586
+ it 'returns decimal representation of a positive string float, with no leading digits' do
587
+ @property.typecast('.41').should eql(BigDecimal('0.41'))
588
+ end
589
+
590
+ it 'returns decimal representation of a zero integer' do
591
+ @property.typecast(0).should eql(BigDecimal('0.0'))
592
+ end
593
+
594
+ it 'returns decimal representation of a positive integer' do
595
+ @property.typecast(24).should eql(BigDecimal('24.0'))
596
+ end
597
+
598
+ it 'returns decimal representation of a negative integer' do
599
+ @property.typecast(-24).should eql(BigDecimal('-24.0'))
600
+ end
601
+
602
+ it 'returns decimal representation of a zero float' do
603
+ @property.typecast(0.0).should eql(BigDecimal('0.0'))
604
+ end
605
+
606
+ it 'returns decimal representation of a positive float' do
607
+ @property.typecast(24.35).should eql(BigDecimal('24.35'))
608
+ end
609
+
610
+ it 'returns decimal representation of a negative float' do
611
+ @property.typecast(-24.35).should eql(BigDecimal('-24.35'))
612
+ end
613
+
614
+ [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
615
+ it "does not typecast non-numeric value #{value.inspect}" do
616
+ @property.typecast(value).should equal(value)
617
+ end
618
+ end
619
+ end
620
+
621
+ describe 'when type primitive is a DateTime' do
622
+ before do
623
+ @property = Image.properties[:retouched_at]
624
+ end
625
+
626
+ describe 'and value given as a hash with keys like :year, :month, etc' do
627
+ it 'builds a DateTime instance from hash values' do
628
+ result = @property.typecast(
629
+ 'year' => '2006',
630
+ 'month' => '11',
631
+ 'day' => '23',
632
+ 'hour' => '12',
633
+ 'min' => '0',
634
+ 'sec' => '0'
635
+ )
636
+
637
+ result.should be_kind_of(DateTime)
638
+ result.year.should eql(2006)
639
+ result.month.should eql(11)
640
+ result.day.should eql(23)
641
+ result.hour.should eql(12)
642
+ result.min.should eql(0)
643
+ result.sec.should eql(0)
644
+ end
645
+ end
646
+
647
+ describe 'and value is a string' do
648
+ it 'parses the string' do
649
+ Image.properties[:retouched_at].typecast('Dec, 2006').month.should == 12
650
+ end
651
+ end
652
+
653
+ it 'does not typecast non-datetime values' do
654
+ @property.typecast('not-datetime').should eql('not-datetime')
655
+ end
656
+ end
657
+
658
+ describe 'when type primitive is a Date' do
659
+ before do
660
+ @property = Image.properties[:taken_on]
661
+ end
662
+
663
+ describe 'and value given as a hash with keys like :year, :month, etc' do
664
+ it 'builds a Date instance from hash values' do
665
+ result = @property.typecast(
666
+ 'year' => '2007',
667
+ 'month' => '3',
668
+ 'day' => '25'
669
+ )
670
+
671
+ result.should be_kind_of(Date)
672
+ result.year.should eql(2007)
673
+ result.month.should eql(3)
674
+ result.day.should eql(25)
675
+ end
676
+ end
677
+
678
+ describe 'and value is a string' do
679
+ it 'parses the string' do
680
+ result = @property.typecast('Dec 20th, 2006')
681
+ result.month.should == 12
682
+ result.day.should == 20
683
+ result.year.should == 2006
684
+ end
685
+ end
686
+
687
+ it 'does not typecast non-date values' do
688
+ @property.typecast('not-date').should eql('not-date')
689
+ end
690
+ end
691
+
692
+ describe 'when type primitive is a Time' do
693
+ before do
694
+ @property = Image.properties[:taken_at]
695
+ end
696
+
697
+ describe 'and value given as a hash with keys like :year, :month, etc' do
698
+ it 'builds a Time instance from hash values' do
699
+ result = @property.typecast(
700
+ 'year' => '2006',
701
+ 'month' => '11',
702
+ 'day' => '23',
703
+ 'hour' => '12',
704
+ 'min' => '0',
705
+ 'sec' => '0'
706
+ )
707
+
708
+ result.should be_kind_of(Time)
709
+ result.year.should eql(2006)
710
+ result.month.should eql(11)
711
+ result.day.should eql(23)
712
+ result.hour.should eql(12)
713
+ result.min.should eql(0)
714
+ result.sec.should eql(0)
715
+ end
716
+ end
717
+
718
+ describe 'and value is a string' do
719
+ it 'parses the string' do
720
+ result = @property.typecast('22:24')
721
+ result.hour.should eql(22)
722
+ result.min.should eql(24)
723
+ end
724
+ end
725
+
726
+ it 'does not typecast non-time values' do
727
+ pending 'Time#parse is too permissive' do
728
+ @property.typecast('not-time').should eql('not-time')
729
+ end
730
+ end
731
+ end
732
+
733
+ describe 'when type primitive is a Class' do
734
+ before do
735
+ @property = Image.properties[:type]
736
+ end
737
+
738
+ it 'returns same value if a class' do
739
+ @value = Image
740
+ @property.typecast(@value).should equal(@value)
741
+ end
742
+
743
+ it 'returns the class if found' do
744
+ @property.typecast('Image').should eql(Image)
745
+ end
746
+
747
+ it 'does not typecast non-class values' do
748
+ @property.typecast('NoClass').should eql('NoClass')
749
+ end
750
+ end
751
+
752
+ describe 'when type primitive is a Boolean' do
753
+ before do
754
+ @property = Image.properties[:visible]
755
+ end
756
+
757
+ [ true, 'true', 'TRUE', '1', 1, 't', 'T' ].each do |value|
758
+ it "returns true when value is #{value.inspect}" do
759
+ @property.typecast(value).should be_true
760
+ end
761
+ end
762
+
763
+ [ false, 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
764
+ it "returns false when value is #{value.inspect}" do
765
+ @property.typecast(value).should be_false
766
+ end
767
+ end
768
+
769
+ [ 'string', 2, 1.0, BigDecimal('1.0'), DateTime.now, Time.now, Date.today, Class, Object.new, ].each do |value|
770
+ it "does not typecast value #{value.inspect}" do
771
+ @property.typecast(value).should equal(value)
772
+ end
773
+ end
774
+ end
775
+ end # #typecase
776
+
777
+ describe '#unique?' do
778
+ it 'is true for fields that explicitly given uniq index' do
779
+ Track.properties[:musicbrainz_hash].unique?.should be_true
780
+ end
781
+
782
+ it 'is true for serial fields' do
783
+ pending do
784
+ Track.properties[:title].unique?.should be_true
785
+ end
786
+ end
787
+
788
+ it 'is true for keys' do
789
+ Image.properties[:md5hash].unique?.should be_true
790
+ end
791
+ end
792
+
793
+ describe '#unique_index' do
794
+ it 'returns true when property has unique index' do
795
+ Track.properties[:musicbrainz_hash].unique_index.should be_true
796
+ end
797
+
798
+ it 'returns nil when property has no unique index' do
799
+ Image.properties[:title].unique_index.should be_nil
800
+ end
801
+ end
802
+
803
+ describe '#valid?' do
804
+ describe 'when type primitive is a Boolean' do
805
+ before do
806
+ @property = Image.properties[:visible]
807
+ end
808
+
809
+ [ true, false ].each do |value|
810
+ it "returns true when value is #{value.inspect}" do
811
+ @property.valid?(value).should be_true
812
+ end
813
+ end
814
+
815
+ [ 'true', 'TRUE', '1', 1, 't', 'T', 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
816
+ it "returns false for #{value.inspect}" do
817
+ @property.valid?(value).should be_false
818
+ end
819
+ end
820
+ end
821
+ end
822
+
823
+ describe '#value' do
824
+ it 'returns value for core types'
825
+
826
+ it 'triggers dump operation for custom types'
827
+ end
828
+ end
829
+ end # DataMapper::Property