sbf-dm-core 1.3.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (259) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +29 -0
  3. data/.document +5 -0
  4. data/.gitignore +44 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +468 -0
  7. data/.travis.yml +57 -0
  8. data/.yardopts +1 -0
  9. data/Gemfile +70 -0
  10. data/LICENSE +20 -0
  11. data/README.md +269 -0
  12. data/Rakefile +4 -0
  13. data/dm-core.gemspec +21 -0
  14. data/lib/dm-core/adapters/abstract_adapter.rb +233 -0
  15. data/lib/dm-core/adapters/in_memory_adapter.rb +110 -0
  16. data/lib/dm-core/adapters.rb +249 -0
  17. data/lib/dm-core/associations/many_to_many.rb +477 -0
  18. data/lib/dm-core/associations/many_to_one.rb +282 -0
  19. data/lib/dm-core/associations/one_to_many.rb +332 -0
  20. data/lib/dm-core/associations/one_to_one.rb +84 -0
  21. data/lib/dm-core/associations/relationship.rb +650 -0
  22. data/lib/dm-core/backwards.rb +11 -0
  23. data/lib/dm-core/collection.rb +1486 -0
  24. data/lib/dm-core/core_ext/kernel.rb +21 -0
  25. data/lib/dm-core/core_ext/pathname.rb +4 -0
  26. data/lib/dm-core/core_ext/symbol.rb +10 -0
  27. data/lib/dm-core/identity_map.rb +6 -0
  28. data/lib/dm-core/model/hook.rb +99 -0
  29. data/lib/dm-core/model/is.rb +30 -0
  30. data/lib/dm-core/model/property.rb +244 -0
  31. data/lib/dm-core/model/relationship.rb +366 -0
  32. data/lib/dm-core/model/scope.rb +87 -0
  33. data/lib/dm-core/model.rb +876 -0
  34. data/lib/dm-core/property/binary.rb +19 -0
  35. data/lib/dm-core/property/boolean.rb +35 -0
  36. data/lib/dm-core/property/class.rb +23 -0
  37. data/lib/dm-core/property/date.rb +45 -0
  38. data/lib/dm-core/property/date_time.rb +44 -0
  39. data/lib/dm-core/property/decimal.rb +47 -0
  40. data/lib/dm-core/property/discriminator.rb +40 -0
  41. data/lib/dm-core/property/float.rb +27 -0
  42. data/lib/dm-core/property/integer.rb +32 -0
  43. data/lib/dm-core/property/invalid_value_error.rb +17 -0
  44. data/lib/dm-core/property/lookup.rb +26 -0
  45. data/lib/dm-core/property/numeric.rb +35 -0
  46. data/lib/dm-core/property/object.rb +33 -0
  47. data/lib/dm-core/property/serial.rb +13 -0
  48. data/lib/dm-core/property/string.rb +47 -0
  49. data/lib/dm-core/property/text.rb +12 -0
  50. data/lib/dm-core/property/time.rb +46 -0
  51. data/lib/dm-core/property/typecast/numeric.rb +32 -0
  52. data/lib/dm-core/property/typecast/time.rb +33 -0
  53. data/lib/dm-core/property.rb +856 -0
  54. data/lib/dm-core/property_set.rb +177 -0
  55. data/lib/dm-core/query/conditions/comparison.rb +886 -0
  56. data/lib/dm-core/query/conditions/operation.rb +710 -0
  57. data/lib/dm-core/query/direction.rb +33 -0
  58. data/lib/dm-core/query/operator.rb +34 -0
  59. data/lib/dm-core/query/path.rb +113 -0
  60. data/lib/dm-core/query/sort.rb +38 -0
  61. data/lib/dm-core/query.rb +1352 -0
  62. data/lib/dm-core/relationship_set.rb +69 -0
  63. data/lib/dm-core/repository.rb +226 -0
  64. data/lib/dm-core/resource/persistence_state/clean.rb +36 -0
  65. data/lib/dm-core/resource/persistence_state/deleted.rb +26 -0
  66. data/lib/dm-core/resource/persistence_state/dirty.rb +91 -0
  67. data/lib/dm-core/resource/persistence_state/immutable.rb +32 -0
  68. data/lib/dm-core/resource/persistence_state/persisted.rb +25 -0
  69. data/lib/dm-core/resource/persistence_state/transient.rb +87 -0
  70. data/lib/dm-core/resource/persistence_state.rb +70 -0
  71. data/lib/dm-core/resource.rb +1220 -0
  72. data/lib/dm-core/spec/lib/adapter_helpers.rb +63 -0
  73. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  74. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  75. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  76. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  77. data/lib/dm-core/spec/setup.rb +164 -0
  78. data/lib/dm-core/spec/shared/adapter_spec.rb +366 -0
  79. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  80. data/lib/dm-core/spec/shared/resource_spec.rb +1221 -0
  81. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  82. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +184 -0
  83. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  84. data/lib/dm-core/support/assertions.rb +8 -0
  85. data/lib/dm-core/support/chainable.rb +18 -0
  86. data/lib/dm-core/support/deprecate.rb +12 -0
  87. data/lib/dm-core/support/descendant_set.rb +89 -0
  88. data/lib/dm-core/support/equalizer.rb +48 -0
  89. data/lib/dm-core/support/ext/array.rb +22 -0
  90. data/lib/dm-core/support/ext/blank.rb +25 -0
  91. data/lib/dm-core/support/ext/hash.rb +67 -0
  92. data/lib/dm-core/support/ext/module.rb +47 -0
  93. data/lib/dm-core/support/ext/object.rb +57 -0
  94. data/lib/dm-core/support/ext/string.rb +24 -0
  95. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  96. data/lib/dm-core/support/hook.rb +388 -0
  97. data/lib/dm-core/support/inflections.rb +60 -0
  98. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  99. data/lib/dm-core/support/inflector/methods.rb +151 -0
  100. data/lib/dm-core/support/lazy_array.rb +451 -0
  101. data/lib/dm-core/support/local_object_space.rb +13 -0
  102. data/lib/dm-core/support/logger.rb +201 -0
  103. data/lib/dm-core/support/mash.rb +176 -0
  104. data/lib/dm-core/support/naming_conventions.rb +109 -0
  105. data/lib/dm-core/support/ordered_set.rb +381 -0
  106. data/lib/dm-core/support/subject.rb +33 -0
  107. data/lib/dm-core/support/subject_set.rb +251 -0
  108. data/lib/dm-core/version.rb +3 -0
  109. data/lib/dm-core.rb +274 -0
  110. data/script/performance.rb +275 -0
  111. data/script/profile.rb +218 -0
  112. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  113. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +69 -0
  114. data/spec/public/associations/many_to_many_spec.rb +197 -0
  115. data/spec/public/associations/many_to_one_spec.rb +83 -0
  116. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  117. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  118. data/spec/public/associations/one_to_many_spec.rb +81 -0
  119. data/spec/public/associations/one_to_one_spec.rb +176 -0
  120. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  121. data/spec/public/collection_spec.rb +69 -0
  122. data/spec/public/finalize_spec.rb +77 -0
  123. data/spec/public/model/hook_spec.rb +245 -0
  124. data/spec/public/model/property_spec.rb +91 -0
  125. data/spec/public/model/relationship_spec.rb +1040 -0
  126. data/spec/public/model_spec.rb +456 -0
  127. data/spec/public/property/binary_spec.rb +43 -0
  128. data/spec/public/property/boolean_spec.rb +21 -0
  129. data/spec/public/property/class_spec.rb +27 -0
  130. data/spec/public/property/date_spec.rb +21 -0
  131. data/spec/public/property/date_time_spec.rb +21 -0
  132. data/spec/public/property/decimal_spec.rb +23 -0
  133. data/spec/public/property/discriminator_spec.rb +134 -0
  134. data/spec/public/property/float_spec.rb +22 -0
  135. data/spec/public/property/integer_spec.rb +22 -0
  136. data/spec/public/property/object_spec.rb +117 -0
  137. data/spec/public/property/serial_spec.rb +22 -0
  138. data/spec/public/property/string_spec.rb +21 -0
  139. data/spec/public/property/text_spec.rb +62 -0
  140. data/spec/public/property/time_spec.rb +21 -0
  141. data/spec/public/property_spec.rb +333 -0
  142. data/spec/public/resource/state_spec.rb +72 -0
  143. data/spec/public/resource_spec.rb +289 -0
  144. data/spec/public/sel_spec.rb +53 -0
  145. data/spec/public/setup_spec.rb +145 -0
  146. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  147. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  148. data/spec/public/shared/collection_shared_spec.rb +1637 -0
  149. data/spec/public/shared/finder_shared_spec.rb +1647 -0
  150. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  151. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  152. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  153. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  154. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  155. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  156. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  157. data/spec/semipublic/associations_spec.rb +177 -0
  158. data/spec/semipublic/collection_spec.rb +110 -0
  159. data/spec/semipublic/model_spec.rb +96 -0
  160. data/spec/semipublic/property/binary_spec.rb +13 -0
  161. data/spec/semipublic/property/boolean_spec.rb +47 -0
  162. data/spec/semipublic/property/class_spec.rb +33 -0
  163. data/spec/semipublic/property/date_spec.rb +43 -0
  164. data/spec/semipublic/property/date_time_spec.rb +46 -0
  165. data/spec/semipublic/property/decimal_spec.rb +83 -0
  166. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  167. data/spec/semipublic/property/float_spec.rb +82 -0
  168. data/spec/semipublic/property/integer_spec.rb +82 -0
  169. data/spec/semipublic/property/lookup_spec.rb +29 -0
  170. data/spec/semipublic/property/serial_spec.rb +13 -0
  171. data/spec/semipublic/property/string_spec.rb +13 -0
  172. data/spec/semipublic/property/text_spec.rb +31 -0
  173. data/spec/semipublic/property/time_spec.rb +50 -0
  174. data/spec/semipublic/property_spec.rb +114 -0
  175. data/spec/semipublic/query/conditions/comparison_spec.rb +1502 -0
  176. data/spec/semipublic/query/conditions/operation_spec.rb +1296 -0
  177. data/spec/semipublic/query/path_spec.rb +471 -0
  178. data/spec/semipublic/query_spec.rb +3665 -0
  179. data/spec/semipublic/resource/state/clean_spec.rb +89 -0
  180. data/spec/semipublic/resource/state/deleted_spec.rb +79 -0
  181. data/spec/semipublic/resource/state/dirty_spec.rb +163 -0
  182. data/spec/semipublic/resource/state/immutable_spec.rb +107 -0
  183. data/spec/semipublic/resource/state/transient_spec.rb +163 -0
  184. data/spec/semipublic/resource/state_spec.rb +230 -0
  185. data/spec/semipublic/resource_spec.rb +23 -0
  186. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  187. data/spec/semipublic/shared/resource_shared_spec.rb +198 -0
  188. data/spec/semipublic/shared/resource_state_shared_spec.rb +91 -0
  189. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  190. data/spec/spec_helper.rb +34 -0
  191. data/spec/support/core_ext/hash.rb +10 -0
  192. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  193. data/spec/support/properties/huge_integer.rb +17 -0
  194. data/spec/unit/array_spec.rb +23 -0
  195. data/spec/unit/blank_spec.rb +73 -0
  196. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  197. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  198. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  199. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  200. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  201. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  202. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  203. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  204. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  205. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  206. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  207. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  208. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  216. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  217. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  218. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  219. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  220. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  221. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  222. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  223. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  224. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  226. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  227. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  228. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  229. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  230. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  231. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  232. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  233. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  237. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  239. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  240. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  241. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  242. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  243. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  244. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  245. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  246. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  247. data/spec/unit/hash_spec.rb +27 -0
  248. data/spec/unit/hook_spec.rb +1216 -0
  249. data/spec/unit/inflections_spec.rb +14 -0
  250. data/spec/unit/lazy_array_spec.rb +1949 -0
  251. data/spec/unit/mash_spec.rb +289 -0
  252. data/spec/unit/module_spec.rb +70 -0
  253. data/spec/unit/object_spec.rb +38 -0
  254. data/spec/unit/try_dup_spec.rb +46 -0
  255. data/tasks/ci.rake +1 -0
  256. data/tasks/spec.rake +18 -0
  257. data/tasks/yard.rake +9 -0
  258. data/tasks/yardstick.rake +19 -0
  259. metadata +323 -0
