ardm-core 1.2.1

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