ghost_dm-core 1.3.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +35 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +65 -0
  6. data/LICENSE +20 -0
  7. data/README.md +269 -0
  8. data/Rakefile +4 -0
  9. data/dm-core.gemspec +24 -0
  10. data/lib/dm-core.rb +292 -0
  11. data/lib/dm-core/adapters.rb +222 -0
  12. data/lib/dm-core/adapters/abstract_adapter.rb +237 -0
  13. data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
  14. data/lib/dm-core/associations/many_to_many.rb +499 -0
  15. data/lib/dm-core/associations/many_to_one.rb +290 -0
  16. data/lib/dm-core/associations/one_to_many.rb +348 -0
  17. data/lib/dm-core/associations/one_to_one.rb +86 -0
  18. data/lib/dm-core/associations/relationship.rb +663 -0
  19. data/lib/dm-core/backwards.rb +13 -0
  20. data/lib/dm-core/collection.rb +1515 -0
  21. data/lib/dm-core/core_ext/kernel.rb +23 -0
  22. data/lib/dm-core/core_ext/pathname.rb +6 -0
  23. data/lib/dm-core/core_ext/symbol.rb +10 -0
  24. data/lib/dm-core/identity_map.rb +7 -0
  25. data/lib/dm-core/model.rb +874 -0
  26. data/lib/dm-core/model/hook.rb +103 -0
  27. data/lib/dm-core/model/is.rb +32 -0
  28. data/lib/dm-core/model/property.rb +249 -0
  29. data/lib/dm-core/model/relationship.rb +378 -0
  30. data/lib/dm-core/model/scope.rb +89 -0
  31. data/lib/dm-core/property.rb +866 -0
  32. data/lib/dm-core/property/binary.rb +21 -0
  33. data/lib/dm-core/property/boolean.rb +20 -0
  34. data/lib/dm-core/property/class.rb +17 -0
  35. data/lib/dm-core/property/date.rb +10 -0
  36. data/lib/dm-core/property/date_time.rb +10 -0
  37. data/lib/dm-core/property/decimal.rb +36 -0
  38. data/lib/dm-core/property/discriminator.rb +44 -0
  39. data/lib/dm-core/property/float.rb +16 -0
  40. data/lib/dm-core/property/integer.rb +22 -0
  41. data/lib/dm-core/property/invalid_value_error.rb +22 -0
  42. data/lib/dm-core/property/lookup.rb +27 -0
  43. data/lib/dm-core/property/numeric.rb +38 -0
  44. data/lib/dm-core/property/object.rb +34 -0
  45. data/lib/dm-core/property/serial.rb +14 -0
  46. data/lib/dm-core/property/string.rb +38 -0
  47. data/lib/dm-core/property/text.rb +9 -0
  48. data/lib/dm-core/property/time.rb +10 -0
  49. data/lib/dm-core/property_set.rb +177 -0
  50. data/lib/dm-core/query.rb +1366 -0
  51. data/lib/dm-core/query/conditions/comparison.rb +911 -0
  52. data/lib/dm-core/query/conditions/operation.rb +721 -0
  53. data/lib/dm-core/query/direction.rb +36 -0
  54. data/lib/dm-core/query/operator.rb +35 -0
  55. data/lib/dm-core/query/path.rb +114 -0
  56. data/lib/dm-core/query/sort.rb +39 -0
  57. data/lib/dm-core/relationship_set.rb +72 -0
  58. data/lib/dm-core/repository.rb +226 -0
  59. data/lib/dm-core/resource.rb +1214 -0
  60. data/lib/dm-core/resource/persistence_state.rb +75 -0
  61. data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
  62. data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
  63. data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
  64. data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
  65. data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
  66. data/lib/dm-core/resource/persistence_state/transient.rb +80 -0
  67. data/lib/dm-core/spec/lib/adapter_helpers.rb +64 -0
  68. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  69. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  70. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  71. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  72. data/lib/dm-core/spec/setup.rb +174 -0
  73. data/lib/dm-core/spec/shared/adapter_spec.rb +341 -0
  74. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  75. data/lib/dm-core/spec/shared/resource_spec.rb +1232 -0
  76. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  77. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +176 -0
  78. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  79. data/lib/dm-core/support/assertions.rb +8 -0
  80. data/lib/dm-core/support/chainable.rb +18 -0
  81. data/lib/dm-core/support/deprecate.rb +12 -0
  82. data/lib/dm-core/support/descendant_set.rb +89 -0
  83. data/lib/dm-core/support/equalizer.rb +48 -0
  84. data/lib/dm-core/support/ext/array.rb +22 -0
  85. data/lib/dm-core/support/ext/blank.rb +25 -0
  86. data/lib/dm-core/support/ext/hash.rb +67 -0
  87. data/lib/dm-core/support/ext/module.rb +47 -0
  88. data/lib/dm-core/support/ext/object.rb +57 -0
  89. data/lib/dm-core/support/ext/string.rb +24 -0
  90. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  91. data/lib/dm-core/support/hook.rb +405 -0
  92. data/lib/dm-core/support/inflections.rb +60 -0
  93. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  94. data/lib/dm-core/support/inflector/methods.rb +151 -0
  95. data/lib/dm-core/support/lazy_array.rb +451 -0
  96. data/lib/dm-core/support/local_object_space.rb +13 -0
  97. data/lib/dm-core/support/logger.rb +201 -0
  98. data/lib/dm-core/support/mash.rb +176 -0
  99. data/lib/dm-core/support/naming_conventions.rb +90 -0
  100. data/lib/dm-core/support/ordered_set.rb +380 -0
  101. data/lib/dm-core/support/subject.rb +33 -0
  102. data/lib/dm-core/support/subject_set.rb +250 -0
  103. data/lib/dm-core/version.rb +3 -0
  104. data/script/performance.rb +275 -0
  105. data/script/profile.rb +218 -0
  106. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  107. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
  108. data/spec/public/associations/many_to_many_spec.rb +197 -0
  109. data/spec/public/associations/many_to_one_spec.rb +83 -0
  110. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  111. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  112. data/spec/public/associations/one_to_many_spec.rb +81 -0
  113. data/spec/public/associations/one_to_one_spec.rb +176 -0
  114. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  115. data/spec/public/collection_spec.rb +69 -0
  116. data/spec/public/finalize_spec.rb +76 -0
  117. data/spec/public/model/hook_spec.rb +246 -0
  118. data/spec/public/model/property_spec.rb +88 -0
  119. data/spec/public/model/relationship_spec.rb +1040 -0
  120. data/spec/public/model_spec.rb +462 -0
  121. data/spec/public/property/binary_spec.rb +41 -0
  122. data/spec/public/property/boolean_spec.rb +22 -0
  123. data/spec/public/property/class_spec.rb +28 -0
  124. data/spec/public/property/date_spec.rb +22 -0
  125. data/spec/public/property/date_time_spec.rb +22 -0
  126. data/spec/public/property/decimal_spec.rb +23 -0
  127. data/spec/public/property/discriminator_spec.rb +135 -0
  128. data/spec/public/property/float_spec.rb +22 -0
  129. data/spec/public/property/integer_spec.rb +22 -0
  130. data/spec/public/property/object_spec.rb +107 -0
  131. data/spec/public/property/serial_spec.rb +22 -0
  132. data/spec/public/property/string_spec.rb +22 -0
  133. data/spec/public/property/text_spec.rb +63 -0
  134. data/spec/public/property/time_spec.rb +22 -0
  135. data/spec/public/property_spec.rb +341 -0
  136. data/spec/public/resource_spec.rb +288 -0
  137. data/spec/public/sel_spec.rb +53 -0
  138. data/spec/public/setup_spec.rb +145 -0
  139. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  140. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  141. data/spec/public/shared/collection_shared_spec.rb +1667 -0
  142. data/spec/public/shared/finder_shared_spec.rb +1629 -0
  143. data/spec/rcov.opts +6 -0
  144. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  145. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  146. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  147. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  148. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  149. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  150. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  151. data/spec/semipublic/associations_spec.rb +177 -0
  152. data/spec/semipublic/collection_spec.rb +110 -0
  153. data/spec/semipublic/model_spec.rb +96 -0
  154. data/spec/semipublic/property/binary_spec.rb +13 -0
  155. data/spec/semipublic/property/boolean_spec.rb +47 -0
  156. data/spec/semipublic/property/class_spec.rb +33 -0
  157. data/spec/semipublic/property/date_spec.rb +43 -0
  158. data/spec/semipublic/property/date_time_spec.rb +46 -0
  159. data/spec/semipublic/property/decimal_spec.rb +83 -0
  160. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  161. data/spec/semipublic/property/float_spec.rb +82 -0
  162. data/spec/semipublic/property/integer_spec.rb +82 -0
  163. data/spec/semipublic/property/lookup_spec.rb +29 -0
  164. data/spec/semipublic/property/serial_spec.rb +13 -0
  165. data/spec/semipublic/property/string_spec.rb +13 -0
  166. data/spec/semipublic/property/text_spec.rb +31 -0
  167. data/spec/semipublic/property/time_spec.rb +50 -0
  168. data/spec/semipublic/property_spec.rb +114 -0
  169. data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
  170. data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
  171. data/spec/semipublic/query/path_spec.rb +471 -0
  172. data/spec/semipublic/query_spec.rb +3682 -0
  173. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  174. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  175. data/spec/semipublic/resource/state/dirty_spec.rb +162 -0
  176. data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
  177. data/spec/semipublic/resource/state/transient_spec.rb +162 -0
  178. data/spec/semipublic/resource/state_spec.rb +230 -0
  179. data/spec/semipublic/resource_spec.rb +23 -0
  180. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  181. data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
  182. data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
  183. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  184. data/spec/spec.opts +5 -0
  185. data/spec/spec_helper.rb +38 -0
  186. data/spec/support/core_ext/hash.rb +10 -0
  187. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  188. data/spec/support/properties/huge_integer.rb +17 -0
  189. data/spec/unit/array_spec.rb +23 -0
  190. data/spec/unit/blank_spec.rb +73 -0
  191. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  192. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  193. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  194. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  195. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  196. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  197. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  198. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  199. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  200. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  201. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  202. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  203. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  204. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  205. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  206. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  207. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  208. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  216. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  217. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  218. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  219. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  220. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  221. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  222. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  223. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  224. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  226. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  227. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  228. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  229. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  230. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  231. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  232. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  233. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  237. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  239. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  240. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  241. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  242. data/spec/unit/hash_spec.rb +28 -0
  243. data/spec/unit/hook_spec.rb +1235 -0
  244. data/spec/unit/inflections_spec.rb +16 -0
  245. data/spec/unit/lazy_array_spec.rb +1949 -0
  246. data/spec/unit/mash_spec.rb +312 -0
  247. data/spec/unit/module_spec.rb +71 -0
  248. data/spec/unit/object_spec.rb +38 -0
  249. data/spec/unit/try_dup_spec.rb +46 -0
  250. data/tasks/ci.rake +1 -0
  251. data/tasks/spec.rake +38 -0
  252. data/tasks/yard.rake +9 -0
  253. data/tasks/yardstick.rake +19 -0
  254. metadata +365 -0
