sbf-dm-core 1.3.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (259) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +29 -0
  3. data/.document +5 -0
  4. data/.gitignore +44 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +468 -0
  7. data/.travis.yml +57 -0
  8. data/.yardopts +1 -0
  9. data/Gemfile +70 -0
  10. data/LICENSE +20 -0
  11. data/README.md +269 -0
  12. data/Rakefile +4 -0
  13. data/dm-core.gemspec +21 -0
  14. data/lib/dm-core/adapters/abstract_adapter.rb +233 -0
  15. data/lib/dm-core/adapters/in_memory_adapter.rb +110 -0
  16. data/lib/dm-core/adapters.rb +249 -0
  17. data/lib/dm-core/associations/many_to_many.rb +477 -0
  18. data/lib/dm-core/associations/many_to_one.rb +282 -0
  19. data/lib/dm-core/associations/one_to_many.rb +332 -0
  20. data/lib/dm-core/associations/one_to_one.rb +84 -0
  21. data/lib/dm-core/associations/relationship.rb +650 -0
  22. data/lib/dm-core/backwards.rb +11 -0
  23. data/lib/dm-core/collection.rb +1486 -0
  24. data/lib/dm-core/core_ext/kernel.rb +21 -0
  25. data/lib/dm-core/core_ext/pathname.rb +4 -0
  26. data/lib/dm-core/core_ext/symbol.rb +10 -0
  27. data/lib/dm-core/identity_map.rb +6 -0
  28. data/lib/dm-core/model/hook.rb +99 -0
  29. data/lib/dm-core/model/is.rb +30 -0
  30. data/lib/dm-core/model/property.rb +244 -0
  31. data/lib/dm-core/model/relationship.rb +366 -0
  32. data/lib/dm-core/model/scope.rb +87 -0
  33. data/lib/dm-core/model.rb +876 -0
  34. data/lib/dm-core/property/binary.rb +19 -0
  35. data/lib/dm-core/property/boolean.rb +35 -0
  36. data/lib/dm-core/property/class.rb +23 -0
  37. data/lib/dm-core/property/date.rb +45 -0
  38. data/lib/dm-core/property/date_time.rb +44 -0
  39. data/lib/dm-core/property/decimal.rb +47 -0
  40. data/lib/dm-core/property/discriminator.rb +40 -0
  41. data/lib/dm-core/property/float.rb +27 -0
  42. data/lib/dm-core/property/integer.rb +32 -0
  43. data/lib/dm-core/property/invalid_value_error.rb +17 -0
  44. data/lib/dm-core/property/lookup.rb +26 -0
  45. data/lib/dm-core/property/numeric.rb +35 -0
  46. data/lib/dm-core/property/object.rb +33 -0
  47. data/lib/dm-core/property/serial.rb +13 -0
  48. data/lib/dm-core/property/string.rb +47 -0
  49. data/lib/dm-core/property/text.rb +12 -0
  50. data/lib/dm-core/property/time.rb +46 -0
  51. data/lib/dm-core/property/typecast/numeric.rb +32 -0
  52. data/lib/dm-core/property/typecast/time.rb +33 -0
  53. data/lib/dm-core/property.rb +856 -0
  54. data/lib/dm-core/property_set.rb +177 -0
  55. data/lib/dm-core/query/conditions/comparison.rb +886 -0
  56. data/lib/dm-core/query/conditions/operation.rb +710 -0
  57. data/lib/dm-core/query/direction.rb +33 -0
  58. data/lib/dm-core/query/operator.rb +34 -0
  59. data/lib/dm-core/query/path.rb +113 -0
  60. data/lib/dm-core/query/sort.rb +38 -0
  61. data/lib/dm-core/query.rb +1352 -0
  62. data/lib/dm-core/relationship_set.rb +69 -0
  63. data/lib/dm-core/repository.rb +226 -0
  64. data/lib/dm-core/resource/persistence_state/clean.rb +36 -0
  65. data/lib/dm-core/resource/persistence_state/deleted.rb +26 -0
  66. data/lib/dm-core/resource/persistence_state/dirty.rb +91 -0
  67. data/lib/dm-core/resource/persistence_state/immutable.rb +32 -0
  68. data/lib/dm-core/resource/persistence_state/persisted.rb +25 -0
  69. data/lib/dm-core/resource/persistence_state/transient.rb +87 -0
  70. data/lib/dm-core/resource/persistence_state.rb +70 -0
  71. data/lib/dm-core/resource.rb +1220 -0
  72. data/lib/dm-core/spec/lib/adapter_helpers.rb +63 -0
  73. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  74. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  75. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  76. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  77. data/lib/dm-core/spec/setup.rb +164 -0
  78. data/lib/dm-core/spec/shared/adapter_spec.rb +366 -0
  79. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  80. data/lib/dm-core/spec/shared/resource_spec.rb +1221 -0
  81. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  82. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +184 -0
  83. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  84. data/lib/dm-core/support/assertions.rb +8 -0
  85. data/lib/dm-core/support/chainable.rb +18 -0
  86. data/lib/dm-core/support/deprecate.rb +12 -0
  87. data/lib/dm-core/support/descendant_set.rb +89 -0
  88. data/lib/dm-core/support/equalizer.rb +48 -0
  89. data/lib/dm-core/support/ext/array.rb +22 -0
  90. data/lib/dm-core/support/ext/blank.rb +25 -0
  91. data/lib/dm-core/support/ext/hash.rb +67 -0
  92. data/lib/dm-core/support/ext/module.rb +47 -0
  93. data/lib/dm-core/support/ext/object.rb +57 -0
  94. data/lib/dm-core/support/ext/string.rb +24 -0
  95. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  96. data/lib/dm-core/support/hook.rb +388 -0
  97. data/lib/dm-core/support/inflections.rb +60 -0
  98. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  99. data/lib/dm-core/support/inflector/methods.rb +151 -0
  100. data/lib/dm-core/support/lazy_array.rb +451 -0
  101. data/lib/dm-core/support/local_object_space.rb +13 -0
  102. data/lib/dm-core/support/logger.rb +201 -0
  103. data/lib/dm-core/support/mash.rb +176 -0
  104. data/lib/dm-core/support/naming_conventions.rb +109 -0
  105. data/lib/dm-core/support/ordered_set.rb +381 -0
  106. data/lib/dm-core/support/subject.rb +33 -0
  107. data/lib/dm-core/support/subject_set.rb +251 -0
  108. data/lib/dm-core/version.rb +3 -0
  109. data/lib/dm-core.rb +274 -0
  110. data/script/performance.rb +275 -0
  111. data/script/profile.rb +218 -0
  112. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  113. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +69 -0
  114. data/spec/public/associations/many_to_many_spec.rb +197 -0
  115. data/spec/public/associations/many_to_one_spec.rb +83 -0
  116. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  117. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  118. data/spec/public/associations/one_to_many_spec.rb +81 -0
  119. data/spec/public/associations/one_to_one_spec.rb +176 -0
  120. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  121. data/spec/public/collection_spec.rb +69 -0
  122. data/spec/public/finalize_spec.rb +77 -0
  123. data/spec/public/model/hook_spec.rb +245 -0
  124. data/spec/public/model/property_spec.rb +91 -0
  125. data/spec/public/model/relationship_spec.rb +1040 -0
  126. data/spec/public/model_spec.rb +456 -0
  127. data/spec/public/property/binary_spec.rb +43 -0
  128. data/spec/public/property/boolean_spec.rb +21 -0
  129. data/spec/public/property/class_spec.rb +27 -0
  130. data/spec/public/property/date_spec.rb +21 -0
  131. data/spec/public/property/date_time_spec.rb +21 -0
  132. data/spec/public/property/decimal_spec.rb +23 -0
  133. data/spec/public/property/discriminator_spec.rb +134 -0
  134. data/spec/public/property/float_spec.rb +22 -0
  135. data/spec/public/property/integer_spec.rb +22 -0
  136. data/spec/public/property/object_spec.rb +117 -0
  137. data/spec/public/property/serial_spec.rb +22 -0
  138. data/spec/public/property/string_spec.rb +21 -0
  139. data/spec/public/property/text_spec.rb +62 -0
  140. data/spec/public/property/time_spec.rb +21 -0
  141. data/spec/public/property_spec.rb +333 -0
  142. data/spec/public/resource/state_spec.rb +72 -0
  143. data/spec/public/resource_spec.rb +289 -0
  144. data/spec/public/sel_spec.rb +53 -0
  145. data/spec/public/setup_spec.rb +145 -0
  146. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  147. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  148. data/spec/public/shared/collection_shared_spec.rb +1637 -0
  149. data/spec/public/shared/finder_shared_spec.rb +1647 -0
  150. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  151. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  152. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  153. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  154. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  155. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  156. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  157. data/spec/semipublic/associations_spec.rb +177 -0
  158. data/spec/semipublic/collection_spec.rb +110 -0
  159. data/spec/semipublic/model_spec.rb +96 -0
  160. data/spec/semipublic/property/binary_spec.rb +13 -0
  161. data/spec/semipublic/property/boolean_spec.rb +47 -0
  162. data/spec/semipublic/property/class_spec.rb +33 -0
  163. data/spec/semipublic/property/date_spec.rb +43 -0
  164. data/spec/semipublic/property/date_time_spec.rb +46 -0
  165. data/spec/semipublic/property/decimal_spec.rb +83 -0
  166. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  167. data/spec/semipublic/property/float_spec.rb +82 -0
  168. data/spec/semipublic/property/integer_spec.rb +82 -0
  169. data/spec/semipublic/property/lookup_spec.rb +29 -0
  170. data/spec/semipublic/property/serial_spec.rb +13 -0
  171. data/spec/semipublic/property/string_spec.rb +13 -0
  172. data/spec/semipublic/property/text_spec.rb +31 -0
  173. data/spec/semipublic/property/time_spec.rb +50 -0
  174. data/spec/semipublic/property_spec.rb +114 -0
  175. data/spec/semipublic/query/conditions/comparison_spec.rb +1502 -0
  176. data/spec/semipublic/query/conditions/operation_spec.rb +1296 -0
  177. data/spec/semipublic/query/path_spec.rb +471 -0
  178. data/spec/semipublic/query_spec.rb +3665 -0
  179. data/spec/semipublic/resource/state/clean_spec.rb +89 -0
  180. data/spec/semipublic/resource/state/deleted_spec.rb +79 -0
  181. data/spec/semipublic/resource/state/dirty_spec.rb +163 -0
  182. data/spec/semipublic/resource/state/immutable_spec.rb +107 -0
  183. data/spec/semipublic/resource/state/transient_spec.rb +163 -0
  184. data/spec/semipublic/resource/state_spec.rb +230 -0
  185. data/spec/semipublic/resource_spec.rb +23 -0
  186. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  187. data/spec/semipublic/shared/resource_shared_spec.rb +198 -0
  188. data/spec/semipublic/shared/resource_state_shared_spec.rb +91 -0
  189. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  190. data/spec/spec_helper.rb +34 -0
  191. data/spec/support/core_ext/hash.rb +10 -0
  192. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  193. data/spec/support/properties/huge_integer.rb +17 -0
  194. data/spec/unit/array_spec.rb +23 -0
  195. data/spec/unit/blank_spec.rb +73 -0
  196. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  197. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  198. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  199. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  200. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  201. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  202. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  203. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  204. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  205. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  206. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  207. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  208. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  216. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  217. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  218. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  219. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  220. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  221. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  222. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  223. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  224. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  226. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  227. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  228. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  229. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  230. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  231. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  232. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  233. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  237. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  239. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  240. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  241. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  242. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  243. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  244. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  245. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  246. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  247. data/spec/unit/hash_spec.rb +27 -0
  248. data/spec/unit/hook_spec.rb +1216 -0
  249. data/spec/unit/inflections_spec.rb +14 -0
  250. data/spec/unit/lazy_array_spec.rb +1949 -0
  251. data/spec/unit/mash_spec.rb +289 -0
  252. data/spec/unit/module_spec.rb +70 -0
  253. data/spec/unit/object_spec.rb +38 -0
  254. data/spec/unit/try_dup_spec.rb +46 -0
  255. data/tasks/ci.rake +1 -0
  256. data/tasks/spec.rake +18 -0
  257. data/tasks/yard.rake +9 -0
  258. data/tasks/yardstick.rake +19 -0
  259. metadata +323 -0
@@ -0,0 +1,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