ghost_dm-core 1.3.0.beta

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 (254) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +35 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +65 -0
  6. data/LICENSE +20 -0
  7. data/README.md +269 -0
  8. data/Rakefile +4 -0
  9. data/dm-core.gemspec +24 -0
  10. data/lib/dm-core.rb +292 -0
  11. data/lib/dm-core/adapters.rb +222 -0
  12. data/lib/dm-core/adapters/abstract_adapter.rb +237 -0
  13. data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
  14. data/lib/dm-core/associations/many_to_many.rb +499 -0
  15. data/lib/dm-core/associations/many_to_one.rb +290 -0
  16. data/lib/dm-core/associations/one_to_many.rb +348 -0
  17. data/lib/dm-core/associations/one_to_one.rb +86 -0
  18. data/lib/dm-core/associations/relationship.rb +663 -0
  19. data/lib/dm-core/backwards.rb +13 -0
  20. data/lib/dm-core/collection.rb +1515 -0
  21. data/lib/dm-core/core_ext/kernel.rb +23 -0
  22. data/lib/dm-core/core_ext/pathname.rb +6 -0
  23. data/lib/dm-core/core_ext/symbol.rb +10 -0
  24. data/lib/dm-core/identity_map.rb +7 -0
  25. data/lib/dm-core/model.rb +874 -0
  26. data/lib/dm-core/model/hook.rb +103 -0
  27. data/lib/dm-core/model/is.rb +32 -0
  28. data/lib/dm-core/model/property.rb +249 -0
  29. data/lib/dm-core/model/relationship.rb +378 -0
  30. data/lib/dm-core/model/scope.rb +89 -0
  31. data/lib/dm-core/property.rb +866 -0
  32. data/lib/dm-core/property/binary.rb +21 -0
  33. data/lib/dm-core/property/boolean.rb +20 -0
  34. data/lib/dm-core/property/class.rb +17 -0
  35. data/lib/dm-core/property/date.rb +10 -0
  36. data/lib/dm-core/property/date_time.rb +10 -0
  37. data/lib/dm-core/property/decimal.rb +36 -0
  38. data/lib/dm-core/property/discriminator.rb +44 -0
  39. data/lib/dm-core/property/float.rb +16 -0
  40. data/lib/dm-core/property/integer.rb +22 -0
  41. data/lib/dm-core/property/invalid_value_error.rb +22 -0
  42. data/lib/dm-core/property/lookup.rb +27 -0
  43. data/lib/dm-core/property/numeric.rb +38 -0
  44. data/lib/dm-core/property/object.rb +34 -0
  45. data/lib/dm-core/property/serial.rb +14 -0
  46. data/lib/dm-core/property/string.rb +38 -0
  47. data/lib/dm-core/property/text.rb +9 -0
  48. data/lib/dm-core/property/time.rb +10 -0
  49. data/lib/dm-core/property_set.rb +177 -0
  50. data/lib/dm-core/query.rb +1366 -0
  51. data/lib/dm-core/query/conditions/comparison.rb +911 -0
  52. data/lib/dm-core/query/conditions/operation.rb +721 -0
  53. data/lib/dm-core/query/direction.rb +36 -0
  54. data/lib/dm-core/query/operator.rb +35 -0
  55. data/lib/dm-core/query/path.rb +114 -0
  56. data/lib/dm-core/query/sort.rb +39 -0
  57. data/lib/dm-core/relationship_set.rb +72 -0
  58. data/lib/dm-core/repository.rb +226 -0
  59. data/lib/dm-core/resource.rb +1214 -0
  60. data/lib/dm-core/resource/persistence_state.rb +75 -0
  61. data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
  62. data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
  63. data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
  64. data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
  65. data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
  66. data/lib/dm-core/resource/persistence_state/transient.rb +80 -0
  67. data/lib/dm-core/spec/lib/adapter_helpers.rb +64 -0
  68. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  69. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  70. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  71. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  72. data/lib/dm-core/spec/setup.rb +174 -0
  73. data/lib/dm-core/spec/shared/adapter_spec.rb +341 -0
  74. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  75. data/lib/dm-core/spec/shared/resource_spec.rb +1232 -0
  76. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  77. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +176 -0
  78. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  79. data/lib/dm-core/support/assertions.rb +8 -0
  80. data/lib/dm-core/support/chainable.rb +18 -0
  81. data/lib/dm-core/support/deprecate.rb +12 -0
  82. data/lib/dm-core/support/descendant_set.rb +89 -0
  83. data/lib/dm-core/support/equalizer.rb +48 -0
  84. data/lib/dm-core/support/ext/array.rb +22 -0
  85. data/lib/dm-core/support/ext/blank.rb +25 -0
  86. data/lib/dm-core/support/ext/hash.rb +67 -0
  87. data/lib/dm-core/support/ext/module.rb +47 -0
  88. data/lib/dm-core/support/ext/object.rb +57 -0
  89. data/lib/dm-core/support/ext/string.rb +24 -0
  90. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  91. data/lib/dm-core/support/hook.rb +405 -0
  92. data/lib/dm-core/support/inflections.rb +60 -0
  93. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  94. data/lib/dm-core/support/inflector/methods.rb +151 -0
  95. data/lib/dm-core/support/lazy_array.rb +451 -0
  96. data/lib/dm-core/support/local_object_space.rb +13 -0
  97. data/lib/dm-core/support/logger.rb +201 -0
  98. data/lib/dm-core/support/mash.rb +176 -0
  99. data/lib/dm-core/support/naming_conventions.rb +90 -0
  100. data/lib/dm-core/support/ordered_set.rb +380 -0
  101. data/lib/dm-core/support/subject.rb +33 -0
  102. data/lib/dm-core/support/subject_set.rb +250 -0
  103. data/lib/dm-core/version.rb +3 -0
  104. data/script/performance.rb +275 -0
  105. data/script/profile.rb +218 -0
  106. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  107. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
  108. data/spec/public/associations/many_to_many_spec.rb +197 -0
  109. data/spec/public/associations/many_to_one_spec.rb +83 -0
  110. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  111. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  112. data/spec/public/associations/one_to_many_spec.rb +81 -0
  113. data/spec/public/associations/one_to_one_spec.rb +176 -0
  114. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  115. data/spec/public/collection_spec.rb +69 -0
  116. data/spec/public/finalize_spec.rb +76 -0
  117. data/spec/public/model/hook_spec.rb +246 -0
  118. data/spec/public/model/property_spec.rb +88 -0
  119. data/spec/public/model/relationship_spec.rb +1040 -0
  120. data/spec/public/model_spec.rb +462 -0
  121. data/spec/public/property/binary_spec.rb +41 -0
  122. data/spec/public/property/boolean_spec.rb +22 -0
  123. data/spec/public/property/class_spec.rb +28 -0
  124. data/spec/public/property/date_spec.rb +22 -0
  125. data/spec/public/property/date_time_spec.rb +22 -0
  126. data/spec/public/property/decimal_spec.rb +23 -0
  127. data/spec/public/property/discriminator_spec.rb +135 -0
  128. data/spec/public/property/float_spec.rb +22 -0
  129. data/spec/public/property/integer_spec.rb +22 -0
  130. data/spec/public/property/object_spec.rb +107 -0
  131. data/spec/public/property/serial_spec.rb +22 -0
  132. data/spec/public/property/string_spec.rb +22 -0
  133. data/spec/public/property/text_spec.rb +63 -0
  134. data/spec/public/property/time_spec.rb +22 -0
  135. data/spec/public/property_spec.rb +341 -0
  136. data/spec/public/resource_spec.rb +288 -0
  137. data/spec/public/sel_spec.rb +53 -0
  138. data/spec/public/setup_spec.rb +145 -0
  139. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  140. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  141. data/spec/public/shared/collection_shared_spec.rb +1667 -0
  142. data/spec/public/shared/finder_shared_spec.rb +1629 -0
  143. data/spec/rcov.opts +6 -0
  144. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  145. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  146. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  147. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  148. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  149. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  150. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  151. data/spec/semipublic/associations_spec.rb +177 -0
  152. data/spec/semipublic/collection_spec.rb +110 -0
  153. data/spec/semipublic/model_spec.rb +96 -0
  154. data/spec/semipublic/property/binary_spec.rb +13 -0
  155. data/spec/semipublic/property/boolean_spec.rb +47 -0
  156. data/spec/semipublic/property/class_spec.rb +33 -0
  157. data/spec/semipublic/property/date_spec.rb +43 -0
  158. data/spec/semipublic/property/date_time_spec.rb +46 -0
  159. data/spec/semipublic/property/decimal_spec.rb +83 -0
  160. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  161. data/spec/semipublic/property/float_spec.rb +82 -0
  162. data/spec/semipublic/property/integer_spec.rb +82 -0
  163. data/spec/semipublic/property/lookup_spec.rb +29 -0
  164. data/spec/semipublic/property/serial_spec.rb +13 -0
  165. data/spec/semipublic/property/string_spec.rb +13 -0
  166. data/spec/semipublic/property/text_spec.rb +31 -0
  167. data/spec/semipublic/property/time_spec.rb +50 -0
  168. data/spec/semipublic/property_spec.rb +114 -0
  169. data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
  170. data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
  171. data/spec/semipublic/query/path_spec.rb +471 -0
  172. data/spec/semipublic/query_spec.rb +3682 -0
  173. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  174. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  175. data/spec/semipublic/resource/state/dirty_spec.rb +162 -0
  176. data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
  177. data/spec/semipublic/resource/state/transient_spec.rb +162 -0
  178. data/spec/semipublic/resource/state_spec.rb +230 -0
  179. data/spec/semipublic/resource_spec.rb +23 -0
  180. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  181. data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
  182. data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
  183. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  184. data/spec/spec.opts +5 -0
  185. data/spec/spec_helper.rb +38 -0
  186. data/spec/support/core_ext/hash.rb +10 -0
  187. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  188. data/spec/support/properties/huge_integer.rb +17 -0
  189. data/spec/unit/array_spec.rb +23 -0
  190. data/spec/unit/blank_spec.rb +73 -0
  191. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  192. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  193. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  194. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  195. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  196. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  197. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  198. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  199. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  200. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  201. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  202. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  203. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  204. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  205. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  206. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  207. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  208. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  216. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  217. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  218. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  219. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  220. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  221. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  222. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  223. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  224. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  226. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  227. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  228. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  229. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  230. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  231. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  232. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  233. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  237. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  239. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  240. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  241. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  242. data/spec/unit/hash_spec.rb +28 -0
  243. data/spec/unit/hook_spec.rb +1235 -0
  244. data/spec/unit/inflections_spec.rb +16 -0
  245. data/spec/unit/lazy_array_spec.rb +1949 -0
  246. data/spec/unit/mash_spec.rb +312 -0
  247. data/spec/unit/module_spec.rb +71 -0
  248. data/spec/unit/object_spec.rb +38 -0
  249. data/spec/unit/try_dup_spec.rb +46 -0
  250. data/tasks/ci.rake +1 -0
  251. data/tasks/spec.rake +38 -0
  252. data/tasks/yard.rake +9 -0
  253. data/tasks/yardstick.rake +19 -0
  254. metadata +365 -0
