sbf-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 (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