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,1221 @@
1
+ shared_examples 'A public Resource' do
2
+ before :all do
3
+ @no_join = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter) ||
4
+ defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
5
+
6
+ relationship = @user_model.relationships[:referrer]
7
+ @one_to_one_through = relationship.kind_of?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
8
+
9
+ @skip = @no_join && @one_to_one_through
10
+ end
11
+
12
+ before :all do
13
+ unless @skip
14
+ %w[ @user_model @user @comment_model ].each do |ivar|
15
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
16
+ end
17
+ end
18
+ end
19
+
20
+ before do
21
+ pending if @skip
22
+ end
23
+
24
+ %i(== ===).each do |method|
25
+ it { expect(@user).to respond_to(method) }
26
+
27
+ describe "##{method}" do
28
+ describe 'when comparing to the same resource' do
29
+ before :all do
30
+ @other = @user
31
+ @return = @user.__send__(method, @other)
32
+ end
33
+
34
+ it 'returns true' do
35
+ expect(@return).to be(true)
36
+ end
37
+ end
38
+
39
+ describe 'when comparing to an resource that does not respond to resource methods' do
40
+ before :all do
41
+ @other = Object.new
42
+ @return = @user.__send__(method, @other)
43
+ end
44
+
45
+ it 'returns false' do
46
+ expect(@return).to be(false)
47
+ end
48
+ end
49
+
50
+ describe 'when comparing to a resource with the same properties, but the model is a subclass' do
51
+ before :all do
52
+ rescue_if @skip do
53
+ @other = @author_model.new(@user.attributes)
54
+ @return = @user.__send__(method, @other)
55
+ end
56
+ end
57
+
58
+ it 'returns true' do
59
+ expect(@return).to be(true)
60
+ end
61
+ end
62
+
63
+ describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
64
+ before :all do
65
+ rescue_if @skip do
66
+ @other = @user_model.get(*@user.key)
67
+ @return = @user.__send__(method, @other)
68
+ end
69
+ end
70
+
71
+ it 'returns true' do
72
+ expect(@return).to be(true)
73
+ end
74
+ end
75
+
76
+ describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
77
+ before :all do
78
+ rescue_if @skip do
79
+ @user.age = 20
80
+ @other = @user_model.get(*@user.key)
81
+ @return = @user.__send__(method, @other)
82
+ end
83
+ end
84
+
85
+ it 'returns false' do
86
+ expect(@return).to be(false)
87
+ end
88
+ end
89
+
90
+ describe 'when comparing to a resource with the same properties' do
91
+ before :all do
92
+ rescue_if @skip do
93
+ @other = @user_model.new(@user.attributes)
94
+ @return = @user.__send__(method, @other)
95
+ end
96
+ end
97
+
98
+ it 'returns true' do
99
+ expect(@return).to be(true)
100
+ end
101
+ end
102
+
103
+ with_alternate_adapter do
104
+ before :all do
105
+ if @user_model.respond_to?(:auto_migrate!)
106
+ # force the user model to be available in the alternate repository
107
+ @user_model.auto_migrate!(@adapter.name)
108
+ end
109
+ end
110
+
111
+ describe 'when comparing to a resource with a different repository, but the same properties' do
112
+ before :all do
113
+ rescue_if @skip do
114
+ @other = @repository.scope { @user_model.create(@user.attributes) }
115
+ @return = @user.__send__(method, @other)
116
+ end
117
+ end
118
+
119
+ it 'returns false' do
120
+ expect(@return).to be(false)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ it { expect(@user).to respond_to(:<=>) }
128
+
129
+ describe '#<=>' do
130
+ describe 'when the default order properties are equal with another resource' do
131
+ before :all do
132
+ rescue_if @skip && RUBY_VERSION < '1.9.2' do
133
+ @other = @user_model.new(:name => 'dbussink')
134
+ @return = @user <=> @other
135
+ end
136
+ end
137
+
138
+ it 'returns 0' do
139
+ expect(@return).to eq 0
140
+ end
141
+ end
142
+
143
+ describe 'when the default order property values are sorted before another resource' do
144
+ before :all do
145
+ rescue_if @skip && RUBY_VERSION < '1.9.2' do
146
+ @other = @user_model.new(:name => 'c')
147
+ @return = @user <=> @other
148
+ end
149
+ end
150
+
151
+ it 'returns 1' do
152
+ expect(@return).to eq 1
153
+ end
154
+ end
155
+
156
+ describe 'when the default order property values are sorted after another resource' do
157
+ before :all do
158
+ rescue_if @skip && RUBY_VERSION < '1.9.2' do
159
+ @other = @user_model.new(:name => 'e')
160
+ @return = @user <=> @other
161
+ end
162
+ end
163
+
164
+ it 'returns -1' do
165
+ expect(@return).to eq -1
166
+ end
167
+ end
168
+
169
+ describe 'when comparing an unrelated type of Object' do
170
+ it 'raises an exception' do
171
+ expect {
172
+ @user <=> @comment_model.new
173
+ }.to raise_error(ArgumentError, "Cannot compare a #{@comment_model} instance with a #{@user_model} instance")
174
+ end
175
+ end
176
+ end
177
+
178
+ it { expect(@user).to respond_to(:attribute_get) }
179
+
180
+ describe '#attribute_get' do
181
+ it { expect(@user.attribute_get(:name)).to eq 'dbussink' }
182
+ end
183
+
184
+ it { expect(@user).to respond_to(:attribute_set) }
185
+
186
+ describe '#attribute_set' do
187
+ before { @user.attribute_set(:name, 'dkubb') }
188
+
189
+ it { expect(@user.name).to eq 'dkubb' }
190
+ end
191
+
192
+ it { expect(@user).to respond_to(:attributes) }
193
+
194
+ describe '#attributes' do
195
+ describe 'with a new resource' do
196
+ before :all do
197
+ rescue_if @skip do
198
+ @user = @user.model.new
199
+ end
200
+ end
201
+
202
+ it 'returns the expected values' do
203
+ expect(@user.attributes).to eq({})
204
+ end
205
+ end
206
+
207
+ describe 'with a new resource with a property set' do
208
+ before :all do
209
+ rescue_if @skip do
210
+ @user = @user.model.new
211
+ @user.name = 'dbussink'
212
+ end
213
+ end
214
+
215
+ it 'returns the expected values' do
216
+ expect(@user.attributes).to eq({name: 'dbussink'})
217
+ end
218
+ end
219
+
220
+ describe 'with a saved resource' do
221
+ it 'returns the expected values' do
222
+ expect(DataMapper::Ext::Hash.only(@user.attributes, :name, :description, :age)).to eq({name: 'dbussink', description: 'Test', age: 25})
223
+ end
224
+ end
225
+ end
226
+
227
+ it { expect(@user).to respond_to(:attributes=) }
228
+
229
+ describe '#attributes=' do
230
+ describe 'when a public mutator is specified' do
231
+ before :all do
232
+ rescue_if @skip do
233
+ @user.attributes = { :name => 'dkubb', @user.class.properties[:age] => '42' }
234
+ end
235
+ end
236
+
237
+ it 'sets the value' do
238
+ expect(@user.name).to eql('dkubb')
239
+ end
240
+
241
+ it 'typecasts and sets the value' do
242
+ expect(@use.age).to eql(42)
243
+ end
244
+ end
245
+
246
+ describe 'when a non-public mutator is specified' do
247
+ it 'raises an exception' do
248
+ expect {
249
+ @user.attributes = {admin: true}
250
+ }.to raise_error(ArgumentError, "The attribute \'admin\' is not accessible in #{@user_model}")
251
+ end
252
+ end
253
+ end
254
+
255
+ %i(destroy destroy!).each do |method|
256
+ it { expect(@user).to respond_to(:destroy) }
257
+
258
+ describe "##{method}" do
259
+ describe 'on a single resource' do
260
+ before :all do
261
+ @resource = @user_model.create(:name => 'hacker', :age => 20, :comment => @comment)
262
+
263
+ @return = @resource.__send__(method)
264
+ end
265
+
266
+ it 'successfully removes a resource' do
267
+ expect(@return).to be(true)
268
+ end
269
+
270
+ it 'marks the destroyed resource as readonly' do
271
+ expect(@resource).to be_readonly
272
+ end
273
+
274
+ it "returns true when calling #{method} on a destroyed resource" do
275
+ expect(@resource.__send__(method)).to be(true)
276
+ end
277
+
278
+ it 'removes resource from persistent storage' do
279
+ expect(@user_model.get(*@resource.key)).to be_nil
280
+ end
281
+ end
282
+
283
+ describe 'with has relationship resources' do
284
+ it 'raises an exception'
285
+ end
286
+ end
287
+ end
288
+
289
+ it { expect(@user).to respond_to(:dirty?) }
290
+
291
+ describe '#dirty?' do
292
+ describe 'on a record, with dirty attributes' do
293
+ before { @user.age = 100 }
294
+
295
+ it { expect(@user).to be_dirty }
296
+ end
297
+
298
+ describe 'on a record, with no dirty attributes, and dirty parents' do
299
+ before :all do
300
+ rescue_if @skip do
301
+ expect(@user).not_to be_dirty
302
+
303
+ parent = @user.parent = @user_model.new(:name => 'Parent')
304
+ expect(parent).to be_dirty
305
+ end
306
+ end
307
+
308
+ it { expect(@user).to be_dirty }
309
+ end
310
+
311
+ describe 'on a record, with no dirty attributes, and dirty children' do
312
+ before :all do
313
+ rescue_if @skip do
314
+ expect(@user).not_to be_dirty
315
+
316
+ child = @user.children.new(:name => 'Child')
317
+ expect(child).to be_dirty
318
+ end
319
+ end
320
+
321
+ it { expect(@user).to be_dirty }
322
+ end
323
+
324
+ describe 'on a record, with no dirty attributes, and dirty siblings' do
325
+ before :all do
326
+ rescue_if @skip do
327
+ expect(@user).not_to be_dirty
328
+
329
+ parent = @user_model.create(:name => 'Parent', :comment => @comment)
330
+ expect(parent).not_to be_dirty
331
+
332
+ @user.update(:parent => parent)
333
+ expect(@user).not_to be_dirty
334
+
335
+ sibling = parent.children.new(:name => 'Sibling')
336
+ expect(sibling).to be_dirty
337
+ expect(parent).to be_dirty
338
+ end
339
+ end
340
+
341
+ it { expect(@user).not_to be_dirty }
342
+ end
343
+
344
+ describe 'on a saved record, with no dirty attributes' do
345
+ it { expect(@user).not_to be_dirty }
346
+ end
347
+
348
+ describe 'on a new record, with no dirty attributes, no default attributes, and no identity field' do
349
+ before { @user = @user_model.new }
350
+
351
+ it { expect(@user).not_to be_dirty }
352
+ end
353
+
354
+ describe 'on a new record, with no dirty attributes, no default attributes, and an identity field' do
355
+ before { @comment = @comment_model.new }
356
+
357
+ it { expect(@comment).to be_dirty }
358
+ end
359
+
360
+ describe 'on a new record, with no dirty attributes, default attributes, and no identity field' do
361
+ before { @default = Default.new }
362
+
363
+ it { expect(@default).to be_dirty }
364
+ end
365
+
366
+ describe 'on a record with itself as a parent (circular dependency)' do
367
+ before :all do
368
+ rescue_if @skip do
369
+ @user.parent = @user
370
+ end
371
+ end
372
+
373
+ it 'does not raise an exception' do
374
+ expect {
375
+ expect(@user.dirty?).to be(true)
376
+ }.not_to raise_error(SystemStackError)
377
+ end
378
+ end
379
+
380
+ describe 'on a record with itself as a child (circular dependency)' do
381
+ before :all do
382
+ rescue_if @skip do
383
+ @user.children = [ @user ]
384
+ end
385
+ end
386
+
387
+ it 'does not raise an exception' do
388
+ except {
389
+ expect(@user.dirty?).to be(true)
390
+ }.not_to raise_error(SystemStackError)
391
+ end
392
+ end
393
+
394
+ describe 'on a record with a parent as a child (circular dependency)' do
395
+ before :all do
396
+ rescue_if @skip do
397
+ @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
398
+ expect(@user.save).to be(true)
399
+ end
400
+ end
401
+
402
+ it 'does not raise an exception' do
403
+ expect {
404
+ expect(@user.dirty?).to be(true)
405
+ }.not_to raise_error
406
+ end
407
+ end
408
+ end
409
+
410
+ it { expect(@user).to respond_to(:eql?) }
411
+
412
+ describe '#eql?' do
413
+ describe 'when comparing to the same resource' do
414
+ before :all do
415
+ @other = @user
416
+ @return = @user.eql?(@other)
417
+ end
418
+
419
+ it 'returns true' do
420
+ expect(@return).to be(true)
421
+ end
422
+ end
423
+
424
+ describe 'when comparing to an resource that does not respond to model' do
425
+ before :all do
426
+ @other = Object.new
427
+ @return = @user.eql?(@other)
428
+ end
429
+
430
+ it 'returns false' do
431
+ expect(@return).to be(false)
432
+ end
433
+ end
434
+
435
+ describe 'when comparing to a resource with the same properties, but the model is a subclass' do
436
+ before :all do
437
+ rescue_if @skip do
438
+ @other = @author_model.new(@user.attributes)
439
+ @return = @user.eql?(@other)
440
+ end
441
+ end
442
+
443
+ it 'returns false' do
444
+ expect(@return).to be(false)
445
+ end
446
+ end
447
+
448
+ describe 'when comparing to a resource with a different key' do
449
+ before :all do
450
+ @other = @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment)
451
+ @return = @user.eql?(@other)
452
+ end
453
+
454
+ it 'returns false' do
455
+ expect(@return).to be(false)
456
+ end
457
+ end
458
+
459
+ describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
460
+ before :all do
461
+ rescue_if @skip do
462
+ @other = @user_model.get(*@user.key)
463
+ @return = @user.eql?(@other)
464
+ end
465
+ end
466
+
467
+ it 'returns true' do
468
+ expect(@return).to be(true)
469
+ end
470
+ end
471
+
472
+ describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
473
+ before :all do
474
+ rescue_if @skip do
475
+ @user.age = 20
476
+ @other = @user_model.get(*@user.key)
477
+ @return = @user.eql?(@other)
478
+ end
479
+ end
480
+
481
+ it 'returns false' do
482
+ expect(@return).to be(false)
483
+ end
484
+ end
485
+
486
+ describe 'when comparing to a resource with the same properties' do
487
+ before :all do
488
+ rescue_if @skip do
489
+ @other = @user_model.new(@user.attributes)
490
+ @return = @user.eql?(@other)
491
+ end
492
+ end
493
+
494
+ it 'returns true' do
495
+ expect(@return).to be(true)
496
+ end
497
+ end
498
+
499
+ with_alternate_adapter do
500
+ before :all do
501
+ if @user_model.respond_to?(:auto_migrate!)
502
+ # force the user model to be available in the alternate repository
503
+ @user_model.auto_migrate!(@adapter.name)
504
+ end
505
+ end
506
+
507
+ describe 'when comparing to a resource with a different repository, but the same properties' do
508
+ before :all do
509
+ rescue_if @skip do
510
+ @other = @repository.scope { @user_model.create(@user.attributes) }
511
+ @return = @user.eql?(@other)
512
+ end
513
+ end
514
+
515
+ it 'returns false' do
516
+ expect(@return).to be(false)
517
+ end
518
+ end
519
+ end
520
+ end
521
+
522
+ it { expect(@user).to respond_to(:inspect) }
523
+
524
+ describe '#inspect' do
525
+ before :all do
526
+ rescue_if @skip do
527
+ @user = @user_model.get(*@user.key)
528
+ @inspected = @user.inspect
529
+ end
530
+ end
531
+
532
+ it { expect(@inspected).to match(/^#<#{@user_model}/) }
533
+
534
+ it { expect(@inspected).to match(/name="dbussink"/) }
535
+
536
+ it { expect(@inspected).to match(/age=25/) }
537
+
538
+ it { expect(@inspected).to match(/description=<not loaded>/) }
539
+ end
540
+
541
+ it { expect(@user).to respond_to(:key) }
542
+
543
+ describe '#key' do
544
+ before :all do
545
+ rescue_if @skip do
546
+ @key = @user.key
547
+ @user.name = 'dkubb'
548
+ end
549
+ end
550
+
551
+ it { expect(@key).to be_kind_of(Array) }
552
+
553
+ it 'always returns the key value persisted in the back end' do
554
+ expect(@key.first).to eql('dbussink')
555
+ end
556
+
557
+ it { expect(@user.key).to eql(@key) }
558
+ end
559
+
560
+ it { expect(@user).to respond_to(:new?) }
561
+
562
+ describe '#new?' do
563
+ describe 'on an existing record' do
564
+ it { expect(@user).not_to be_new }
565
+ end
566
+
567
+ describe 'on a new record' do
568
+ before { @user = @user_model.new }
569
+
570
+ it { expect(@user).to be_new }
571
+ end
572
+ end
573
+
574
+ it { expect(@user).to respond_to(:reload) }
575
+
576
+ describe '#reload' do
577
+ before do
578
+ # reset the user for each spec
579
+ rescue_if(@skip) do
580
+ @user.update(:name => 'dbussink', :age => 25, :description => 'Test')
581
+ end
582
+ end
583
+
584
+ subject { rescue_if(@skip) { @user.reload } }
585
+
586
+ describe 'on a resource not persisted' do
587
+ before do
588
+ @user.attributes = { :description => 'Changed' }
589
+ end
590
+
591
+ it { is_expected.to be_kind_of(DataMapper::Resource) }
592
+
593
+ it { is_expected.to equal(@user) }
594
+
595
+ it { is_expected.to be_clean }
596
+
597
+ it 'reset the changed attributes' do
598
+ expect(method(:subject)).to change(@user, :description).from('Changed').to('Test')
599
+ end
600
+ end
601
+
602
+ describe 'on a resource where the key is changed, but not persisted' do
603
+ before do
604
+ @user.attributes = { :name => 'dkubb' }
605
+ end
606
+
607
+ it { is_expected.to be_kind_of(DataMapper::Resource) }
608
+
609
+ it { is_expected.to equal(@user) }
610
+
611
+ it { is_expected.to be_clean }
612
+
613
+ it 'resets the changed attributes' do
614
+ expect(method(:subject)).to change(@user, :name).from('dkubb').to('dbussink')
615
+ end
616
+ end
617
+
618
+ describe 'on a resource that is changed outside another resource' do
619
+ before do
620
+ rescue_if @skip do
621
+ @user.dup.update(:description => 'Changed')
622
+ end
623
+ end
624
+
625
+ it { is_expected.to be_kind_of(DataMapper::Resource) }
626
+
627
+ it { is_expected.to equal(@user) }
628
+
629
+ it { is_expected.to be_clean }
630
+
631
+ it 'reloads the resource from the data store' do
632
+ expect(method(:subject)).to change(@user, :description).from('Test').to('Changed')
633
+ end
634
+ end
635
+
636
+ describe 'on an anonymous resource' do
637
+ before do
638
+ rescue_if @skip do
639
+ @user = @user.model.first(fields: [:description])
640
+ expect(@user.description).to eq 'Test'
641
+ end
642
+ end
643
+
644
+ it { is_expected.to be_kind_of(DataMapper::Resource) }
645
+
646
+ it { is_expected.to equal(@user) }
647
+
648
+ it { is_expected.to be_clean }
649
+
650
+ it 'does not reload any attributes' do
651
+ expect(method(:subject)).not_to change(@user, :attributes)
652
+ end
653
+ end
654
+ end
655
+
656
+ it { expect(@user).to respond_to(:readonly?) }
657
+
658
+ describe '#readonly?' do
659
+ describe 'on a new resource' do
660
+ before :all do
661
+ rescue_if @skip do
662
+ @user = @user.model.new
663
+ end
664
+ end
665
+
666
+ it 'returns false' do
667
+ expect(@user.readonly?).to be(false)
668
+ end
669
+ end
670
+
671
+ describe 'on a saved resource' do
672
+ before :all do
673
+ rescue_if @skip do
674
+ expect(@user).to be_saved
675
+ end
676
+ end
677
+
678
+ it 'returns false' do
679
+ expect(@user.readonly?).to be(false)
680
+ end
681
+ end
682
+
683
+ describe 'on a destroyed resource' do
684
+ before :all do
685
+ rescue_if @skip do
686
+ expect(@user.destroy).to be(true)
687
+ end
688
+ end
689
+
690
+ it 'returns true' do
691
+ expect(@user.readonly?).to be(true)
692
+ end
693
+ end
694
+
695
+ describe 'on an anonymous resource' do
696
+ before :all do
697
+ rescue_if @skip do
698
+ # load the user without a key
699
+ @user = @user.model.first(:fields => @user_model.properties - @user_model.key)
700
+ end
701
+ end
702
+
703
+ it 'returns true' do
704
+ expect(@user.readonly?).to be(true)
705
+ end
706
+ end
707
+ end
708
+
709
+ %i(save save!).each do |method|
710
+ it { expect(@user).to respond_to(method) }
711
+
712
+ describe "##{method}" do
713
+ before :all do
714
+ @user_model.class_eval do
715
+ attr_accessor :save_hook_call_count
716
+
717
+ before :save do
718
+ @save_hook_call_count ||= 0
719
+ @save_hook_call_count += 1
720
+ end
721
+ end
722
+ end
723
+
724
+ describe 'on a new, not dirty resource' do
725
+ before :all do
726
+ @user = @user_model.new
727
+ @return = @user.__send__(method)
728
+ end
729
+
730
+ it 'returns false' do
731
+ expect(@return).to be(false)
732
+ end
733
+
734
+ it 'calls save hook expected number of times' do
735
+ expect(@user.save_hook_call_count).to be_nil
736
+ end
737
+ end
738
+
739
+ describe 'on a not new, not dirty resource' do
740
+ before :all do
741
+ rescue_if @skip do
742
+ @return = @user.__send__(method)
743
+ end
744
+ end
745
+
746
+ it 'returns true even when resource is not dirty' do
747
+ expect(@return).to be(true)
748
+ end
749
+
750
+ it 'calls save hook expected number of times' do
751
+ expect(@user.save_hook_call_count).to be_nil
752
+ end
753
+ end
754
+
755
+ describe 'on a not new, dirty resource' do
756
+ before :all do
757
+ rescue_if @skip do
758
+ @user.age = 26
759
+ @return = @user.__send__(method)
760
+ end
761
+ end
762
+
763
+ it 'saves a resource succesfully when dirty' do
764
+ expect(@return).to be(true)
765
+ end
766
+
767
+ it 'actually stores the changes to persistent storage' do
768
+ expect(@user.attributes).to eq @user.reload.attributes
769
+ end
770
+
771
+ it 'calls save hook expected number of times' do
772
+ expect(@user.save_hook_call_count).to eq((method == :save) ? 1 : nil)
773
+ end
774
+ end
775
+
776
+ describe 'on a new, invalid resource' do
777
+ before :all do
778
+ @user = @user_model.new(name: nil)
779
+ expect { @user.__send__(method) }.to raise_error(DataMapper::Property::InvalidValueError) do |error|
780
+ expect(error.property).to eq @user_model.properties[:name]
781
+ end
782
+ end
783
+
784
+ it 'calls save hook expected number of times' do
785
+ expect(@user.save_hook_call_count).to eq((method == :save) ? 1 : nil)
786
+ end
787
+ end
788
+
789
+ describe 'on a dirty invalid resource' do
790
+ before :all do
791
+ rescue_if @skip do
792
+ @user.name = nil
793
+ end
794
+ end
795
+
796
+ it 'does not save an invalid resource' do
797
+ expect { @user.__send__(method) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
798
+ expect(error.property).to eq @user_model.properties[:name]
799
+ end)
800
+ end
801
+
802
+ it 'calls save hook expected number of times' do
803
+ expect(@user.save_hook_call_count).to eq((method == :save) ? 1 : nil)
804
+ end
805
+ end
806
+
807
+ describe 'with new resources in a has relationship' do
808
+ before do
809
+ rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
810
+ @initial_comments = @user.comments.size
811
+ @first_comment = @user.comments.new(:body => "DM is great!")
812
+ @second_comment = @comment_model.new(:user => @user, :body => "is it really?")
813
+ @return = @user.__send__(method)
814
+ end
815
+ end
816
+
817
+ it 'saves resource' do
818
+ pending unless@user.respond_to?(:comments)
819
+
820
+ expect(@return).to be(true)
821
+ end
822
+
823
+ it 'saves the first resource created through new' do
824
+ pending unless @user.respond_to?(:comments)
825
+
826
+ expect(@first_comment.new?).to be(false)
827
+ end
828
+
829
+ it 'saves the correct foreign key for the first resource' do
830
+ pending unless @user.respond_to?(:comments)
831
+
832
+ expect(@first_comment.user).to eql(@user)
833
+ end
834
+
835
+ it 'saves the second resource created through the constructor' do
836
+ pending 'Changing a belongs_to parent adds the resource to the correct association'
837
+
838
+ expect(@second_comment.new?).to be(false)
839
+ end
840
+
841
+ it 'saves the correct foreign key for the second resource' do
842
+ pending unless @user.respond_to?(:comments)
843
+
844
+ expect(@second_comment.user).to eql(@user)
845
+ end
846
+
847
+ it 'created 2 extra resources in persistent storage' do
848
+ pending 'Changing a belongs_to parent adds the resource to the correct association'
849
+
850
+ expect(@user.comments.size).to eq @initial_comments + 2
851
+ end
852
+ end
853
+
854
+ describe 'with dirty resources in a has relationship' do
855
+ before :all do
856
+ rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
857
+ @first_comment = @user.comments.create(:body => 'DM is great!')
858
+ @second_comment = @comment_model.create(:user => @user, :body => 'is it really?')
859
+
860
+ @first_comment.body = 'It still has rough edges'
861
+ @second_comment.body = 'But these cool specs help fixing that'
862
+ @second_comment.user = @user_model.create(:name => 'dkubb')
863
+
864
+ @return = @user.__send__(method)
865
+ end
866
+ end
867
+
868
+ it 'returns true' do
869
+ pending unless @user.respond_to?(:comments)
870
+
871
+ expect(@return).to be(true)
872
+ end
873
+
874
+ it 'is not dirty' do
875
+ expect(@user).not_to be_dirty
876
+ end
877
+
878
+ it 'has saved the first child resource' do
879
+ pending unless @user.respond_to?(:comments)
880
+
881
+ expect(@first_comment.model.get(*@first_comment.key).body).to eq 'It still has rough edges'
882
+ end
883
+
884
+ it 'has not saved the second child resource' do
885
+ pending unless @user.respond_to?(:comments)
886
+
887
+ expect(@second_comment.model.get(*@second_comment.key).body).to eq 'is it really?'
888
+ end
889
+ end
890
+
891
+ describe 'with a new dependency' do
892
+ before :all do
893
+ @first_comment = @comment_model.new(:body => "DM is great!")
894
+ @first_comment.user = @user_model.new(:name => 'dkubb')
895
+ end
896
+
897
+ it 'does not raise an exception when saving the resource' do
898
+ pending
899
+
900
+ expect { expect(@first_comment.send(method)).to be(false) }.not_to raise_error
901
+ end
902
+ end
903
+
904
+ describe 'with a dirty dependency' do
905
+ before :all do
906
+ rescue_if @skip do
907
+ @user.name = 'dbussink-the-second'
908
+
909
+ @first_comment = @comment_model.new(:body => 'DM is great!')
910
+ @first_comment.user = @user
911
+
912
+ @return = @first_comment.__send__(method)
913
+ end
914
+ end
915
+
916
+ it 'successfully saves the resource' do
917
+ expect(@return).to be(true)
918
+ end
919
+
920
+ it 'does not have a dirty dependency' do
921
+ expect(@user).not_to be_dirty
922
+ end
923
+
924
+ it 'successfully saves the dependency' do
925
+ expect(@user.name).to eq @user_model.get(*@user.key).name
926
+ end
927
+ end
928
+
929
+ describe 'with a new resource and new relations' do
930
+ before :all do
931
+ @article = @article_model.new(:body => "Main")
932
+ rescue_if 'TODO: fix for one to one association', (!@article.respond_to?(:paragraphs)) do
933
+ @paragraph = @article.paragraphs.new(:text => 'Content')
934
+
935
+ @article.__send__(method)
936
+ end
937
+ end
938
+
939
+ it 'is not dirty' do
940
+ pending unless @article.respond_to?(:paragraphs)
941
+
942
+ expect(@article).not_to be_dirty
943
+ end
944
+
945
+ it 'is not dirty' do
946
+ pending unless @article.respond_to?(:paragraphs)
947
+
948
+ expect(@paragraph).not_to be_dirty
949
+ end
950
+
951
+ it 'sets the related resource' do
952
+ pending unless @article.respond_to?(:paragraphs)
953
+
954
+ expect(@paragraph.article).to eq @article
955
+ end
956
+
957
+ it 'sets the foreign key properly' do
958
+ pending unless @article.respond_to?(:paragraphs)
959
+
960
+ expect(@paragraph.article_id).to eq @article.id
961
+ end
962
+ end
963
+
964
+ describe 'with a dirty resource with a changed key' do
965
+ before :all do
966
+ rescue_if @skip do
967
+ @original_key = @user.key
968
+ @user.name = 'dkubb'
969
+ @return = @user.__send__(method)
970
+ end
971
+ end
972
+
973
+ it 'saves a resource successfully when dirty' do
974
+ expect(@return).to be(true)
975
+ end
976
+
977
+ it 'actually stores the changes to persistent storage' do
978
+ expect(@user.name).to eq @user.reload.name
979
+ end
980
+
981
+ it 'updates the identity map' do
982
+ expect(@user.repository.identity_map(@user_model)).to have_key(%w(dkubb))
983
+ end
984
+
985
+ it 'removes the old entry from the identity map' do
986
+ expect(@user.repository.identity_map(@user_model)).not_to have_key(@original_key)
987
+ end
988
+ end
989
+
990
+ describe 'on a new resource with unsaved parent and grandparent' do
991
+ before :all do
992
+ @grandparent = @user_model.new(:name => 'dkubb', :comment => @comment)
993
+ @parent = @user_model.new(:name => 'ashleymoran', :comment => @comment, :referrer => @grandparent)
994
+ @child = @user_model.new(:name => 'mrship', :comment => @comment, :referrer => @parent)
995
+
996
+ @response = @child.__send__(method)
997
+ end
998
+
999
+ it 'returns true' do
1000
+ expect(@response).to be(true)
1001
+ end
1002
+
1003
+ it 'saves the child' do
1004
+ expect(@child).to be_saved
1005
+ end
1006
+
1007
+ it 'saves the parent' do
1008
+ expect(@parent).to be_saved
1009
+ end
1010
+
1011
+ it 'saves the grandparent' do
1012
+ expect(@grandparent).to be_saved
1013
+ end
1014
+
1015
+ it 'relates the child to the parent' do
1016
+ expect(@child.model.get(*@child.key).referrer).to eq @parent
1017
+ end
1018
+
1019
+ it 'relates the parent to the grandparent' do
1020
+ expect(@parent.model.get(*@parent.key).referrer).to eq @grandparent
1021
+ end
1022
+
1023
+ it 'relates the grandparent to nothing' do
1024
+ expect(@grandparent.model.get(*@grandparent.key).referrer).to be_nil
1025
+ end
1026
+ end
1027
+
1028
+ describe 'on a destroyed resource' do
1029
+ before :all do
1030
+ rescue_if @skip do
1031
+ @user.destroy
1032
+ end
1033
+ end
1034
+
1035
+ it 'raises an exception' do
1036
+ expect {
1037
+ @user.__send__(method)
1038
+ }.to raise_error(DataMapper::PersistenceError, "#{@user.model}##{method} cannot be called on a destroyed resource")
1039
+ end
1040
+ end
1041
+
1042
+ describe 'on a record with itself as a parent (circular dependency)' do
1043
+ before :all do
1044
+ rescue_if @skip do
1045
+ @user.parent = @user
1046
+ end
1047
+ end
1048
+
1049
+ it 'does not raise an exception' do
1050
+ expect {
1051
+ expect(@user.__send__(method)).to be(true)
1052
+ }.not_to raise_error(SystemStackError)
1053
+ end
1054
+ end
1055
+
1056
+ describe 'on a record with itself as a child (circular dependency)' do
1057
+ before :all do
1058
+ rescue_if @skip do
1059
+ @user.children = [ @user ]
1060
+ end
1061
+ end
1062
+
1063
+ it 'does not raise an exception' do
1064
+ expect {
1065
+ expect(@user.__send__(method)).to be(true)
1066
+ }.not_to raise_error(SystemStackError)
1067
+ end
1068
+ end
1069
+
1070
+ describe 'on a record with a parent as a child (circular dependency)' do
1071
+ before :all do
1072
+ rescue_if @skip do
1073
+ @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
1074
+ end
1075
+ end
1076
+
1077
+ it 'does not raise an exception' do
1078
+ expect {
1079
+ expect(@user.__send__(method)).to be(true)
1080
+ }.not_to raise_error(SystemStackError)
1081
+ end
1082
+ end
1083
+ end
1084
+ end
1085
+
1086
+ it { expect(@user).to respond_to(:saved?) }
1087
+
1088
+ describe '#saved?' do
1089
+
1090
+ describe 'on an existing record' do
1091
+ it { expect(@user).to be_saved }
1092
+ end
1093
+
1094
+ describe 'on a new record' do
1095
+
1096
+ before { @user = @user_model.new }
1097
+
1098
+ it { expect(@user).not_to be_saved }
1099
+ end
1100
+
1101
+ end
1102
+
1103
+ %i(update update!).each do |method|
1104
+ it { expect(@user).to respond_to(method) }
1105
+
1106
+ describe "##{method}" do
1107
+ describe 'with attributes' do
1108
+ before :all do
1109
+ rescue_if @skip do
1110
+ @attributes = { :description => 'Changed' }
1111
+ @return = @user.__send__(method, @attributes)
1112
+ end
1113
+ end
1114
+
1115
+ it 'returns true' do
1116
+ expect(@return).to be(true)
1117
+ end
1118
+
1119
+ it 'updates attributes of Resource' do
1120
+ @attributes.each { |key, value| expect(@user.__send__(key)).to eq value }
1121
+ end
1122
+
1123
+ it 'persists the changes' do
1124
+ resource = @user_model.get(*@user.key)
1125
+ @attributes.each { |key, value| expect(resource.__send__(key)).to eq value }
1126
+ end
1127
+ end
1128
+
1129
+ describe 'with attributes where one is a parent association' do
1130
+ before :all do
1131
+ rescue_if @skip do
1132
+ @attributes = { :referrer => @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment) }
1133
+ @return = @user.__send__(method, @attributes)
1134
+ end
1135
+ end
1136
+
1137
+ it 'returns true' do
1138
+ expect(@return).to be(true)
1139
+ end
1140
+
1141
+ it 'updates attributes of Resource' do
1142
+ @attributes.each { |key, value| expect(@user.__send__(key)).to eq value }
1143
+ end
1144
+
1145
+ it 'persists the changes' do
1146
+ resource = @user_model.get(*@user.key)
1147
+ @attributes.each { |key, value| expect(resource.__send__(key)).to eq value }
1148
+ end
1149
+ end
1150
+
1151
+ describe 'with attributes where a value is nil for a property that does not allow nil' do
1152
+ before do
1153
+ expect { @user.__send__(method, name: nil) }
1154
+ .to raise_error(DataMapper::Property::InvalidValueError) do |error|
1155
+ expect(error.property).to eq @user_model.properties[:name]
1156
+ end
1157
+ end
1158
+
1159
+ it 'does not persist the changes' do
1160
+ expect(@user.reload.name).not_to be_nil
1161
+ end
1162
+ end
1163
+
1164
+ describe 'on a new resource' do
1165
+ before :all do
1166
+ rescue_if @skip do
1167
+ @user = @user.model.new(@user.attributes)
1168
+ @user.age = 99
1169
+ end
1170
+ end
1171
+
1172
+ it 'raises an exception' do
1173
+ expect {
1174
+ @user.__send__(method, admin: true)
1175
+ }.to raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a new resource")
1176
+ end
1177
+ end
1178
+
1179
+ describe 'on a dirty resource' do
1180
+ before :all do
1181
+ rescue_if @skip do
1182
+ @user.age = 99
1183
+ end
1184
+ end
1185
+
1186
+ it 'raises an exception' do
1187
+ expect {
1188
+ @user.__send__(method, admin: true)
1189
+ }.to raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a dirty resource")
1190
+ end
1191
+ end
1192
+ end
1193
+ end
1194
+
1195
+ describe 'lazy loading' do
1196
+ before :all do
1197
+ rescue_if @skip do
1198
+ @user.name = 'dkubb'
1199
+ @user.age = 33
1200
+ @user.summary = 'Programmer'
1201
+
1202
+ # lazy load the description
1203
+ @user.description
1204
+ end
1205
+ end
1206
+
1207
+ it 'does not overwrite dirty attribute' do
1208
+ expect(@user.age).to eq 33
1209
+ end
1210
+
1211
+ it 'does not overwrite dirty lazy attribute' do
1212
+ expect(@user.summary).to eq 'Programmer'
1213
+ end
1214
+
1215
+ it 'does not overwrite dirty key' do
1216
+ pending
1217
+
1218
+ expect(@user.name).to eq 'dkubb'
1219
+ end
1220
+ end
1221
+ end