@@ -0,0 +1,246 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataMapper::Model::Hook do
4
+ before :all do
5
+ class ::ModelHookSpecs
6
+ include DataMapper::Resource
7
+
8
+ property :id, Serial
9
+ property :value, Integer, :required => true, :default => 1
10
+
11
+ def an_instance_method
12
+ end
13
+ end
14
+
15
+ class ::ModelHookSpecsSubclass < ModelHookSpecs; end
16
+
17
+ DataMapper.finalize
18
+ end
19
+
20
+ before :all do
21
+ @resource = ModelHookSpecs.new
22
+ end
23
+
24
+ describe '#before' do
25
+ describe 'an instance method' do
26
+ before do
27
+ @hooks = hooks = []
28
+ ModelHookSpecs.before(:an_instance_method) { hooks << :before_instance_method }
29
+
30
+ @resource.an_instance_method
31
+ end
32
+
33
+ it 'should execute before instance method hook' do
34
+ @hooks.should == [ :before_instance_method ]
35
+ end
36
+ end
37
+
38
+ describe 'save' do
39
+ supported_by :all do
40
+ before do
41
+ @hooks = hooks = []
42
+ ModelHookSpecs.before(:save) { hooks << :before_save }
43
+
44
+ @resource.save
45
+ end
46
+
47
+ it 'should execute before save hook' do
48
+ @hooks.should == [ :before_save ]
49
+ end
50
+ end
51
+ end
52
+
53
+ describe 'create' do
54
+ supported_by :all do
55
+ before do
56
+ @hooks = hooks = []
57
+ ModelHookSpecs.before(:create) { hooks << :before_create }
58
+
59
+ @resource.save
60
+ end
61
+
62
+ it 'should execute before create hook' do
63
+ @hooks.should == [ :before_create ]
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'update' do
69
+ supported_by :all do
70
+ before do
71
+ @hooks = hooks = []
72
+ ModelHookSpecs.before(:update) { hooks << :before_update }
73
+
74
+ @resource.save
75
+ @resource.update(:value => 2)
76
+ end
77
+
78
+ it 'should execute before update hook' do
79
+ @hooks.should == [ :before_update ]
80
+ end
81
+ end
82
+ end
83
+
84
+ describe 'destroy' do
85
+ supported_by :all do
86
+ before do
87
+ @hooks = hooks = []
88
+ ModelHookSpecs.before(:destroy) { hooks << :before_destroy }
89
+
90
+ @resource.save
91
+ @resource.destroy
92
+ end
93
+
94
+ it 'should execute before destroy hook' do
95
+ @hooks.should == [ :before_destroy ]
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'with an inherited hook' do
101
+ supported_by :all do
102
+ before do
103
+ @hooks = hooks = []
104
+ ModelHookSpecs.before(:an_instance_method) { hooks << :inherited_hook }
105
+ end
106
+
107
+ it 'should execute inherited hook' do
108
+ ModelHookSpecsSubclass.new.an_instance_method
109
+ @hooks.should == [ :inherited_hook ]
110
+ end
111
+ end
112
+ end
113
+
114
+ describe 'with a hook declared in the subclasss' do
115
+ supported_by :all do
116
+ before do
117
+ @hooks = hooks = []
118
+ ModelHookSpecsSubclass.before(:an_instance_method) { hooks << :hook }
119
+ end
120
+
121
+ it 'should execute hook' do
122
+ ModelHookSpecsSubclass.new.an_instance_method
123
+ @hooks.should == [ :hook ]
124
+ end
125
+
126
+ it 'should not alter hooks in the parent class' do
127
+ @hooks.should be_empty
128
+ ModelHookSpecs.new.an_instance_method
129
+ @hooks.should == []
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ describe '#after' do
136
+ describe 'an instance method' do
137
+ before do
138
+ @hooks = hooks = []
139
+ ModelHookSpecs.after(:an_instance_method) { hooks << :after_instance_method }
140
+
141
+ @resource.an_instance_method
142
+ end
143
+
144
+ it 'should execute after instance method hook' do
145
+ @hooks.should == [ :after_instance_method ]
146
+ end
147
+ end
148
+
149
+ describe 'save' do
150
+ supported_by :all do
151
+ before do
152
+ @hooks = hooks = []
153
+ ModelHookSpecs.after(:save) { hooks << :after_save }
154
+
155
+ @resource.save
156
+ end
157
+
158
+ it 'should execute after save hook' do
159
+ @hooks.should == [ :after_save ]
160
+ end
161
+ end
162
+ end
163
+
164
+ describe 'create' do
165
+ supported_by :all do
166
+ before do
167
+ @hooks = hooks = []
168
+ ModelHookSpecs.after(:create) { hooks << :after_create }
169
+
170
+ @resource.save
171
+ end
172
+
173
+ it 'should execute after create hook' do
174
+ @hooks.should == [ :after_create ]
175
+ end
176
+ end
177
+ end
178
+
179
+ describe 'update' do
180
+ supported_by :all do
181
+ before do
182
+ @hooks = hooks = []
183
+ ModelHookSpecs.after(:update) { hooks << :after_update }
184
+
185
+ @resource.save
186
+ @resource.update(:value => 2)
187
+ end
188
+
189
+ it 'should execute after update hook' do
190
+ @hooks.should == [ :after_update ]
191
+ end
192
+ end
193
+ end
194
+
195
+ describe 'destroy' do
196
+ supported_by :all do
197
+ before do
198
+ @hooks = hooks = []
199
+ ModelHookSpecs.after(:destroy) { hooks << :after_destroy }
200
+
201
+ @resource.save
202
+ @resource.destroy
203
+ end
204
+
205
+ it 'should execute after destroy hook' do
206
+ @hooks.should == [ :after_destroy ]
207
+ end
208
+ end
209
+ end
210
+
211
+ describe 'with an inherited hook' do
212
+ supported_by :all do
213
+ before do
214
+ @hooks = hooks = []
215
+ ModelHookSpecs.after(:an_instance_method) { hooks << :inherited_hook }
216
+ end
217
+
218
+ it 'should execute inherited hook' do
219
+ ModelHookSpecsSubclass.new.an_instance_method
220
+ @hooks.should == [ :inherited_hook ]
221
+ end
222
+ end
223
+ end
224
+
225
+ describe 'with a hook declared in the subclasss' do
226
+ supported_by :all do
227
+ before do
228
+ @hooks = hooks = []
229
+ ModelHookSpecsSubclass.after(:an_instance_method) { hooks << :hook }
230
+ end
231
+
232
+ it 'should execute hook' do
233
+ ModelHookSpecsSubclass.new.an_instance_method
234
+ @hooks.should == [ :hook ]
235
+ end
236
+
237
+ it 'should not alter hooks in the parent class' do
238
+ @hooks.should be_empty
239
+ ModelHookSpecs.new.an_instance_method
240
+ @hooks.should == []
241
+ end
242
+ end
243
+ end
244
+
245
+ end
246
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "a correct property declaration" do
4
+ it 'should define a name accessor' do
5
+ @model.should_not be_method_defined(@property_name)
6
+ subject
7
+ @model.should be_method_defined(@property_name)
8
+ end
9
+
10
+ it 'should define a name= mutator' do
11
+ @model.should_not be_method_defined(:"#{@property_name}=")
12
+ subject
13
+ @model.should be_method_defined(:"#{@property_name}=")
14
+ end
15
+ end
16
+
17
+ describe DataMapper::Model::Property do
18
+ before do
19
+ Object.send(:remove_const, :ModelPropertySpecs) if defined?(ModelPropertySpecs)
20
+ class ::ModelPropertySpecs
21
+ include DataMapper::Resource
22
+
23
+ property :id, Serial
24
+ end
25
+ DataMapper.finalize
26
+ end
27
+
28
+ describe '#property' do
29
+ context "using default repository" do
30
+ before do
31
+ Object.send(:remove_const, :UserDefault) if defined?(::UserDefault)
32
+
33
+ class ::UserDefault
34
+ include DataMapper::Resource
35
+ property :id, Serial
36
+ end
37
+
38
+ @model = ::UserDefault
39
+ @property_name = :name
40
+ end
41
+
42
+ subject do
43
+ ::UserDefault.property(:name, String)
44
+ end
45
+
46
+ it_should_behave_like "a correct property declaration"
47
+ end
48
+
49
+ context "using alternate repository" do
50
+ before do
51
+ Object.send(:remove_const, :UserAlternate) if defined?(::UserAlternate)
52
+
53
+ class ::UserAlternate
54
+ include DataMapper::Resource
55
+ property :id, Serial
56
+ repository(:alternate) { property :age, Integer }
57
+ end
58
+
59
+ @model = UserAlternate
60
+ @property_name = :alt_name
61
+ end
62
+
63
+ subject do
64
+ ::UserAlternate.property(:alt_name, String)
65
+ end
66
+
67
+ it_should_behave_like "a correct property declaration"
68
+ end
69
+
70
+ it 'should raise an exception if the method exists' do
71
+ lambda {
72
+ ModelPropertySpecs.property(:key, String)
73
+ }.should raise_error(ArgumentError, '+name+ was :key, which cannot be used as a property name since it collides with an existing method or a query option')
74
+ end
75
+
76
+ it 'should raise an exception if the property is boolean and method with question mark already exists' do
77
+ lambda {
78
+ ModelPropertySpecs.property(:destroyed, DataMapper::Property::Boolean)
79
+ }.should raise_error(ArgumentError, '+name+ was :destroyed, which cannot be used as a property name since it collides with an existing method or a query option')
80
+ end
81
+
82
+ it 'should raise an exception if the name is the same as one of the query options' do
83
+ lambda {
84
+ ModelPropertySpecs.property(:order, String)
85
+ }.should raise_error(ArgumentError, '+name+ was :order, which cannot be used as a property name since it collides with an existing method or a query option')
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,1040 @@
1
+ require 'spec_helper'
2
+
3
+ share_examples_for 'it creates a one accessor' do
4
+ describe 'accessor' do
5
+ describe 'when there is no associated resource' do
6
+ describe 'without a query' do
7
+ before :all do
8
+ @return = @car.__send__(@name)
9
+ end
10
+
11
+ it 'should return nil' do
12
+ @return.should be_nil
13
+ end
14
+ end
15
+
16
+ describe 'with a query' do
17
+ before :all do
18
+ @return = @car.__send__(@name, :id => 99)
19
+ end
20
+
21
+ it 'should return nil' do
22
+ @return.should be_nil
23
+ end
24
+ end
25
+ end
26
+
27
+ describe 'when there is an associated resource' do
28
+ before :all do
29
+ @expected = @model.new
30
+ @car.__send__("#{@name}=", @expected)
31
+ end
32
+
33
+ describe 'without a query' do
34
+ before :all do
35
+ @return = @car.__send__(@name)
36
+ end
37
+
38
+ it 'should return a Resource' do
39
+ @return.should be_kind_of(DataMapper::Resource)
40
+ end
41
+
42
+ it 'should return the expected Resource' do
43
+ @return.should equal(@expected)
44
+ end
45
+ end
46
+
47
+ describe 'with a query' do
48
+ before :all do
49
+ @car.save # save @car and @expected to set @expected.id
50
+
51
+ @expected.id.should_not be_nil
52
+
53
+ @return = @car.__send__(@name, :id => @expected.id)
54
+ end
55
+
56
+ it 'should return a Resource' do
57
+ @return.should be_kind_of(DataMapper::Resource)
58
+ end
59
+
60
+ it 'should return the expected Resource' do
61
+ @return.should == @expected
62
+ end
63
+ end
64
+ end
65
+
66
+ describe 'when the target model is scoped' do
67
+ before :all do
68
+ @resource = @model.new
69
+ @car.__send__("#{@name}=", @resource)
70
+ @car.save
71
+
72
+ # set the model scope to not match the expected resource
73
+ @model.default_scope.update(:id.not => @resource.id)
74
+
75
+ @return = @car.model.get(*@car.key).__send__(@name)
76
+ end
77
+
78
+ it 'should return nil' do
79
+ @return.should be_nil
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ share_examples_for 'it creates a one mutator' do
86
+ describe 'mutator' do
87
+ describe 'when setting a Resource' do
88
+ before :all do
89
+ @expected = @model.new
90
+
91
+ @return = @car.__send__("#{@name}=", @expected)
92
+ end
93
+
94
+ it 'should return the expected Resource' do
95
+ @return.should equal(@expected)
96
+ end
97
+
98
+ it 'should set the Resource' do
99
+ @car.__send__(@name).should equal(@expected)
100
+ end
101
+
102
+ it 'should relate associated Resource' do
103
+ relationship = Car.relationships[@name]
104
+ many_to_one = relationship.kind_of?(DataMapper::Associations::ManyToOne::Relationship)
105
+ one_to_one_through = relationship.kind_of?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
106
+
107
+ pending_if many_to_one || one_to_one_through do
108
+ @expected.car.should == @car
109
+ end
110
+ end
111
+
112
+ it 'should persist the Resource' do
113
+ @car.save.should be(true)
114
+ @car.model.get(*@car.key).__send__(@name).should == @expected
115
+ end
116
+
117
+ it 'should persist the associated Resource' do
118
+ @car.save.should be(true)
119
+ @expected.should be_saved
120
+ @expected.model.get(*@expected.key).car.should == @car
121
+ end
122
+ end
123
+
124
+ describe 'when setting a Hash' do
125
+ before :all do
126
+ @car.__send__("#{@name}=", @model.new)
127
+
128
+ attributes = { :id => 10 }
129
+ @expected = @model.new(attributes)
130
+
131
+ @return = @car.__send__("#{@name}=", attributes)
132
+ end
133
+
134
+ it 'should return the expected Resource' do
135
+ @return.should == @expected
136
+ end
137
+
138
+ it 'should set the Resource' do
139
+ @car.__send__(@name).should equal(@return)
140
+ end
141
+
142
+ it 'should relate associated Resource' do
143
+ relationship = Car.relationships[@name]
144
+ many_to_one = relationship.kind_of?(DataMapper::Associations::ManyToOne::Relationship)
145
+ one_to_one_through = relationship.kind_of?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
146
+
147
+ pending_if many_to_one || one_to_one_through do
148
+ @return.car.should == @car
149
+ end
150
+ end
151
+
152
+ it 'should persist the Resource' do
153
+ @car.save.should be(true)
154
+ @car.model.get(*@car.key).__send__(@name).should == @return
155
+ end
156
+
157
+ it 'should persist the associated Resource' do
158
+ @car.save.should be(true)
159
+ @return.should be_saved
160
+ @return.model.get(*@return.key).car.should == @car
161
+ end
162
+ end
163
+
164
+ describe 'when setting nil' do
165
+ before :all do
166
+ @car.__send__("#{@name}=", @model.new)
167
+
168
+ @return = @car.__send__("#{@name}=", nil)
169
+ end
170
+
171
+ it 'should return nil' do
172
+ @return.should be_nil
173
+ end
174
+
175
+ it 'should set nil' do
176
+ @car.__send__(@name).should be_nil
177
+ end
178
+
179
+ it 'should persist as nil' do
180
+ @car.save.should be(true)
181
+ @car.model.get(*@car.key).__send__(@name).should be_nil
182
+ end
183
+ end
184
+
185
+ describe 'when changing the Resource' do
186
+ before :all do
187
+ @car.__send__("#{@name}=", @model.new)
188
+ @expected = @model.new
189
+
190
+ @return = @car.__send__("#{@name}=", @expected)
191
+ end
192
+
193
+ it 'should return the expected Resource' do
194
+ @return.should equal(@expected)
195
+ end
196
+
197
+ it 'should set the Resource' do
198
+ @car.__send__(@name).should equal(@expected)
199
+ end
200
+
201
+ it 'should relate associated Resource' do
202
+ relationship = Car.relationships[@name]
203
+ many_to_one = relationship.kind_of?(DataMapper::Associations::ManyToOne::Relationship)
204
+ one_to_one_through = relationship.kind_of?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
205
+
206
+ pending_if 'should create back-reference', many_to_one || one_to_one_through do
207
+ @expected.car.should == @car
208
+ end
209
+ end
210
+
211
+ it 'should persist the Resource' do
212
+ @car.save.should be(true)
213
+ @car.model.get(*@car.key).__send__(@name).should == @expected
214
+ end
215
+
216
+ it 'should persist the associated Resource' do
217
+ @car.save.should be(true)
218
+ @expected.should be_saved
219
+ @expected.model.get(*@expected.key).car.should == @car
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ share_examples_for 'it creates a many accessor' do
226
+ describe 'accessor' do
227
+ describe 'when there is no child resource and the source is saved' do
228
+ before :all do
229
+ @car.save.should be(true)
230
+ @return = @car.__send__(@name)
231
+ end
232
+
233
+ it 'should return a Collection' do
234
+ @return.should be_kind_of(DataMapper::Collection)
235
+ end
236
+
237
+ it 'should return an empty Collection' do
238
+ @return.should be_empty
239
+ end
240
+ end
241
+
242
+ describe 'when there is no child resource and the source is not saved' do
243
+ before :all do
244
+ @return = @car.__send__(@name)
245
+ end
246
+
247
+ it 'should return a Collection' do
248
+ @return.should be_kind_of(DataMapper::Collection)
249
+ end
250
+
251
+ it 'should return an empty Collection' do
252
+ @return.should be_empty
253
+ end
254
+ end
255
+
256
+ describe 'when there is a child resource' do
257
+ before :all do
258
+ @return = nil
259
+
260
+ @expected = @model.new
261
+ @car.__send__("#{@name}=", [ @expected ])
262
+
263
+ @return = @car.__send__(@name)
264
+ end
265
+
266
+ it 'should return a Collection' do
267
+ @return.should be_kind_of(DataMapper::Collection)
268
+ end
269
+
270
+ it 'should return expected Resources' do
271
+ @return.should == [ @expected ]
272
+ end
273
+ end
274
+
275
+ describe 'when the target model is scoped' do
276
+ before :all do
277
+ 2.times { @car.__send__(@name).new }
278
+ @car.save
279
+
280
+ @expected = @car.__send__(@name).first
281
+ @expected.should_not be_nil
282
+
283
+ # set the model scope to only return the first record
284
+ @model.default_scope.update(
285
+ Hash[ @model.key(@repository.name).zip(@expected.key) ]
286
+ )
287
+
288
+ @return = @car.model.get(*@car.key).__send__(@name)
289
+ end
290
+
291
+ it 'should return a Collection' do
292
+ @return.should be_kind_of(DataMapper::Collection)
293
+ end
294
+
295
+ it 'should return expected Resources' do
296
+ @return.should == [ @expected ]
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ share_examples_for 'it creates a many mutator' do
303
+ describe 'mutator' do
304
+ describe 'when setting an Array of Resources' do
305
+ before :all do
306
+ @expected = [ @model.new ]
307
+
308
+ @return = @car.__send__("#{@name}=", @expected)
309
+ end
310
+
311
+ it 'should return the expected Collection' do
312
+ @return.should == @expected
313
+ end
314
+
315
+ it 'should set the Collection' do
316
+ @car.__send__(@name).should == @expected
317
+ @car.__send__(@name).zip(@expected) { |value, expected| value.should equal(expected) }
318
+ end
319
+
320
+ it 'should relate the associated Collection' do
321
+ pending_if Car.relationships[@name].kind_of?(DataMapper::Associations::ManyToMany::Relationship) do
322
+ @expected.each { |resource| resource.car.should == @car }
323
+ end
324
+ end
325
+
326
+ it 'should persist the Collection' do
327
+ @car.save.should be(true)
328
+ @car.model.get(*@car.key).__send__(@name).should == @expected
329
+ end
330
+
331
+ it 'should persist the associated Resource' do
332
+ @car.save.should be(true)
333
+ @expected.each { |resource| resource.should be_saved }
334
+ @expected.each { |resource| resource.model.get(*resource.key).car.should == @car }
335
+ end
336
+ end
337
+
338
+ describe 'when setting an Array of Hashes' do
339
+ before :all do
340
+ attributes = { :id => 11 }
341
+ @hashes = [ attributes ]
342
+ @expected = [ @model.new(attributes) ]
343
+
344
+ @return = @car.__send__("#{@name}=", @hashes)
345
+ end
346
+
347
+ it 'should return the expected Collection' do
348
+ @return.should == @expected
349
+ end
350
+
351
+ it 'should set the Collection' do
352
+ @car.__send__(@name).should == @return
353
+ end
354
+
355
+ it 'should relate the associated Collection' do
356
+ pending_if Car.relationships[@name].kind_of?(DataMapper::Associations::ManyToMany::Relationship) do
357
+ @return.each { |resource| resource.car.should == @car }
358
+ end
359
+ end
360
+
361
+ it 'should persist the Collection' do
362
+ @car.save.should be(true)
363
+ @car.model.get(*@car.key).__send__(@name).should == @return
364
+ end
365
+
366
+ it 'should persist the associated Resource' do
367
+ @car.save.should be(true)
368
+ @return.each { |resource| resource.should be_saved }
369
+ @return.each { |resource| resource.model.get(*resource.key).car.should == @car }
370
+ end
371
+ end
372
+
373
+ describe 'when setting an empty collection' do
374
+ before :all do
375
+ @car.__send__("#{@name}=", [ @model.new ])
376
+
377
+ @return = @car.__send__("#{@name}=", [])
378
+ end
379
+
380
+ it 'should return a Collection' do
381
+ @return.should be_kind_of(DataMapper::Collection)
382
+ end
383
+
384
+ it 'should set an empty Collection' do
385
+ @car.__send__(@name).should be_empty
386
+ end
387
+
388
+ it 'should persist as an empty Collection' do
389
+ @car.save.should be(true)
390
+ @car.model.get(*@car.key).__send__(@name).should be_empty
391
+ end
392
+ end
393
+
394
+ describe 'when changing an associated collection' do
395
+ before :all do
396
+ @car.__send__("#{@name}=", [ @model.new ])
397
+
398
+ @expected = [ @model.new ]
399
+
400
+ @return = @car.__send__("#{@name}=", @expected)
401
+ end
402
+
403
+ it 'should return the expected Resource' do
404
+ @return.should == @expected
405
+ end
406
+
407
+ it 'should set the Resource' do
408
+ @car.__send__(@name).should == @expected
409
+ @car.__send__(@name).zip(@expected) { |value, expected| value.should equal(expected) }
410
+ end
411
+
412
+ it 'should relate associated Resource' do
413
+ pending_if Car.relationships[@name].kind_of?(DataMapper::Associations::ManyToMany::Relationship) do
414
+ @expected.each { |resource| resource.car.should == @car }
415
+ end
416
+ end
417
+
418
+ it 'should persist the Resource' do
419
+ @car.save.should be(true)
420
+ @car.model.get(*@car.key).__send__(@name).should == @expected
421
+ end
422
+
423
+ it 'should persist the associated Resource' do
424
+ @car.save.should be(true)
425
+ @expected.each { |resource| resource.should be_saved }
426
+ @expected.each { |resource| resource.model.get(*resource.key).car.should == @car }
427
+ end
428
+ end
429
+ end
430
+ end
431
+
432
+ describe DataMapper::Associations do
433
+ before :all do
434
+ class ::Car
435
+ include DataMapper::Resource
436
+
437
+ property :id, Serial
438
+ property :name, String
439
+ end
440
+
441
+ class ::Engine
442
+ include DataMapper::Resource
443
+
444
+ property :id, Serial
445
+ end
446
+
447
+ class ::Door
448
+ include DataMapper::Resource
449
+
450
+ property :id, Serial
451
+ end
452
+
453
+ class ::Window
454
+ include DataMapper::Resource
455
+
456
+ property :id, Serial
457
+ end
458
+ end
459
+
460
+ def n
461
+ 1.0/0
462
+ end
463
+
464
+ it { Engine.should respond_to(:belongs_to) }
465
+
466
+ describe '#belongs_to' do
467
+ before :all do
468
+ @model = Engine
469
+ @name = :engine
470
+
471
+ Car.belongs_to(@name, :required => false)
472
+ Engine.has(1, :car)
473
+ DataMapper.finalize
474
+ end
475
+
476
+ supported_by :all do
477
+ before :all do
478
+ @car = Car.new
479
+ end
480
+
481
+ it { @car.should respond_to(@name) }
482
+
483
+ it_should_behave_like 'it creates a one accessor'
484
+
485
+ it { @car.should respond_to("#{@name}=") }
486
+
487
+ it_should_behave_like 'it creates a one mutator'
488
+
489
+ describe 'with a :key option' do
490
+ before :all do
491
+ @relationship = Car.belongs_to("#{@name}_with_key".to_sym, @model, :required => false, :key => true)
492
+ DataMapper.finalize
493
+ end
494
+
495
+ it 'should create a foreign key that is part of the key' do
496
+ @relationship.child_key.each do |property|
497
+ property.should be_key
498
+ end
499
+ end
500
+ end
501
+
502
+ describe 'with a :unique option' do
503
+ let(:unique) { [ :one, :two, :three ] }
504
+
505
+ before :all do
506
+ @relationship = Car.belongs_to("#{@name}_with_unique".to_sym, @model, :unique => unique)
507
+ DataMapper.finalize
508
+ end
509
+
510
+ it 'should create a foreign key that is unique' do
511
+ @relationship.child_key.each do |property|
512
+ property.should be_unique
513
+ end
514
+ end
515
+
516
+ it 'should create a foreign key that has a unique index' do
517
+ @relationship.child_key.each do |property|
518
+ property.unique_index.should equal(unique)
519
+ end
520
+ end
521
+ end
522
+ end
523
+
524
+ # TODO: refactor these specs into above structure once they pass
525
+ describe 'pending query specs' do
526
+ before :all do
527
+ Car.has(1, :engine)
528
+ Engine.belongs_to(:car)
529
+ DataMapper.finalize
530
+ end
531
+
532
+ supported_by :all do
533
+ describe 'querying for a parent resource when only the foreign key is set' do
534
+ before :all do
535
+ # create a car that would be returned if the query is not
536
+ # scoped properly to retrieve @car
537
+ Car.create
538
+
539
+ @car = Car.create
540
+ engine = Engine.new(:car_id => @car.id)
541
+
542
+ @return = engine.car
543
+ end
544
+
545
+ it 'should return a Resource' do
546
+ @return.should be_kind_of(DataMapper::Resource)
547
+ end
548
+
549
+ it 'should return expected Resource' do
550
+ @return.should eql(@car)
551
+ end
552
+ end
553
+
554
+ describe 'querying for a parent resource' do
555
+ before :all do
556
+ @car = Car.create
557
+ @engine = Engine.create(:car => @car)
558
+ @resource = @engine.car(:id => @car.id)
559
+ end
560
+
561
+ it 'should return a Resource' do
562
+ @resource.should be_kind_of(DataMapper::Resource)
563
+ end
564
+
565
+ it 'should return expected Resource' do
566
+ @resource.should eql(@car)
567
+ end
568
+ end
569
+
570
+ describe 'querying for a parent resource that does not exist' do
571
+ before :all do
572
+ @car = Car.create
573
+ @engine = Engine.create(:car => @car)
574
+ @resource = @engine.car(:id.not => @car.id)
575
+ end
576
+
577
+ it 'should return nil' do
578
+ @resource.should be_nil
579
+ end
580
+ end
581
+
582
+ describe 'changing the parent resource' do
583
+ before :all do
584
+ @car = Car.create
585
+ @engine = Engine.new
586
+ @engine.car = @car
587
+ end
588
+
589
+ it 'should set the associated foreign key' do
590
+ @engine.car_id.should == @car.id
591
+ end
592
+
593
+ it 'should add the engine object to the car' do
594
+ pending 'Changing a belongs_to parent should add the object to the correct association' do
595
+ @car.engines.should be_include(@engine)
596
+ end
597
+ end
598
+ end
599
+
600
+ describe 'changing the parent foreign key' do
601
+ before :all do
602
+ @car = Car.create
603
+
604
+ @engine = Engine.new(:car_id => @car.id)
605
+ end
606
+
607
+ it 'should set the associated resource' do
608
+ @engine.car.should eql(@car)
609
+ end
610
+ end
611
+
612
+ describe 'changing an existing resource through the relation' do
613
+ before :all do
614
+ @car1 = Car.create
615
+ @car2 = Car.create
616
+ @engine = Engine.create(:car => @car1)
617
+ @engine.car = @car2
618
+ end
619
+
620
+ it 'should also change the foreign key' do
621
+ @engine.car_id.should == @car2.id
622
+ end
623
+
624
+ it 'should add the engine to the car' do
625
+ pending 'Changing a belongs_to parent should add the object to the correct association' do
626
+ @car2.engines.should be_include(@engine)
627
+ end
628
+ end
629
+ end
630
+
631
+ describe 'changing an existing resource through the relation' do
632
+ before :all do
633
+ @car1 = Car.create
634
+ @car2 = Car.create
635
+ @engine = Engine.create(:car => @car1)
636
+ @engine.car_id = @car2.id
637
+ end
638
+
639
+ it 'should also change the foreign key' do
640
+ @engine.car.should eql(@car2)
641
+ end
642
+
643
+ it 'should add the engine to the car' do
644
+ pending 'a change to the foreign key should also change the related object' do
645
+ @car2.engines.should be_include(@engine)
646
+ end
647
+ end
648
+ end
649
+ end
650
+ end
651
+
652
+ describe 'with a model' do
653
+ before :all do
654
+ Engine.belongs_to(:vehicle, Car)
655
+ DataMapper.finalize
656
+ end
657
+
658
+ it 'should set the relationship target model' do
659
+ Engine.relationships[:vehicle].target_model.should == Car
660
+ end
661
+ end
662
+
663
+ describe 'with a :model option' do
664
+ before :all do
665
+ Engine.belongs_to(:vehicle, :model => Car)
666
+ DataMapper.finalize
667
+ end
668
+
669
+ it 'should set the relationship target model' do
670
+ Engine.relationships[:vehicle].target_model.should == Car
671
+ end
672
+ end
673
+
674
+ describe 'with a single element as :child_key option' do
675
+ before :all do
676
+ Engine.belongs_to(:vehicle, :model => Car, :child_key => :bike_id)
677
+ DataMapper.finalize
678
+ end
679
+
680
+ it 'should set the relationship child key' do
681
+ Engine.relationships[:vehicle].child_key.map { |property| property.name }.should == [:bike_id]
682
+ end
683
+ end
684
+
685
+ describe 'with an array as :child_key option' do
686
+ before :all do
687
+ Engine.belongs_to(:vehicle, :model => Car, :child_key => [:bike_id])
688
+ DataMapper.finalize
689
+ end
690
+
691
+ it 'should set the relationship child key' do
692
+ Engine.relationships[:vehicle].child_key.map { |property| property.name }.should == [:bike_id]
693
+ end
694
+ end
695
+
696
+ describe 'with a single element as :parent_key option' do
697
+ before :all do
698
+ Engine.belongs_to(:vehicle, :model => Car, :parent_key => :name)
699
+ DataMapper.finalize
700
+ end
701
+
702
+ it 'should set the relationship parent key' do
703
+ Engine.relationships[:vehicle].parent_key.map { |property| property.name }.should == [:name]
704
+ end
705
+ end
706
+
707
+ describe 'with an array as :parent_key option' do
708
+ before :all do
709
+ Engine.belongs_to(:vehicle, :model => Car, :parent_key => [:name])
710
+ DataMapper.finalize
711
+ end
712
+
713
+ it 'should set the relationship parent key' do
714
+ Engine.relationships[:vehicle].parent_key.map { |property| property.name }.should == [:name]
715
+ end
716
+ end
717
+ end
718
+
719
+ it { Car.should respond_to(:has) }
720
+
721
+ describe '#has' do
722
+ describe '1' do
723
+ before :all do
724
+ @model = Engine
725
+ @name = :engine
726
+
727
+ Car.has(1, @name)
728
+ Engine.belongs_to(:car)
729
+ DataMapper.finalize
730
+ end
731
+
732
+ supported_by :all do
733
+ before :all do
734
+ @car = Car.new
735
+ end
736
+
737
+ it { @car.should respond_to(@name) }
738
+
739
+ it_should_behave_like 'it creates a one accessor'
740
+
741
+ it { @car.should respond_to("#{@name}=") }
742
+
743
+ it_should_behave_like 'it creates a one mutator'
744
+ end
745
+ end
746
+
747
+ describe '1 through' do
748
+ before :all do
749
+ @model = Engine
750
+ @name = :engine
751
+
752
+ Car.has(1, @name, :through => DataMapper::Resource)
753
+ Engine.has(1, :car, :through => DataMapper::Resource)
754
+ DataMapper.finalize
755
+ end
756
+
757
+ supported_by :all do
758
+ before :all do
759
+ @no_join = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter) ||
760
+ defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
761
+ end
762
+
763
+ before :all do
764
+ @car = Car.new
765
+ end
766
+
767
+ before do
768
+ pending if @no_join
769
+ end
770
+
771
+ it { @car.should respond_to(@name) }
772
+
773
+ it_should_behave_like 'it creates a one accessor'
774
+
775
+ it { @car.should respond_to("#{@name}=") }
776
+
777
+ it_should_behave_like 'it creates a one mutator'
778
+ end
779
+ end
780
+
781
+ describe 'n..n' do
782
+ before :all do
783
+ @model = Door
784
+ @name = :doors
785
+
786
+ Car.has(1..4, @name)
787
+ Door.belongs_to(:car, :required => false)
788
+ DataMapper.finalize
789
+ end
790
+
791
+ supported_by :all do
792
+ before :all do
793
+ @car = Car.new
794
+ end
795
+
796
+ it { @car.should respond_to(@name) }
797
+
798
+ it_should_behave_like 'it creates a many accessor'
799
+
800
+ it { @car.should respond_to("#{@name}=") }
801
+
802
+ it_should_behave_like 'it creates a many mutator'
803
+ end
804
+ end
805
+
806
+ describe 'n..n through' do
807
+ before :all do
808
+ @model = Window
809
+ @name = :windows
810
+
811
+ Window.has(1, :car, :through => DataMapper::Resource)
812
+ Car.has(1..4, :windows, :through => DataMapper::Resource)
813
+ DataMapper.finalize
814
+ end
815
+
816
+ supported_by :all do
817
+ before :all do
818
+ @no_join = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter) ||
819
+ defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
820
+ end
821
+
822
+ before :all do
823
+ @car = Car.new
824
+ end
825
+
826
+ before do
827
+ pending if @no_join
828
+ end
829
+
830
+ it { @car.should respond_to(@name) }
831
+
832
+ it_should_behave_like 'it creates a many accessor'
833
+
834
+ it { @car.should respond_to("#{@name}=") }
835
+
836
+ it_should_behave_like 'it creates a many mutator'
837
+ end
838
+ end
839
+
840
+ describe 'when the 3rd argument is a Model' do
841
+ before :all do
842
+ Car.has(1, :engine, Engine)
843
+ DataMapper.finalize
844
+ end
845
+
846
+ it 'should set the relationship target model' do
847
+ Car.relationships[:engine].target_model.should == Engine
848
+ end
849
+ end
850
+
851
+ describe 'when the 3rd argument is a String' do
852
+ before :all do
853
+ Car.has(1, :engine, 'Engine')
854
+ DataMapper.finalize
855
+ end
856
+
857
+ it 'should set the relationship target model' do
858
+ Car.relationships[:engine].target_model.should == Engine
859
+ end
860
+ end
861
+
862
+ it 'should raise an exception if the cardinality is not understood' do
863
+ lambda { Car.has(n..n, :doors) }.should raise_error(ArgumentError)
864
+ end
865
+
866
+ it 'should raise an exception if the minimum constraint is larger than the maximum' do
867
+ lambda { Car.has(2..1, :doors) }.should raise_error(ArgumentError)
868
+ end
869
+ end
870
+
871
+ describe 'property prefix inference' do
872
+ describe 'when a relationship has an inverse' do
873
+ before :all do
874
+ @engine_relationship = Car.has(1, :engine, :inverse => Engine.belongs_to(:sports_car, Car))
875
+ DataMapper.finalize
876
+ end
877
+
878
+ supported_by :all do
879
+ it 'should have a child key prefix the same as the inverse relationship' do
880
+ @engine_relationship.child_key.map { |property| property.name }.should == [ :sports_car_id ]
881
+ end
882
+ end
883
+ end
884
+
885
+ describe 'when a relationship does not have an inverse' do
886
+ before :all do
887
+ @engine_relationship = Car.has(1, :engine)
888
+ DataMapper.finalize
889
+ end
890
+
891
+ supported_by :all do
892
+ it 'should have a child key prefix inferred from the source model name' do
893
+ @engine_relationship.child_key.map { |property| property.name }.should == [ :car_id ]
894
+ end
895
+ end
896
+ end
897
+
898
+ describe 'when a relationship is inherited' do
899
+ describe 'has an inverse' do
900
+ before :all do
901
+ Car.property(:type, DataMapper::Property::Discriminator)
902
+
903
+ class ::ElectricCar < Car; end
904
+
905
+ Car.has(1, :engine, :inverse => Engine.belongs_to(:sports_car, Car))
906
+ DataMapper.finalize
907
+ end
908
+
909
+ supported_by :all do
910
+ before :all do
911
+ @engine_relationship = ElectricCar.relationships(@repository.name)[:engine]
912
+ end
913
+
914
+ it 'should have a source model equal to the ancestor' do
915
+ @engine_relationship.source_model.should equal(Car)
916
+ end
917
+
918
+ it 'should have a child key prefix the same as the inverse relationship' do
919
+ @engine_relationship.child_key.map { |property| property.name }.should == [ :sports_car_id ]
920
+ end
921
+ end
922
+ end
923
+
924
+ describe 'does not have an inverse' do
925
+ before :all do
926
+ Car.property(:type, DataMapper::Property::Discriminator)
927
+
928
+ class ::ElectricCar < Car; end
929
+
930
+ Car.has(1, :engine)
931
+ DataMapper.finalize
932
+ end
933
+
934
+ supported_by :all do
935
+ before :all do
936
+ @engine_relationship = ElectricCar.relationships(@repository.name)[:engine]
937
+ end
938
+
939
+ it 'should have a source model equal to the ancestor' do
940
+ @engine_relationship.source_model.should equal(Car)
941
+ end
942
+
943
+ it 'should have a child key prefix inferred from the source model name' do
944
+ @engine_relationship.child_key.map { |property| property.name }.should == [ :car_id ]
945
+ end
946
+ end
947
+ end
948
+ end
949
+
950
+ describe "when a subclass defines it's own relationship" do
951
+ describe 'has an inverse' do
952
+ before :all do
953
+ Car.property(:type, DataMapper::Property::Discriminator)
954
+
955
+ class ::ElectricCar < Car; end
956
+
957
+ ElectricCar.has(1, :engine, :inverse => Engine.belongs_to(:sports_car, Car))
958
+ DataMapper.finalize
959
+ end
960
+
961
+ supported_by :all do
962
+ before :all do
963
+ @engine_relationship = ElectricCar.relationships(@repository.name)[:engine]
964
+ end
965
+
966
+ it 'should have a source model equal to the descendant' do
967
+ @engine_relationship.source_model.should equal(ElectricCar)
968
+ end
969
+
970
+ it 'should have a child key prefix the same as the inverse relationship' do
971
+ @engine_relationship.child_key.map { |property| property.name }.should == [ :sports_car_id ]
972
+ end
973
+ end
974
+ end
975
+
976
+ describe 'does not have an inverse' do
977
+ before :all do
978
+ Car.property(:type, DataMapper::Property::Discriminator)
979
+
980
+ class ::ElectricCar < Car; end
981
+
982
+ ElectricCar.has(1, :engine)
983
+ DataMapper.finalize
984
+ end
985
+
986
+ supported_by :all do
987
+ before :all do
988
+ @engine_relationship = ElectricCar.relationships(@repository.name)[:engine]
989
+ end
990
+
991
+ it 'should have a source model equal to the descendant' do
992
+ @engine_relationship.source_model.should equal(ElectricCar)
993
+ end
994
+
995
+ it 'should have a child key prefix inferred from the source model name' do
996
+ @engine_relationship.child_key.map { |property| property.name }.should == [ :electric_car_id ]
997
+ end
998
+ end
999
+ end
1000
+ end
1001
+ end
1002
+
1003
+ describe 'child is also a parent' do
1004
+ before :all do
1005
+ class ::Employee
1006
+ include DataMapper::Resource
1007
+
1008
+ property :id, Serial
1009
+ property :name, String
1010
+
1011
+ belongs_to :company
1012
+ end
1013
+
1014
+ class ::Company
1015
+ include DataMapper::Resource
1016
+
1017
+ property :id, Serial
1018
+ property :name, String
1019
+
1020
+ belongs_to :owner, Employee, :required => false
1021
+ has n, :employees
1022
+ end
1023
+ DataMapper.finalize
1024
+ end
1025
+
1026
+ supported_by :all do
1027
+ before :all do
1028
+ @company = Company.create(:name => 'ACME Inc.')
1029
+ @employee = @company.employees.create(:name => 'Wil E. Coyote')
1030
+ end
1031
+
1032
+ it 'should save the child as a parent' do
1033
+ lambda {
1034
+ @company.owner = @employee
1035
+ @company.save.should be(true)
1036
+ }.should_not raise_error
1037
+ end
1038
+ end
1039
+ end
1040
+ end