@@ -0,0 +1,229 @@
1
+ share_examples_for 'A public Property' do
2
+ before :all do
3
+ %w[ @type @load_as @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 ".load_as" do
105
+ it "should return the load_as" do
106
+ [@type, @subtype].each do |type|
107
+ type.load_as.should be(@load_as)
108
+ end
109
+ end
110
+
111
+ it "should change the load_as class" do
112
+ @subtype.load_as Object
113
+ @subtype.load_as.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,1232 @@
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
+ expect { @user.__send__(method) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
789
+ error.property.should == @user_model.properties[:name]
790
+ end)
791
+ end
792
+
793
+ it 'should call save hook expected number of times' do
794
+ @user.save_hook_call_count.should == (method == :save ? 1 : nil)
795
+ end
796
+ end
797
+
798
+ describe 'on a dirty invalid resource' do
799
+ before :all do
800
+ rescue_if @skip do
801
+ @user.name = nil
802
+ end
803
+ end
804
+
805
+ it 'should not save an invalid resource' do
806
+ expect { @user.__send__(method) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
807
+ error.property.should == @user_model.properties[:name]
808
+ end)
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 do
1165
+ expect { @user.__send__(method, :name => nil) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
1166
+ error.property.should == @user_model.properties[:name]
1167
+ end)
1168
+ end
1169
+
1170
+ it 'should not persist the changes' do
1171
+ @user.reload.name.should_not be_nil
1172
+ end
1173
+ end
1174
+
1175
+ describe 'on a new resource' do
1176
+ before :all do
1177
+ rescue_if @skip do
1178
+ @user = @user.model.new(@user.attributes)
1179
+ @user.age = 99
1180
+ end
1181
+ end
1182
+
1183
+ it 'should raise an exception' do
1184
+ lambda {
1185
+ @user.__send__(method, :admin => true)
1186
+ }.should raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a new resource")
1187
+ end
1188
+ end
1189
+
1190
+ describe 'on a dirty resource' do
1191
+ before :all do
1192
+ rescue_if @skip do
1193
+ @user.age = 99
1194
+ end
1195
+ end
1196
+
1197
+ it 'should raise an exception' do
1198
+ lambda {
1199
+ @user.__send__(method, :admin => true)
1200
+ }.should raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a dirty resource")
1201
+ end
1202
+ end
1203
+ end
1204
+ end
1205
+
1206
+ describe 'lazy loading' do
1207
+ before :all do
1208
+ rescue_if @skip do
1209
+ @user.name = 'dkubb'
1210
+ @user.age = 33
1211
+ @user.summary = 'Programmer'
1212
+
1213
+ # lazy load the description
1214
+ @user.description
1215
+ end
1216
+ end
1217
+
1218
+ it 'should not overwrite dirty attribute' do
1219
+ @user.age.should == 33
1220
+ end
1221
+
1222
+ it 'should not overwrite dirty lazy attribute' do
1223
+ @user.summary.should == 'Programmer'
1224
+ end
1225
+
1226
+ it 'should not overwrite dirty key' do
1227
+ pending do
1228
+ @user.name.should == 'dkubb'
1229
+ end
1230
+ end
1231
+ end
1232
+ end