@@ -0,0 +1,1040 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ class ::Car
4
+ include DataMapper::Resource
5
+
6
+ property :id, Serial
7
+ property :name, String
8
+ end
9
+
10
+ class ::Engine
11
+ include DataMapper::Resource
12
+
13
+ property :id, Serial
14
+ end
15
+
16
+ class ::Door
17
+ include DataMapper::Resource
18
+
19
+ property :id, Serial
20
+ end
21
+
22
+ class ::Window
23
+ include DataMapper::Resource
24
+
25
+ property :id, Serial
26
+ end
27
+
28
+ shared_examples 'it creates a one accessor' do
29
+ describe 'accessor' do
30
+ describe 'when there is no associated resource' do
31
+ describe 'without a query' do
32
+ before :all do
33
+ @return = @car.__send__(@name)
34
+ end
35
+
36
+ it 'returns nil' do
37
+ expect(@return).to be_nil
38
+ end
39
+ end
40
+
41
+ describe 'with a query' do
42
+ before :all do
43
+ @return = @car.__send__(@name, id: 99)
44
+ end
45
+
46
+ it 'returns nil' do
47
+ expect(@return).to be_nil
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'when there is an associated resource' do
53
+ before :all do
54
+ @expected = @model.new
55
+ @car.__send__("#{@name}=", @expected)
56
+ end
57
+
58
+ describe 'without a query' do
59
+ before :all do
60
+ @return = @car.__send__(@name)
61
+ end
62
+
63
+ it 'returns a Resource' do
64
+ expect(@return).to be_kind_of(DataMapper::Resource)
65
+ end
66
+
67
+ it 'returns the expected Resource' do
68
+ expect(@return).to equal(@expected)
69
+ end
70
+ end
71
+
72
+ describe 'with a query' do
73
+ before :all do
74
+ @car.save # save @car and @expected to set @expected.id
75
+
76
+ expect(@expected.id).not_to be_nil
77
+
78
+ @return = @car.__send__(@name, id: @expected.id)
79
+ end
80
+
81
+ it 'returns a Resource' do
82
+ expect(@return).to be_kind_of(DataMapper::Resource)
83
+ end
84
+
85
+ it 'returns the expected Resource' do
86
+ expect(@return).to eq @expected
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'when the target model is scoped' do
92
+ before :all do
93
+ @resource = @model.new
94
+ @car.__send__("#{@name}=", @resource)
95
+ @car.save
96
+
97
+ # set the model scope to not match the expected resource
98
+ @model.default_scope.update(:id.not => @resource.id)
99
+
100
+ @return = @car.model.get!(*@car.key).__send__(@name)
101
+ end
102
+
103
+ it 'returns nil' do
104
+ expect(@return).to be_nil
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ shared_examples 'it creates a one mutator' do
111
+ describe 'mutator' do
112
+ describe 'when setting a Resource' do
113
+ before :all do
114
+ @expected = @model.new
115
+
116
+ @return = @car.__send__("#{@name}=", @expected)
117
+ end
118
+
119
+ it 'returns the expected Resource' do
120
+ expect(@return).to equal(@expected)
121
+ end
122
+
123
+ it 'sets the Resource' do
124
+ expect(@car.__send__(@name)).to equal(@expected)
125
+ end
126
+
127
+ it 'relates associated Resource' do
128
+ relationship = Car.relationships[@name]
129
+ many_to_one = relationship.is_a?(DataMapper::Associations::ManyToOne::Relationship)
130
+ one_to_one_through = relationship.is_a?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
131
+
132
+ pending if many_to_one || one_to_one_through
133
+
134
+ expect(@expected.car).to eq @car
135
+ end
136
+
137
+ it 'persists the Resource' do
138
+ expect(@car.save).to be(true)
139
+ expect(@car.model.get!(*@car.key).__send__(@name)).to eq @expected
140
+ end
141
+
142
+ it 'persists the associated Resource' do
143
+ expect(@car.save).to be(true)
144
+ expect(@expected).to be_saved
145
+ expect(@expected.model.get!(*@expected.key).car).to eq @car
146
+ end
147
+ end
148
+
149
+ describe 'when setting a Hash' do
150
+ before :all do
151
+ @car.__send__("#{@name}=", @model.new)
152
+
153
+ attributes = {id: 10}
154
+ @expected = @model.new(attributes)
155
+
156
+ @return = @car.__send__("#{@name}=", attributes)
157
+ end
158
+
159
+ it 'returns the expected Resource' do
160
+ expect(@return).to eq @expected
161
+ end
162
+
163
+ it 'sets the Resource' do
164
+ expect(@car.__send__(@name)).to equal(@return)
165
+ end
166
+
167
+ relationship = Car.relationships[@name]
168
+ many_to_one = relationship.is_a?(DataMapper::Associations::ManyToOne::Relationship)
169
+ it 'relates associated Resource' do
170
+ one_to_one_through = relationship.is_a?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
171
+
172
+ pending if many_to_one || one_to_one_through
173
+ expect(@return.car).to eq @car
174
+ end
175
+
176
+ it 'persists the Resource' do
177
+ expect(@car.save).to be(true)
178
+ expect(@car.model.get!(*@car.key).__send__(@name)).to eq @return
179
+ end
180
+
181
+ it 'persists the associated Resource' do
182
+ expect(@car.save).to be(true)
183
+ expect(@return).to be_saved
184
+ expect(@return&.model&.get!(*@return&.key).car).to eq @car
185
+ end
186
+ end
187
+
188
+ describe 'when setting nil' do
189
+ before :all do
190
+ @car.__send__("#{@name}=", @model.new)
191
+
192
+ @return = @car.__send__("#{@name}=", nil)
193
+ end
194
+
195
+ it 'returns nil' do
196
+ expect(@return).to be_nil
197
+ end
198
+
199
+ it 'sets nil' do
200
+ expect(@car.__send__(@name)).to be_nil
201
+ end
202
+
203
+ it 'persists as nil' do
204
+ expect(@car.save).to be(true)
205
+ expect(@car.model.get!(*@car.key).__send__(@name)).to be_nil
206
+ end
207
+ end
208
+
209
+ describe 'when changing the Resource' do
210
+ before :all do
211
+ @car.__send__("#{@name}=", @model.new)
212
+ @expected = @model.new
213
+
214
+ @return = @car.__send__("#{@name}=", @expected)
215
+ end
216
+
217
+ it 'returns the expected Resource' do
218
+ expect(@return).to equal(@expected)
219
+ end
220
+
221
+ it 'sets the Resource' do
222
+ expect(@car.__send__(@name)).to equal(@expected)
223
+ end
224
+
225
+ relationship = Car.relationships[@name]
226
+ many_to_one = relationship.is_a?(DataMapper::Associations::ManyToOne::Relationship)
227
+ it 'relates associated Resource' do
228
+ one_to_one_through = relationship.is_a?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
229
+
230
+ pending 'creates back-reference' if many_to_one || one_to_one_through
231
+ expect(@expected.car).to eq @car
232
+ end
233
+
234
+ it 'persists the Resource' do
235
+ expect(@car.save).to be(true)
236
+ expect(@car.model.get!(*@car.key).__send__(@name)).to eq @expected
237
+ end
238
+
239
+ it 'persists the associated Resource' do
240
+ expect(@car.save).to be(true)
241
+ expect(@expected).to be_saved
242
+ expect(@expected.model.get!(*@expected.key).car).to eq @car
243
+ end
244
+ end
245
+ end
246
+ end
247
+
248
+ shared_examples 'it creates a many accessor' do
249
+ describe 'accessor' do
250
+ describe 'when there is no child resource and the source is saved' do
251
+ before :all do
252
+ expect(@car.save).to be(true)
253
+ @return = @car.__send__(@name)
254
+ end
255
+
256
+ it 'returns a Collection' do
257
+ expect(@return).to be_kind_of(DataMapper::Collection)
258
+ end
259
+
260
+ it 'returns an empty Collection' do
261
+ expect(@return).to be_empty
262
+ end
263
+ end
264
+
265
+ describe 'when there is no child resource and the source is not saved' do
266
+ before :all do
267
+ @return = @car.__send__(@name)
268
+ end
269
+
270
+ it 'returns a Collection' do
271
+ expect(@return).to be_kind_of(DataMapper::Collection)
272
+ end
273
+
274
+ it 'returns an empty Collection' do
275
+ expect(@return).to be_empty
276
+ end
277
+ end
278
+
279
+ describe 'when there is a child resource' do
280
+ before :all do
281
+ @return = nil
282
+
283
+ @expected = @model.new
284
+ @car.__send__("#{@name}=", [@expected])
285
+
286
+ @return = @car.__send__(@name)
287
+ end
288
+
289
+ it 'returns a Collection' do
290
+ expect(@return).to be_kind_of(DataMapper::Collection)
291
+ end
292
+
293
+ it 'returns expected Resources' do
294
+ expect(@return).to eq [@expected]
295
+ end
296
+ end
297
+
298
+ describe 'when the target model is scoped' do
299
+ before :all do
300
+ 2.times { @car.__send__(@name).new }
301
+ @car.save
302
+
303
+ @expected = @car.__send__(@name).first
304
+ expect(@expected).not_to be_nil
305
+
306
+ # set the model scope to only return the first record
307
+ @model.default_scope.update(
308
+ @model.key(@repository.name).zip(@expected.key).to_h
309
+ )
310
+
311
+ @return = @car.model.get!(*@car.key).__send__(@name)
312
+ end
313
+
314
+ it 'returns a Collection' do
315
+ expect(@return).to be_kind_of(DataMapper::Collection)
316
+ end
317
+
318
+ it 'returns expected Resources' do
319
+ expect(@return).to eq [@expected]
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ shared_examples 'it creates a many mutator' do
326
+ describe 'mutator' do
327
+ describe 'when setting an Array of Resources' do
328
+ before :all do
329
+ @expected = [@model.new]
330
+
331
+ @return = @car.__send__("#{@name}=", @expected)
332
+ end
333
+
334
+ it 'returns the expected Collection' do
335
+ expect(@return).to eq @expected
336
+ end
337
+
338
+ it 'sets the Collection' do
339
+ expect(@car.__send__(@name)).to eq @expected
340
+ @car.__send__(@name).zip(@expected) { |value, expected| expect(value).to equal(expected) }
341
+ end
342
+
343
+ it 'relates the associated Collection' do
344
+ pending if Car.relationships[@name].is_a?(DataMapper::Associations::ManyToMany::Relationship)
345
+ @expected.each { |resource| expect(resource.car).to eq @car }
346
+ end
347
+
348
+ it 'persists the Collection' do
349
+ expect(@car.save).to be(true)
350
+ expect(@car.model.get!(*@car.key).__send__(@name)).to eq @expected
351
+ end
352
+
353
+ it 'persists the associated Resource' do
354
+ expect(@car.save).to be(true)
355
+ @expected.each do |resource|
356
+ expect(resource).to be_saved
357
+ expect(resource.model.get!(*resource.key).car).to eq @car
358
+ end
359
+ end
360
+ end
361
+
362
+ describe 'when setting an Array of Hashes' do
363
+ before :all do
364
+ attributes = {id: 11}
365
+ @hashes = [attributes]
366
+ @expected = [@model.new(attributes)]
367
+
368
+ @return = @car.__send__("#{@name}=", @hashes)
369
+ end
370
+
371
+ it 'returns the expected Collection' do
372
+ expect(@return).to eq @expected
373
+ end
374
+
375
+ it 'sets the Collection' do
376
+ expect(@car.__send__(@name)).to eq @return
377
+ end
378
+
379
+ it 'relates the associated Collection' do
380
+ pending if Car.relationships[@name].is_a?(DataMapper::Associations::ManyToMany::Relationship)
381
+ @return.each { |resource| expect(resource.car).to eq @car }
382
+ end
383
+
384
+ it 'persists the Collection' do
385
+ expect(@car.save).to be(true)
386
+ expect(@car.model.get!(*@car.key).__send__(@name)).to eq @return
387
+ end
388
+
389
+ it 'persists the associated Resource' do
390
+ expect(@car.save).to be(true)
391
+ @return&.each do |resource|
392
+ expect(resource).to be_saved
393
+ expect(resource.model.get!(*resource.key).car).to eq @car
394
+ end
395
+ end
396
+ end
397
+
398
+ describe 'when setting an empty collection' do
399
+ before :all do
400
+ @car.__send__("#{@name}=", [@model.new])
401
+
402
+ @return = @car.__send__("#{@name}=", [])
403
+ end
404
+
405
+ it 'returns a Collection' do
406
+ expect(@return).to be_kind_of(DataMapper::Collection)
407
+ end
408
+
409
+ it 'sets an empty Collection' do
410
+ expect(@car.__send__(@name)).to be_empty
411
+ end
412
+
413
+ it 'persists as an empty Collection' do
414
+ expect(@car.save).to be(true)
415
+ expect(@car.model.get!(*@car.key).__send__(@name)).to be_empty
416
+ end
417
+ end
418
+
419
+ describe 'when changing an associated collection' do
420
+ before :all do
421
+ @car.__send__("#{@name}=", [@model.new])
422
+
423
+ @expected = [@model.new]
424
+
425
+ @return = @car.__send__("#{@name}=", @expected)
426
+ end
427
+
428
+ it 'returns the expected Resource' do
429
+ expect(@return).to eq @expected
430
+ end
431
+
432
+ it 'sets the Resource' do
433
+ expect(@car.__send__(@name)).to eq @expected
434
+ @car.__send__(@name).zip(@expected) { |value, expected| expect(value).to equal(expected) }
435
+ end
436
+
437
+ it 'relates associated Resource' do
438
+ pending if Car.relationships[@name].is_a?(DataMapper::Associations::ManyToMany::Relationship)
439
+ @expected.each { |resource| expect(resource.car).to eq @car }
440
+ end
441
+
442
+ it 'persists the Resource' do
443
+ expect(@car.save).to be(true)
444
+ expect(@car.model.get!(*@car.key).__send__(@name)).to eq @expected
445
+ end
446
+
447
+ it 'persists the associated Resource' do
448
+ expect(@car.save).to be(true)
449
+ @expected.each do |resource|
450
+ expect(resource).to be_saved
451
+ expect(resource.model.get!(*resource.key).car).to eq @car
452
+ end
453
+ end
454
+ end
455
+ end
456
+ end
457
+
458
+ describe DataMapper::Associations do
459
+ before :all do
460
+ end
461
+
462
+ def n
463
+ 1.0/0
464
+ end
465
+
466
+ it { expect(Engine).to respond_to(:belongs_to) }
467
+
468
+ describe '#belongs_to' do
469
+ before :all do
470
+ @model = Engine
471
+ @name = :engine
472
+
473
+ Car.belongs_to(@name, required: false)
474
+ Engine.has(1, :car)
475
+ DataMapper.finalize
476
+ end
477
+
478
+ supported_by :all do
479
+ before :all do
480
+ @car = Car.new
481
+ end
482
+
483
+ it { expect(@car).to respond_to(@name) }
484
+
485
+ it_behaves_like 'it creates a one accessor'
486
+
487
+ it { expect(@car).to respond_to("#{@name}=") }
488
+
489
+ it_behaves_like 'it creates a one mutator'
490
+
491
+ describe 'with a :key option' do
492
+ before :all do
493
+ @relationship = Car.belongs_to("#{@name}_with_key".to_sym, @model, required: false, key: true)
494
+ DataMapper.finalize
495
+ end
496
+
497
+ it 'creates a foreign key that is part of the key' do
498
+ @relationship.child_key.each do |property|
499
+ expect(property).to be_key
500
+ end
501
+ end
502
+ end
503
+
504
+ describe 'with a :unique option' do
505
+ let(:unique) { %i(one two three) }
506
+
507
+ before :all do
508
+ @relationship = Car.belongs_to("#{@name}_with_unique".to_sym, @model, unique: unique)
509
+ DataMapper.finalize
510
+ end
511
+
512
+ it 'creates a foreign key that is unique' do
513
+ @relationship.child_key.each do |property|
514
+ expect(property).to be_unique
515
+ end
516
+ end
517
+
518
+ it 'creates a foreign key that has a unique index' do
519
+ @relationship.child_key.each do |property|
520
+ expect(property.unique_index).to equal(unique)
521
+ end
522
+ end
523
+ end
524
+ end
525
+
526
+ # TODO: refactor these specs into above structure once they pass
527
+ describe 'pending query specs' do
528
+ before :all do
529
+ Car.has(1, :engine)
530
+ Engine.belongs_to(:car)
531
+ DataMapper.finalize
532
+ end
533
+
534
+ supported_by :all do
535
+ describe 'querying for a parent resource when only the foreign key is set' do
536
+ before :all do
537
+ # create a car that would be returned if the query is not
538
+ # scoped properly to retrieve @car
539
+ Car.create
540
+
541
+ @car = Car.create
542
+ engine = Engine.new(car_id: @car.id)
543
+
544
+ @return = engine.car
545
+ end
546
+
547
+ it 'returns a Resource' do
548
+ expect(@return).to be_kind_of(DataMapper::Resource)
549
+ end
550
+
551
+ it 'returns expected Resource' do
552
+ expect(@return).to eql(@car)
553
+ end
554
+ end
555
+
556
+ describe 'querying for a parent resource' do
557
+ before :all do
558
+ @car = Car.create
559
+ @engine = Engine.create(car: @car)
560
+ @resource = @engine.car(id: @car.id)
561
+ end
562
+
563
+ it 'returns a Resource' do
564
+ expect(@resource).to be_kind_of(DataMapper::Resource)
565
+ end
566
+
567
+ it 'returns expected Resource' do
568
+ expect(@resource).to eql(@car)
569
+ end
570
+ end
571
+
572
+ describe 'querying for a parent resource that does not exist' do
573
+ before :all do
574
+ @car = Car.create
575
+ @engine = Engine.create(car: @car)
576
+ @resource = @engine.car(:id.not => @car.id)
577
+ end
578
+
579
+ it 'returns nil' do
580
+ expect(@resource).to be_nil
581
+ end
582
+ end
583
+
584
+ describe 'changing the parent resource' do
585
+ before :all do
586
+ @car = Car.create
587
+ @engine = Engine.new
588
+ @engine.car = @car
589
+ end
590
+
591
+ it 'sets the associated foreign key' do
592
+ expect(@engine.car_id).to eq @car.id
593
+ end
594
+
595
+ it 'adds the engine object to the car' do
596
+ pending 'Changing a belongs_to parent adds the object to the correct association'
597
+
598
+ expect(@car.engines).to include(@engine)
599
+ end
600
+ end
601
+
602
+ describe 'changing the parent foreign key' do
603
+ before :all do
604
+ @car = Car.create
605
+
606
+ @engine = Engine.new(car_id: @car.id)
607
+ end
608
+
609
+ it 'sets the associated resource' do
610
+ expect(@engine.car).to eql(@car)
611
+ end
612
+ end
613
+
614
+ describe 'changing an existing resource through the relation' do
615
+ before :all do
616
+ @car1 = Car.create
617
+ @car2 = Car.create
618
+ @engine = Engine.create(car: @car1)
619
+ @engine.car = @car2
620
+ end
621
+
622
+ it 'also changes the foreign key' do
623
+ expect(@engine.car_id).to eq @car2.id
624
+ end
625
+
626
+ it 'adds the engine to the car' do
627
+ pending 'Changing a belongs_to parent adds the object to the correct association'
628
+ expect(@car2.engines).to include(@engine)
629
+ end
630
+ end
631
+
632
+ describe 'changing an existing resource through the relation' do
633
+ before :all do
634
+ @car1 = Car.create
635
+ @car2 = Car.create
636
+ @engine = Engine.create(car: @car1)
637
+ @engine.car_id = @car2.id
638
+ end
639
+
640
+ it 'also changes the foreign key' do
641
+ expect(@engine.car).to eql(@car2)
642
+ end
643
+
644
+ it 'adds the engine to the car' do
645
+ pending 'a change to the foreign key also changes the related object'
646
+ expect(@car2.engines).to include(@engine)
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 'sets the relationship target model' do
659
+ expect(Engine.relationships[:vehicle].target_model).to eq 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 'sets the relationship target model' do
670
+ expect(Engine.relationships[:vehicle].target_model).to eq 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 'sets the relationship child key' do
681
+ expect(Engine.relationships[:vehicle].child_key.map(&:name)).to eq [: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 'sets the relationship child key' do
692
+ expect(Engine.relationships[:vehicle].child_key.map(&:name)).to eq [: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 'sets the relationship parent key' do
703
+ expect(Engine.relationships[:vehicle].parent_key.map(&:name)).to eq [: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 'sets the relationship parent key' do
714
+ expect(Engine.relationships[:vehicle].parent_key.map(&:name)).to eq [:name]
715
+ end
716
+ end
717
+ end
718
+
719
+ it { expect(Car).to 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 { expect(@car).to respond_to(@name) }
738
+
739
+ it_behaves_like 'it creates a one accessor'
740
+
741
+ it { expect(@car).to respond_to("#{@name}=") }
742
+
743
+ it_behaves_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.is_a?(DataMapper::Adapters::InMemoryAdapter)) ||
760
+ (defined?(DataMapper::Adapters::YamlAdapter) && @adapter.is_a?(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 { expect(@car).to respond_to(@name) }
772
+
773
+ it_behaves_like 'it creates a one accessor'
774
+
775
+ it { expect(@car).to respond_to("#{@name}=") }
776
+
777
+ it_behaves_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 { expect(@car).to respond_to(@name) }
797
+
798
+ it_behaves_like 'it creates a many accessor'
799
+
800
+ it { expect(@car).to respond_to("#{@name}=") }
801
+
802
+ it_behaves_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.is_a?(DataMapper::Adapters::InMemoryAdapter)) ||
819
+ (defined?(DataMapper::Adapters::YamlAdapter) && @adapter.is_a?(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 { expect(@car).to respond_to(@name) }
831
+
832
+ it_behaves_like 'it creates a many accessor'
833
+
834
+ it { expect(@car).to respond_to("#{@name}=") }
835
+
836
+ it_behaves_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 'sets the relationship target model' do
847
+ expect(Car.relationships[:engine].target_model).to eq 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 'sets the relationship target model' do
858
+ expect(Car.relationships[:engine].target_model).to eq Engine
859
+ end
860
+ end
861
+
862
+ it 'raises an exception if the cardinality is not understood' do
863
+ expect { Car.has(n..n, :doors) }.to raise_error(ArgumentError)
864
+ end
865
+
866
+ it 'raises an exception if the minimum constraint is larger than the maximum' do
867
+ expect { Car.has(2..1, :doors) }.to 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 'has a child key prefix the same as the inverse relationship' do
880
+ expect(@engine_relationship.child_key.map(&:name)).to eq [: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 'has a child key prefix inferred from the source model name' do
893
+ expect(@engine_relationship.child_key.map(&:name)).to eq [: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 'has a source model equal to the ancestor' do
915
+ expect(@engine_relationship.source_model).to equal(Car)
916
+ end
917
+
918
+ it 'has a child key prefix the same as the inverse relationship' do
919
+ expect(@engine_relationship.child_key.map(&:name)).to eq [: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 'has a source model equal to the ancestor' do
940
+ expect(@engine_relationship.source_model).to equal(Car)
941
+ end
942
+
943
+ it 'has a child key prefix inferred from the source model name' do
944
+ expect(@engine_relationship.child_key.map(&:name)).to eq [: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 'has a source model equal to the descendant' do
967
+ expect(@engine_relationship.source_model).to equal(ElectricCar)
968
+ end
969
+
970
+ it 'has a child key prefix the same as the inverse relationship' do
971
+ expect(@engine_relationship.child_key.map(&:name)).to eq [: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 'has a source model equal to the descendant' do
992
+ expect(@engine_relationship.source_model).to equal(ElectricCar)
993
+ end
994
+
995
+ it 'has a child key prefix inferred from the source model name' do
996
+ expect(@engine_relationship.child_key.map(&:name)).to eq [: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 'saves the child as a parent' do
1033
+ expect {
1034
+ @company.owner = @employee
1035
+ expect(@company.save).to be(true)
1036
+ }.not_to raise_error
1037
+ end
1038
+ end
1039
+ end
1040
+ end