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,1667 @@
1
+ share_examples_for 'A public Collection' do
2
+ before :all do
3
+ %w[ @article_model @article @other @original @articles @other_articles ].each do |ivar|
4
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_defined?(ivar)
5
+ raise "+#{ivar}+ should not be nil in before block" unless instance_variable_get(ivar)
6
+ end
7
+
8
+ @articles.loaded?.should == loaded
9
+ end
10
+
11
+ before :all do
12
+ @no_join = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter) ||
13
+ defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
14
+
15
+ @one_to_many = @articles.kind_of?(DataMapper::Associations::OneToMany::Collection)
16
+ @many_to_many = @articles.kind_of?(DataMapper::Associations::ManyToMany::Collection)
17
+
18
+ @skip = @no_join && @many_to_many
19
+ end
20
+
21
+ before do
22
+ pending if @skip
23
+ end
24
+
25
+ subject { @articles }
26
+
27
+ it { should respond_to(:<<) }
28
+
29
+ describe '#<<' do
30
+ before :all do
31
+ @resource = @article_model.new(:title => 'Title')
32
+
33
+ @return = @articles << @resource
34
+ end
35
+
36
+ it 'should return a Collection' do
37
+ @return.should be_kind_of(DataMapper::Collection)
38
+ end
39
+
40
+ it 'should return self' do
41
+ @return.should equal(@articles)
42
+ end
43
+
44
+ it 'should append one Resource to the Collection' do
45
+ @articles.last.should equal(@resource)
46
+ end
47
+ end
48
+
49
+ it { should respond_to(:blank?) }
50
+
51
+ describe '#blank?' do
52
+ describe 'when the collection is empty' do
53
+ it 'should be true' do
54
+ @articles.clear.blank?.should be(true)
55
+ end
56
+ end
57
+
58
+ describe 'when the collection is not empty' do
59
+ it 'should be false' do
60
+ @articles.blank?.should be(false)
61
+ end
62
+ end
63
+ end
64
+
65
+ it { should respond_to(:clean?) }
66
+
67
+ describe '#clean?' do
68
+ describe 'with all clean resources in the collection' do
69
+ it 'should return true' do
70
+ @articles.clean?.should be(true)
71
+ end
72
+ end
73
+
74
+ describe 'with a dirty resource in the collection' do
75
+ before :all do
76
+ @articles.each { |r| r.content = 'Changed' }
77
+ end
78
+
79
+ it 'should return true' do
80
+ @articles.clean?.should be(false)
81
+ end
82
+ end
83
+ end
84
+
85
+ it { should respond_to(:clear) }
86
+
87
+ describe '#clear' do
88
+ before :all do
89
+ @resources = @articles.entries
90
+
91
+ @return = @articles.clear
92
+ end
93
+
94
+ it 'should return a Collection' do
95
+ @return.should be_kind_of(DataMapper::Collection)
96
+ end
97
+
98
+ it 'should return self' do
99
+ @return.should equal(@articles)
100
+ end
101
+
102
+ it 'should make the Collection empty' do
103
+ @articles.should be_empty
104
+ end
105
+ end
106
+
107
+ [ :collect!, :map! ].each do |method|
108
+ it { should respond_to(method) }
109
+
110
+ describe "##{method}" do
111
+ before :all do
112
+ @resources = @articles.dup.entries
113
+
114
+ @return = @articles.send(method) { |resource| @article_model.new(:title => 'Ignored Title', :content => 'New Content') }
115
+ end
116
+
117
+ it 'should return a Collection' do
118
+ @return.should be_kind_of(DataMapper::Collection)
119
+ end
120
+
121
+ it 'should return self' do
122
+ @return.should equal(@articles)
123
+ end
124
+
125
+ it 'should update the Collection inline' do
126
+ @articles.each { |resource|
127
+ DataMapper::Ext::Hash.only(resource.attributes, :title, :content).should ==
128
+ { :title => 'Sample Article', :content => 'New Content' }
129
+ }
130
+ end
131
+ end
132
+ end
133
+
134
+ it { should respond_to(:concat) }
135
+
136
+ describe '#concat' do
137
+ before :all do
138
+ @return = @articles.concat(@other_articles)
139
+ end
140
+
141
+ it 'should return a Collection' do
142
+ @return.should be_kind_of(DataMapper::Collection)
143
+ end
144
+
145
+ it 'should return self' do
146
+ @return.should equal(@articles)
147
+ end
148
+
149
+ it 'should concatenate the two collections' do
150
+ @return.should == [ @article, @other ]
151
+ end
152
+ end
153
+
154
+ [ :create, :create! ].each do |method|
155
+ it { should respond_to(method) }
156
+
157
+ describe "##{method}" do
158
+ describe 'when scoped to a property' do
159
+ before :all do
160
+ @return = @resource = @articles.send(method)
161
+ end
162
+
163
+ it 'should return a Resource' do
164
+ @return.should be_kind_of(DataMapper::Resource)
165
+ end
166
+
167
+ it 'should be a saved Resource' do
168
+ @resource.should be_saved
169
+ end
170
+
171
+ it 'should append the Resource to the Collection' do
172
+ @articles.last.should equal(@resource)
173
+ end
174
+
175
+ it 'should use the query conditions to set default values' do
176
+ @resource.title.should == 'Sample Article'
177
+ end
178
+
179
+ it 'should not append a Resource if create fails' do
180
+ pending 'TODO: not sure how to best spec this'
181
+ end
182
+ end
183
+
184
+ describe 'when scoped to the key' do
185
+ before :all do
186
+ @articles = @articles.all(:id => 1)
187
+
188
+ @return = @resource = @articles.send(method)
189
+ end
190
+
191
+ it 'should return a Resource' do
192
+ @return.should be_kind_of(DataMapper::Resource)
193
+ end
194
+
195
+ it 'should be a saved Resource' do
196
+ @resource.should be_saved
197
+ end
198
+
199
+ it 'should append the Resource to the Collection' do
200
+ @articles.last.should equal(@resource)
201
+ end
202
+
203
+ it 'should not use the query conditions to set default values' do
204
+ @resource.id.should_not == 1
205
+ end
206
+
207
+ it 'should not append a Resource if create fails' do
208
+ pending 'TODO: not sure how to best spec this'
209
+ end
210
+ end
211
+
212
+ describe 'when scoped to a property with multiple values' do
213
+ before :all do
214
+ @articles = @articles.all(:content => %w[ Sample Other ])
215
+
216
+ @return = @resource = @articles.send(method)
217
+ end
218
+
219
+ it 'should return a Resource' do
220
+ @return.should be_kind_of(DataMapper::Resource)
221
+ end
222
+
223
+ it 'should be a saved Resource' do
224
+ @resource.should be_saved
225
+ end
226
+
227
+ it 'should append the Resource to the Collection' do
228
+ @articles.last.should equal(@resource)
229
+ end
230
+
231
+ it 'should not use the query conditions to set default values' do
232
+ @resource.content.should be_nil
233
+ end
234
+
235
+ it 'should not append a Resource if create fails' do
236
+ pending 'TODO: not sure how to best spec this'
237
+ end
238
+ end
239
+
240
+ describe 'when scoped with a condition other than eql' do
241
+ before :all do
242
+ @articles = @articles.all(:content.not => 'Sample')
243
+
244
+ @return = @resource = @articles.send(method)
245
+ end
246
+
247
+ it 'should return a Resource' do
248
+ @return.should be_kind_of(DataMapper::Resource)
249
+ end
250
+
251
+ it 'should be a saved Resource' do
252
+ @resource.should be_saved
253
+ end
254
+
255
+ it 'should append the Resource to the Collection' do
256
+ @articles.last.should equal(@resource)
257
+ end
258
+
259
+ it 'should not use the query conditions to set default values' do
260
+ @resource.content.should be_nil
261
+ end
262
+
263
+ it 'should not append a Resource if create fails' do
264
+ pending 'TODO: not sure how to best spec this'
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ [ :difference, :- ].each do |method|
271
+ it { should respond_to(method) }
272
+
273
+ describe "##{method}" do
274
+ subject { @articles.send(method, @other_articles) }
275
+
276
+ describe 'with a Collection' do
277
+ it { should be_kind_of(DataMapper::Collection) }
278
+
279
+ it { should == [ @article ] }
280
+
281
+ it { subject.query.should == @articles.query.difference(@other_articles.query) }
282
+
283
+ it { should == @articles.to_a - @other_articles.to_a }
284
+ end
285
+
286
+ describe 'with an Array' do
287
+ before { @other_articles = @other_articles.to_ary }
288
+
289
+ it { should be_kind_of(DataMapper::Collection) }
290
+
291
+ it { should == [ @article ] }
292
+
293
+ it { should == @articles.to_a - @other_articles.to_a }
294
+ end
295
+
296
+ describe 'with a Set' do
297
+ before { @other_articles = @other_articles.to_set }
298
+
299
+ it { should be_kind_of(DataMapper::Collection) }
300
+
301
+ it { should == [ @article ] }
302
+
303
+ it { should == @articles.to_a - @other_articles.to_a }
304
+ end
305
+ end
306
+ end
307
+
308
+ it { should respond_to(:delete) }
309
+
310
+ describe '#delete' do
311
+ describe 'with a Resource within the Collection' do
312
+ before :all do
313
+ @return = @resource = @articles.delete(@article)
314
+ end
315
+
316
+ it 'should return a DataMapper::Resource' do
317
+ @return.should be_kind_of(DataMapper::Resource)
318
+ end
319
+
320
+ it 'should be the expected Resource' do
321
+ # compare keys because FK attributes may have been altered
322
+ # when removing from the Collection
323
+ @resource.key.should == @article.key
324
+ end
325
+
326
+ it 'should remove the Resource from the Collection' do
327
+ @articles.should_not be_include(@resource)
328
+ end
329
+ end
330
+
331
+ describe 'with a Resource not within the Collection' do
332
+ before :all do
333
+ @return = @articles.delete(@other)
334
+ end
335
+
336
+ it 'should return nil' do
337
+ @return.should be_nil
338
+ end
339
+ end
340
+ end
341
+
342
+ it { should respond_to(:delete_at) }
343
+
344
+ describe '#delete_at' do
345
+ describe 'with an offset within the Collection' do
346
+ before :all do
347
+ @return = @resource = @articles.delete_at(0)
348
+ end
349
+
350
+ it 'should return a DataMapper::Resource' do
351
+ @return.should be_kind_of(DataMapper::Resource)
352
+ end
353
+
354
+ it 'should be the expected Resource' do
355
+ @resource.key.should == @article.key
356
+ end
357
+
358
+ it 'should remove the Resource from the Collection' do
359
+ @articles.should_not be_include(@resource)
360
+ end
361
+ end
362
+
363
+ describe 'with an offset not within the Collection' do
364
+ before :all do
365
+ @return = @articles.delete_at(1)
366
+ end
367
+
368
+ it 'should return nil' do
369
+ @return.should be_nil
370
+ end
371
+ end
372
+ end
373
+
374
+ it { should respond_to(:delete_if) }
375
+
376
+ describe '#delete_if' do
377
+ describe 'with a block that matches a Resource in the Collection' do
378
+ before :all do
379
+ @resources = @articles.dup.entries
380
+
381
+ @return = @articles.delete_if { true }
382
+ end
383
+
384
+ it 'should return a Collection' do
385
+ @return.should be_kind_of(DataMapper::Collection)
386
+ end
387
+
388
+ it 'should return self' do
389
+ @return.should equal(@articles)
390
+ end
391
+
392
+ it 'should remove the Resources from the Collection' do
393
+ @resources.each { |resource| @articles.should_not be_include(resource) }
394
+ end
395
+ end
396
+
397
+ describe 'with a block that does not match a Resource in the Collection' do
398
+ before :all do
399
+ @resources = @articles.dup.entries
400
+
401
+ @return = @articles.delete_if { false }
402
+ end
403
+
404
+ it 'should return a Collection' do
405
+ @return.should be_kind_of(DataMapper::Collection)
406
+ end
407
+
408
+ it 'should return self' do
409
+ @return.should equal(@articles)
410
+ end
411
+
412
+ it 'should not modify the Collection' do
413
+ @articles.should == @resources
414
+ end
415
+ end
416
+ end
417
+
418
+ [ :destroy, :destroy! ].each do |method|
419
+ it { should respond_to(method) }
420
+
421
+ describe "##{method}" do
422
+ describe 'on a normal collection' do
423
+ before :all do
424
+ @return = @articles.send(method)
425
+ end
426
+
427
+ it 'should return true' do
428
+ @return.should be(true)
429
+ end
430
+
431
+ it 'should remove the Resources from the datasource' do
432
+ @article_model.all(:title => 'Sample Article').should be_empty
433
+ end
434
+
435
+ it 'should clear the collection' do
436
+ @articles.should be_empty
437
+ end
438
+ end
439
+
440
+ describe 'on a limited collection' do
441
+ before :all do
442
+ @other = @articles.create
443
+ @limited = @articles.all(:limit => 1)
444
+
445
+ @return = @limited.send(method)
446
+ end
447
+
448
+ it 'should return true' do
449
+ @return.should be(true)
450
+ end
451
+
452
+ it 'should remove the Resources from the datasource' do
453
+ @article_model.all(:title => 'Sample Article').should == [ @other ]
454
+ end
455
+
456
+ it 'should clear the collection' do
457
+ @limited.should be_empty
458
+ end
459
+
460
+ it 'should not destroy the other Resource' do
461
+ @article_model.get(*@other.key).should_not be_nil
462
+ end
463
+ end
464
+ end
465
+ end
466
+
467
+ it { should respond_to(:dirty?) }
468
+
469
+ describe '#dirty?' do
470
+ describe 'with all clean resources in the collection' do
471
+ it 'should return false' do
472
+ @articles.dirty?.should be(false)
473
+ end
474
+ end
475
+
476
+ describe 'with a dirty resource in the collection' do
477
+ before :all do
478
+ @articles.each { |r| r.content = 'Changed' }
479
+ end
480
+
481
+ it 'should return true' do
482
+ @articles.dirty?.should be(true)
483
+ end
484
+ end
485
+ end
486
+
487
+ it { should respond_to(:insert) }
488
+
489
+ describe '#insert' do
490
+ before :all do
491
+ @resources = @other_articles
492
+
493
+ @return = @articles.insert(0, *@resources)
494
+ end
495
+
496
+ it 'should return a Collection' do
497
+ @return.should be_kind_of(DataMapper::Collection)
498
+ end
499
+
500
+ it 'should return self' do
501
+ @return.should equal(@articles)
502
+ end
503
+
504
+ it 'should insert one or more Resources at a given offset' do
505
+ @articles.should == @resources << @article
506
+ end
507
+ end
508
+
509
+ it { should respond_to(:inspect) }
510
+
511
+ describe '#inspect' do
512
+ before :all do
513
+ @copy = @articles.dup
514
+ @copy << @article_model.new(:title => 'Ignored Title', :content => 'Other Article')
515
+
516
+ @return = @copy.inspect
517
+ end
518
+
519
+ it { @return.should match(/\A\[.*\]\z/) }
520
+
521
+ it { @return.should match(/\bid=#{@article.id}\b/) }
522
+ it { @return.should match(/\bid=nil\b/) }
523
+
524
+ it { @return.should match(/\btitle=\"Sample Article\"\s/) }
525
+ it { @return.should_not match(/\btitle=\"Ignored Title\"\s/) }
526
+ it { @return.should match(/\bcontent=\"Other Article\"\s/) }
527
+ end
528
+
529
+ [ :intersection, :& ].each do |method|
530
+ it { should respond_to(method) }
531
+
532
+ describe "##{method}" do
533
+ subject { @articles.send(method, @other_articles) }
534
+
535
+ describe 'with a Collection' do
536
+ it { should be_kind_of(DataMapper::Collection) }
537
+
538
+ it { should == [] }
539
+
540
+ it { subject.query.should == @articles.query.intersection(@other_articles.query) }
541
+
542
+ it { should == @articles.to_a & @other_articles.to_a }
543
+ end
544
+
545
+ describe 'with an Array' do
546
+ before { @other_articles = @other_articles.to_ary }
547
+
548
+ it { should be_kind_of(DataMapper::Collection) }
549
+
550
+ it { should == [] }
551
+
552
+ it { should == @articles.to_a & @other_articles.to_a }
553
+ end
554
+
555
+ describe 'with a Set' do
556
+ before { @other_articles = @other_articles.to_set }
557
+
558
+ it { should be_kind_of(DataMapper::Collection) }
559
+
560
+ it { should == [] }
561
+
562
+ it { should == @articles.to_a & @other_articles.to_a }
563
+ end
564
+ end
565
+ end
566
+
567
+ it { should respond_to(:new) }
568
+
569
+ describe '#new' do
570
+ describe 'when scoped to a property' do
571
+ before :all do
572
+ @source = @articles.new(:attachment => "A File")
573
+ @return = @resource = @articles.new(:original => @source)
574
+ end
575
+
576
+ it 'should return a Resource' do
577
+ @return.should be_kind_of(DataMapper::Resource)
578
+ end
579
+
580
+ it 'should be a new Resource' do
581
+ @resource.should be_new
582
+ end
583
+
584
+ it 'should append the Resource to the Collection' do
585
+ @articles.last.should equal(@resource)
586
+ end
587
+
588
+ it 'should use the query conditions to set default values' do
589
+ @resource.title.should == 'Sample Article'
590
+ end
591
+
592
+ it 'should use the query conditions to set default values when accessed through a m:1 relationship' do
593
+ @resource.original.attachment.should == 'A File'
594
+ end
595
+ end
596
+
597
+ describe 'when scoped to the key' do
598
+ before :all do
599
+ @articles = @articles.all(:id => 1)
600
+
601
+ @return = @resource = @articles.new
602
+ end
603
+
604
+ it 'should return a Resource' do
605
+ @return.should be_kind_of(DataMapper::Resource)
606
+ end
607
+
608
+ it 'should be a new Resource' do
609
+ @resource.should be_new
610
+ end
611
+
612
+ it 'should append the Resource to the Collection' do
613
+ @articles.last.should equal(@resource)
614
+ end
615
+
616
+ it 'should not use the query conditions to set default values' do
617
+ @resource.id.should be_nil
618
+ end
619
+ end
620
+
621
+ describe 'when scoped to a property with multiple values' do
622
+ before :all do
623
+ @articles = @articles.all(:content => %w[ Sample Other ])
624
+
625
+ @return = @resource = @articles.new
626
+ end
627
+
628
+ it 'should return a Resource' do
629
+ @return.should be_kind_of(DataMapper::Resource)
630
+ end
631
+
632
+ it 'should be a new Resource' do
633
+ @resource.should be_new
634
+ end
635
+
636
+ it 'should append the Resource to the Collection' do
637
+ @articles.last.should equal(@resource)
638
+ end
639
+
640
+ it 'should not use the query conditions to set default values' do
641
+ @resource.content.should be_nil
642
+ end
643
+ end
644
+
645
+ describe 'when scoped with a condition other than eql' do
646
+ before :all do
647
+ @articles = @articles.all(:content.not => 'Sample')
648
+
649
+ @return = @resource = @articles.new
650
+ end
651
+
652
+ it 'should return a Resource' do
653
+ @return.should be_kind_of(DataMapper::Resource)
654
+ end
655
+
656
+ it 'should be a new Resource' do
657
+ @resource.should be_new
658
+ end
659
+
660
+ it 'should append the Resource to the Collection' do
661
+ @articles.last.should equal(@resource)
662
+ end
663
+
664
+ it 'should not use the query conditions to set default values' do
665
+ @resource.content.should be_nil
666
+ end
667
+ end
668
+ end
669
+
670
+ it { should respond_to(:pop) }
671
+
672
+ describe '#pop' do
673
+ before :all do
674
+ @new = @articles.create(:title => 'Sample Article') # TODO: freeze @new
675
+ end
676
+
677
+ describe 'with no arguments' do
678
+ before :all do
679
+ @return = @articles.pop
680
+ end
681
+
682
+ it 'should return a Resource' do
683
+ @return.should be_kind_of(DataMapper::Resource)
684
+ end
685
+
686
+ it 'should be the last Resource in the Collection' do
687
+ @return.should == @new
688
+ end
689
+
690
+ it 'should remove the Resource from the Collection' do
691
+ @articles.should_not be_include(@new)
692
+ end
693
+ end
694
+
695
+ if RUBY_VERSION >= '1.8.7'
696
+ describe 'with a limit specified' do
697
+ before :all do
698
+ @return = @articles.pop(1)
699
+ end
700
+
701
+ it 'should return an Array' do
702
+ @return.should be_kind_of(Array)
703
+ end
704
+
705
+ it 'should return the expected Resources' do
706
+ @return.should == [ @new ]
707
+ end
708
+
709
+ it 'should remove the Resource from the Collection' do
710
+ @articles.should_not be_include(@new)
711
+ end
712
+ end
713
+ end
714
+ end
715
+
716
+ it { should respond_to(:push) }
717
+
718
+ describe '#push' do
719
+ before :all do
720
+ @resources = [ @article_model.new(:title => 'Title 1'), @article_model.new(:title => 'Title 2') ]
721
+
722
+ @return = @articles.push(*@resources)
723
+ end
724
+
725
+ it 'should return a Collection' do
726
+ @return.should be_kind_of(DataMapper::Collection)
727
+ end
728
+
729
+ it 'should return self' do
730
+ @return.should equal(@articles)
731
+ end
732
+
733
+ it 'should append the Resources to the Collection' do
734
+ @articles.should == [ @article ] + @resources
735
+ end
736
+ end
737
+
738
+ it { should respond_to(:reject!) }
739
+
740
+ describe '#reject!' do
741
+ describe 'with a block that matches a Resource in the Collection' do
742
+ before :all do
743
+ @resources = @articles.dup.entries
744
+
745
+ @return = @articles.reject! { true }
746
+ end
747
+
748
+ it 'should return a Collection' do
749
+ @return.should be_kind_of(DataMapper::Collection)
750
+ end
751
+
752
+ it 'should return self' do
753
+ @return.should equal(@articles)
754
+ end
755
+
756
+ it 'should remove the Resources from the Collection' do
757
+ @resources.each { |resource| @articles.should_not be_include(resource) }
758
+ end
759
+ end
760
+
761
+ describe 'with a block that does not match a Resource in the Collection' do
762
+ before :all do
763
+ @resources = @articles.dup.entries
764
+
765
+ @return = @articles.reject! { false }
766
+ end
767
+
768
+ it 'should return nil' do
769
+ @return.should be_nil
770
+ end
771
+
772
+ it 'should not modify the Collection' do
773
+ @articles.should == @resources
774
+ end
775
+ end
776
+ end
777
+
778
+ it { should respond_to(:reload) }
779
+
780
+ describe '#reload' do
781
+ describe 'with no arguments' do
782
+ before :all do
783
+ @resources = @articles.dup.entries
784
+
785
+ @return = @collection = @articles.reload
786
+ end
787
+
788
+ # FIXME: this is spec order dependent, move this into a helper method
789
+ # and execute in the before :all block
790
+ unless loaded
791
+ it 'should not be a kicker' do
792
+ pending do
793
+ @articles.should_not be_loaded
794
+ end
795
+ end
796
+ end
797
+
798
+ it 'should return a Collection' do
799
+ @return.should be_kind_of(DataMapper::Collection)
800
+ end
801
+
802
+ it 'should return self' do
803
+ @return.should equal(@articles)
804
+ end
805
+
806
+ { :title => true, :content => false }.each do |attribute, expected|
807
+ it "should have query field #{attribute.inspect} #{'not' unless expected} loaded".squeeze(' ') do
808
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should == expected }
809
+ end
810
+ end
811
+ end
812
+
813
+ describe 'with a Hash query' do
814
+ before :all do
815
+ @resources = @articles.dup.entries
816
+
817
+ @return = @collection = @articles.reload(:fields => [ :content ]) # :title is a default field
818
+ end
819
+
820
+ # FIXME: this is spec order dependent, move this into a helper method
821
+ # and execute in the before :all block
822
+ unless loaded
823
+ it 'should not be a kicker' do
824
+ pending do
825
+ @articles.should_not be_loaded
826
+ end
827
+ end
828
+ end
829
+
830
+ it 'should return a Collection' do
831
+ @return.should be_kind_of(DataMapper::Collection)
832
+ end
833
+
834
+ it 'should return self' do
835
+ @return.should equal(@articles)
836
+ end
837
+
838
+ [ :id, :content, :title ].each do |attribute|
839
+ it "should have query field #{attribute.inspect} loaded" do
840
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should be(true) }
841
+ end
842
+ end
843
+ end
844
+
845
+ describe 'with a Query' do
846
+ before :all do
847
+ @query = DataMapper::Query.new(@repository, @article_model, :fields => [ :content ]) # :title is an original field
848
+
849
+ @return = @collection = @articles.reload(@query)
850
+ end
851
+
852
+ # FIXME: this is spec order dependent, move this into a helper method
853
+ # and execute in the before :all block
854
+ unless loaded
855
+ it 'should not be a kicker' do
856
+ pending do
857
+ @articles.should_not be_loaded
858
+ end
859
+ end
860
+ end
861
+
862
+ it 'should return a Collection' do
863
+ @return.should be_kind_of(DataMapper::Collection)
864
+ end
865
+
866
+ it 'should return self' do
867
+ @return.should equal(@articles)
868
+ end
869
+
870
+ [ :id, :content, :title ].each do |attribute|
871
+ it "should have query field #{attribute.inspect} loaded" do
872
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should be(true) }
873
+ end
874
+ end
875
+ end
876
+ end
877
+
878
+ it { should respond_to(:replace) }
879
+
880
+ describe '#replace' do
881
+ describe 'when provided an Array of Resources' do
882
+ before :all do
883
+ @resources = @articles.dup.entries
884
+
885
+ @return = @articles.replace(@other_articles)
886
+ end
887
+
888
+ it 'should return a Collection' do
889
+ @return.should be_kind_of(DataMapper::Collection)
890
+ end
891
+
892
+ it 'should return self' do
893
+ @return.should equal(@articles)
894
+ end
895
+
896
+ it 'should update the Collection with new Resources' do
897
+ @articles.should == @other_articles
898
+ end
899
+ end
900
+
901
+ describe 'when provided an Array of Hashes' do
902
+ before :all do
903
+ @array = [ { :content => 'From Hash' } ].freeze
904
+
905
+ @return = @articles.replace(@array)
906
+ end
907
+
908
+ it 'should return a Collection' do
909
+ @return.should be_kind_of(DataMapper::Collection)
910
+ end
911
+
912
+ it 'should return self' do
913
+ @return.should equal(@articles)
914
+ end
915
+
916
+ it 'should initialize a Resource' do
917
+ @return.first.should be_kind_of(DataMapper::Resource)
918
+ end
919
+
920
+ it 'should be a new Resource' do
921
+ @return.first.should be_new
922
+ end
923
+
924
+ it 'should be a Resource with attributes matching the Hash' do
925
+ DataMapper::Ext::Hash.only(@return.first.attributes, *@array.first.keys).should == @array.first
926
+ end
927
+ end
928
+ end
929
+
930
+ it { should respond_to(:reverse!) }
931
+
932
+ describe '#reverse!' do
933
+ before :all do
934
+ @query = @articles.query
935
+
936
+ @new = @articles.create(:title => 'Sample Article')
937
+
938
+ @return = @articles.reverse!
939
+ end
940
+
941
+ it 'should return a Collection' do
942
+ @return.should be_kind_of(DataMapper::Collection)
943
+ end
944
+
945
+ it 'should return self' do
946
+ @return.should equal(@articles)
947
+ end
948
+
949
+ it 'should return a Collection with reversed entries' do
950
+ @return.should == [ @new, @article ]
951
+ end
952
+
953
+ it 'should return a Query that equal to the original' do
954
+ @return.query.should equal(@query)
955
+ end
956
+ end
957
+
958
+ [ :save, :save! ].each do |method|
959
+ it { should respond_to(method) }
960
+
961
+ describe "##{method}" do
962
+ describe 'when Resources are not saved' do
963
+ before :all do
964
+ @articles.new(:title => 'New Article', :content => 'New Article')
965
+
966
+ @return = @articles.send(method)
967
+ end
968
+
969
+ it 'should return true' do
970
+ @return.should be(true)
971
+ end
972
+
973
+ it 'should save each Resource' do
974
+ @articles.each { |resource| resource.should be_saved }
975
+ end
976
+ end
977
+
978
+ describe 'when Resources have been orphaned' do
979
+ before :all do
980
+ @resources = @articles.entries
981
+ @articles.replace([])
982
+
983
+ @return = @articles.send(method)
984
+ end
985
+
986
+ it 'should return true' do
987
+ @return.should be(true)
988
+ end
989
+ end
990
+ end
991
+ end
992
+
993
+ it { should respond_to(:shift) }
994
+
995
+ describe '#shift' do
996
+ describe 'with no arguments' do
997
+ before :all do
998
+ @return = @articles.shift
999
+ end
1000
+
1001
+ it 'should return a Resource' do
1002
+ @return.should be_kind_of(DataMapper::Resource)
1003
+ end
1004
+
1005
+ it 'should be the first Resource in the Collection' do
1006
+ @return.key.should == @article.key
1007
+ end
1008
+
1009
+ it 'should remove the Resource from the Collection' do
1010
+ @articles.should_not be_include(@return)
1011
+ end
1012
+ end
1013
+
1014
+ if RUBY_VERSION >= '1.8.7'
1015
+ describe 'with a limit specified' do
1016
+ before :all do
1017
+ @return = @articles.shift(1)
1018
+ end
1019
+
1020
+ it 'should return an Array' do
1021
+ @return.should be_kind_of(Array)
1022
+ end
1023
+
1024
+ it 'should return the expected Resources' do
1025
+ @return.size.should == 1
1026
+ @return.first.key.should == @article.key
1027
+ end
1028
+
1029
+ it 'should remove the Resource from the Collection' do
1030
+ @articles.should_not be_include(@article)
1031
+ end
1032
+ end
1033
+ end
1034
+ end
1035
+
1036
+ it { should respond_to(:slice!) }
1037
+
1038
+ describe '#slice!' do
1039
+ before :all do
1040
+ 1.upto(10) { |number| @articles.create(:content => "Article #{number}") }
1041
+
1042
+ @copy = @articles.dup
1043
+ end
1044
+
1045
+ describe 'with a positive offset' do
1046
+ before :all do
1047
+ unless @skip
1048
+ @return = @resource = @articles.slice!(0)
1049
+ end
1050
+ end
1051
+
1052
+ it 'should return a Resource' do
1053
+ @return.should be_kind_of(DataMapper::Resource)
1054
+ end
1055
+
1056
+ it 'should return expected Resource' do
1057
+ @return.key.should == @article.key
1058
+ end
1059
+
1060
+ it 'should return the same as Array#slice!' do
1061
+ @return.should == @copy.entries.slice!(0)
1062
+ end
1063
+
1064
+ it 'should remove the Resource from the Collection' do
1065
+ @articles.should_not be_include(@resource)
1066
+ end
1067
+ end
1068
+
1069
+ describe 'with a positive offset and length' do
1070
+ before :all do
1071
+ unless @skip
1072
+ @return = @resources = @articles.slice!(5, 5)
1073
+ end
1074
+ end
1075
+
1076
+ it 'should return a Collection' do
1077
+ @return.should be_kind_of(DataMapper::Collection)
1078
+ end
1079
+
1080
+ it 'should return the expected Resource' do
1081
+ @return.should == @copy.entries.slice!(5, 5)
1082
+ end
1083
+
1084
+ it 'should remove the Resources from the Collection' do
1085
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1086
+ end
1087
+
1088
+ it 'should scope the Collection' do
1089
+ @resources.reload.should == @copy.entries.slice!(5, 5)
1090
+ end
1091
+ end
1092
+
1093
+ describe 'with a positive range' do
1094
+ before :all do
1095
+ unless @skip
1096
+ @return = @resources = @articles.slice!(5..10)
1097
+ end
1098
+ end
1099
+
1100
+ it 'should return a Collection' do
1101
+ @return.should be_kind_of(DataMapper::Collection)
1102
+ end
1103
+
1104
+ it 'should return the expected Resources' do
1105
+ @return.should == @copy.entries.slice!(5..10)
1106
+ end
1107
+
1108
+ it 'should remove the Resources from the Collection' do
1109
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1110
+ end
1111
+
1112
+ it 'should scope the Collection' do
1113
+ @resources.reload.should == @copy.entries.slice!(5..10)
1114
+ end
1115
+ end
1116
+
1117
+ describe 'with a negative offset' do
1118
+ before :all do
1119
+ unless @skip
1120
+ @return = @resource = @articles.slice!(-1)
1121
+ end
1122
+ end
1123
+
1124
+ it 'should return a Resource' do
1125
+ @return.should be_kind_of(DataMapper::Resource)
1126
+ end
1127
+
1128
+ it 'should return expected Resource' do
1129
+ @return.should == @copy.entries.slice!(-1)
1130
+ end
1131
+
1132
+ it 'should remove the Resource from the Collection' do
1133
+ @articles.should_not be_include(@resource)
1134
+ end
1135
+ end
1136
+
1137
+ describe 'with a negative offset and length' do
1138
+ before :all do
1139
+ unless @skip
1140
+ @return = @resources = @articles.slice!(-5, 5)
1141
+ end
1142
+ end
1143
+
1144
+ it 'should return a Collection' do
1145
+ @return.should be_kind_of(DataMapper::Collection)
1146
+ end
1147
+
1148
+ it 'should return the expected Resources' do
1149
+ @return.should == @copy.entries.slice!(-5, 5)
1150
+ end
1151
+
1152
+ it 'should remove the Resources from the Collection' do
1153
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1154
+ end
1155
+
1156
+ it 'should scope the Collection' do
1157
+ @resources.reload.should == @copy.entries.slice!(-5, 5)
1158
+ end
1159
+ end
1160
+
1161
+ describe 'with a negative range' do
1162
+ before :all do
1163
+ unless @skip
1164
+ @return = @resources = @articles.slice!(-3..-2)
1165
+ end
1166
+ end
1167
+
1168
+ it 'should return a Collection' do
1169
+ @return.should be_kind_of(DataMapper::Collection)
1170
+ end
1171
+
1172
+ it 'should return the expected Resources' do
1173
+ @return.should == @copy.entries.slice!(-3..-2)
1174
+ end
1175
+
1176
+ it 'should remove the Resources from the Collection' do
1177
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1178
+ end
1179
+
1180
+ it 'should scope the Collection' do
1181
+ @resources.reload.should == @copy.entries.slice!(-3..-2)
1182
+ end
1183
+ end
1184
+
1185
+ describe 'with an offset not within the Collection' do
1186
+ before :all do
1187
+ unless @skip
1188
+ @return = @articles.slice!(12)
1189
+ end
1190
+ end
1191
+
1192
+ it 'should return nil' do
1193
+ @return.should be_nil
1194
+ end
1195
+ end
1196
+
1197
+ describe 'with an offset and length not within the Collection' do
1198
+ before :all do
1199
+ unless @skip
1200
+ @return = @articles.slice!(12, 1)
1201
+ end
1202
+ end
1203
+
1204
+ it 'should return nil' do
1205
+ @return.should be_nil
1206
+ end
1207
+ end
1208
+
1209
+ describe 'with a range not within the Collection' do
1210
+ before :all do
1211
+ unless @skip
1212
+ @return = @articles.slice!(12..13)
1213
+ end
1214
+ end
1215
+
1216
+ it 'should return nil' do
1217
+ @return.should be_nil
1218
+ end
1219
+ end
1220
+ end
1221
+
1222
+ it { should respond_to(:sort!) }
1223
+
1224
+ describe '#sort!' do
1225
+ describe 'without a block' do
1226
+ before :all do
1227
+ @return = @articles.unshift(@other).sort!
1228
+ end
1229
+
1230
+ it 'should return a Collection' do
1231
+ @return.should be_kind_of(DataMapper::Collection)
1232
+ end
1233
+
1234
+ it 'should return self' do
1235
+ @return.should equal(@articles)
1236
+ end
1237
+
1238
+ it 'should modify and sort the Collection using default sort order' do
1239
+ @articles.should == [ @article, @other ]
1240
+ end
1241
+ end
1242
+
1243
+ describe 'with a block' do
1244
+ before :all do
1245
+ @return = @articles.unshift(@other).sort! { |a_resource, b_resource| b_resource.id <=> a_resource.id }
1246
+ end
1247
+
1248
+ it 'should return a Collection' do
1249
+ @return.should be_kind_of(DataMapper::Collection)
1250
+ end
1251
+
1252
+ it 'should return self' do
1253
+ @return.should equal(@articles)
1254
+ end
1255
+
1256
+ it 'should modify and sort the Collection using supplied block' do
1257
+ @articles.should == [ @other, @article ]
1258
+ end
1259
+ end
1260
+ end
1261
+
1262
+ [ :splice, :[]= ].each do |method|
1263
+ it { should respond_to(method) }
1264
+
1265
+ describe "##{method}" do
1266
+ before :all do
1267
+ unless @skip
1268
+ orphans = (1..10).map do |number|
1269
+ articles = @articles.dup
1270
+ articles.create(:content => "Article #{number}")
1271
+ articles.pop # remove the article from the tail
1272
+ end
1273
+
1274
+ @articles.unshift(*orphans.first(5))
1275
+ @articles.concat(orphans.last(5))
1276
+
1277
+ unless loaded
1278
+ @articles.should_not be_loaded
1279
+ end
1280
+
1281
+ @copy = @articles.dup
1282
+ @new = @article_model.new(:content => 'New Article')
1283
+ end
1284
+ end
1285
+
1286
+ describe 'with a positive offset and a Resource' do
1287
+ before :all do
1288
+ rescue_if @skip do
1289
+ @original = @copy[1]
1290
+
1291
+ @return = @resource = @articles.send(method, 1, @new)
1292
+ end
1293
+ end
1294
+
1295
+ should_not_be_a_kicker
1296
+
1297
+ it 'should return a Resource' do
1298
+ @return.should be_kind_of(DataMapper::Resource)
1299
+ end
1300
+
1301
+ it 'should return expected Resource' do
1302
+ @return.should equal(@new)
1303
+ end
1304
+
1305
+ it 'should return the same as Array#[]=' do
1306
+ @return.should == @copy.entries[1] = @new
1307
+ end
1308
+
1309
+ it 'should include the Resource in the Collection' do
1310
+ @articles.should be_include(@resource)
1311
+ end
1312
+ end
1313
+
1314
+ describe 'with a positive offset and length and a Resource' do
1315
+ before :all do
1316
+ rescue_if @skip do
1317
+ @original = @copy[2]
1318
+
1319
+ @return = @resource = @articles.send(method, 2, 1, @new)
1320
+ end
1321
+ end
1322
+
1323
+ should_not_be_a_kicker
1324
+
1325
+ it 'should return a Resource' do
1326
+ @return.should be_kind_of(DataMapper::Resource)
1327
+ end
1328
+
1329
+ it 'should return the expected Resource' do
1330
+ @return.should equal(@new)
1331
+ end
1332
+
1333
+ it 'should return the same as Array#[]=' do
1334
+ @return.should == @copy.entries[2, 1] = @new
1335
+ end
1336
+
1337
+ it 'should include the Resource in the Collection' do
1338
+ @articles.should be_include(@resource)
1339
+ end
1340
+ end
1341
+
1342
+ describe 'with a positive range and a Resource' do
1343
+ before :all do
1344
+ rescue_if @skip do
1345
+ @originals = @copy.values_at(2..3)
1346
+
1347
+ @return = @resource = @articles.send(method, 2..3, @new)
1348
+ end
1349
+ end
1350
+
1351
+ should_not_be_a_kicker
1352
+
1353
+ it 'should return a Resource' do
1354
+ @return.should be_kind_of(DataMapper::Resource)
1355
+ end
1356
+
1357
+ it 'should return the expected Resources' do
1358
+ @return.should equal(@new)
1359
+ end
1360
+
1361
+ it 'should return the same as Array#[]=' do
1362
+ @return.should == @copy.entries[2..3] = @new
1363
+ end
1364
+
1365
+ it 'should include the Resource in the Collection' do
1366
+ @articles.should be_include(@resource)
1367
+ end
1368
+ end
1369
+
1370
+ describe 'with a negative offset and a Resource' do
1371
+ before :all do
1372
+ rescue_if @skip do
1373
+ @original = @copy[-1]
1374
+
1375
+ @return = @resource = @articles.send(method, -1, @new)
1376
+ end
1377
+ end
1378
+
1379
+ should_not_be_a_kicker
1380
+
1381
+ it 'should return a Resource' do
1382
+ @return.should be_kind_of(DataMapper::Resource)
1383
+ end
1384
+
1385
+ it 'should return expected Resource' do
1386
+ @return.should equal(@new)
1387
+ end
1388
+
1389
+ it 'should return the same as Array#[]=' do
1390
+ @return.should == @copy.entries[-1] = @new
1391
+ end
1392
+
1393
+ it 'should include the Resource in the Collection' do
1394
+ @articles.should be_include(@resource)
1395
+ end
1396
+ end
1397
+
1398
+ describe 'with a negative offset and length and a Resource' do
1399
+ before :all do
1400
+ rescue_if @skip do
1401
+ @original = @copy[-2]
1402
+
1403
+ @return = @resource = @articles.send(method, -2, 1, @new)
1404
+ end
1405
+ end
1406
+
1407
+ should_not_be_a_kicker
1408
+
1409
+ it 'should return a Resource' do
1410
+ @return.should be_kind_of(DataMapper::Resource)
1411
+ end
1412
+
1413
+ it 'should return the expected Resource' do
1414
+ @return.should equal(@new)
1415
+ end
1416
+
1417
+ it 'should return the same as Array#[]=' do
1418
+ @return.should == @copy.entries[-2, 1] = @new
1419
+ end
1420
+
1421
+ it 'should include the Resource in the Collection' do
1422
+ @articles.should be_include(@resource)
1423
+ end
1424
+ end
1425
+
1426
+ describe 'with a negative range and a Resource' do
1427
+ before :all do
1428
+ rescue_if @skip do
1429
+ @originals = @articles.values_at(-3..-2)
1430
+
1431
+ @return = @resource = @articles.send(method, -3..-2, @new)
1432
+ end
1433
+ end
1434
+
1435
+ should_not_be_a_kicker
1436
+
1437
+ it 'should return a Resource' do
1438
+ @return.should be_kind_of(DataMapper::Resource)
1439
+ end
1440
+
1441
+ it 'should return the expected Resources' do
1442
+ @return.should equal(@new)
1443
+ end
1444
+
1445
+ it 'should return the same as Array#[]=' do
1446
+ @return.should == @copy.entries[-3..-2] = @new
1447
+ end
1448
+
1449
+ it 'should include the Resource in the Collection' do
1450
+ @articles.should be_include(@resource)
1451
+ end
1452
+ end
1453
+ end
1454
+ end
1455
+
1456
+ describe '#[]=' do
1457
+ describe 'when swapping resources' do
1458
+ before :all do
1459
+ rescue_if @skip do
1460
+ @articles.create(:content => 'Another Article')
1461
+
1462
+ @entries = @articles.entries
1463
+
1464
+ @articles[0], @articles[1] = @articles[1], @articles[0]
1465
+ end
1466
+ end
1467
+
1468
+ it 'should include the Resource in the Collection' do
1469
+ @articles.should == @entries.reverse
1470
+ end
1471
+ end
1472
+ end
1473
+
1474
+ [ :union, :|, :+ ].each do |method|
1475
+ it { should respond_to(method) }
1476
+
1477
+ describe "##{method}" do
1478
+ subject { @articles.send(method, @other_articles) }
1479
+
1480
+ describe 'with a Collection' do
1481
+ it { should be_kind_of(DataMapper::Collection) }
1482
+
1483
+ it { should == [ @article, @other ] }
1484
+
1485
+ it { subject.query.should == @articles.query.union(@other_articles.query) }
1486
+
1487
+ it { should == @articles.to_a | @other_articles.to_a }
1488
+ end
1489
+
1490
+ describe 'with an Array' do
1491
+ before { @other_articles = @other_articles.to_ary }
1492
+
1493
+ it { should be_kind_of(DataMapper::Collection) }
1494
+
1495
+ it { should == [ @article, @other ] }
1496
+
1497
+ it { should == @articles.to_a | @other_articles.to_a }
1498
+ end
1499
+
1500
+ describe 'with a Set' do
1501
+ before { @other_articles = @other_articles.to_set }
1502
+
1503
+ it { should be_kind_of(DataMapper::Collection) }
1504
+
1505
+ it { should == [ @article, @other ] }
1506
+
1507
+ it { should == @articles.to_a | @other_articles.to_a }
1508
+ end
1509
+ end
1510
+ end
1511
+
1512
+ it { should respond_to(:unshift) }
1513
+
1514
+ describe '#unshift' do
1515
+ before :all do
1516
+ @resources = [ @article_model.new(:title => 'Title 1'), @article_model.new(:title => 'Title 2') ]
1517
+
1518
+ @return = @articles.unshift(*@resources)
1519
+ end
1520
+
1521
+ it 'should return a Collection' do
1522
+ @return.should be_kind_of(DataMapper::Collection)
1523
+ end
1524
+
1525
+ it 'should return self' do
1526
+ @return.should equal(@articles)
1527
+ end
1528
+
1529
+ it 'should prepend the Resources to the Collection' do
1530
+ @articles.should == @resources + [ @article ]
1531
+ end
1532
+ end
1533
+
1534
+ [ :update, :update! ].each do |method|
1535
+ it { should respond_to(method) }
1536
+
1537
+ describe "##{method}" do
1538
+ describe 'with attributes' do
1539
+ before :all do
1540
+ @attributes = { :title => 'Updated Title' }
1541
+
1542
+ @return = @articles.send(method, @attributes)
1543
+ end
1544
+
1545
+ if method == :update!
1546
+ should_not_be_a_kicker
1547
+ end
1548
+
1549
+ it 'should return true' do
1550
+ @return.should be(true)
1551
+ end
1552
+
1553
+ it 'should update attributes of all Resources' do
1554
+ @articles.each { |resource| @attributes.each { |key, value| resource.__send__(key).should == value } }
1555
+ end
1556
+
1557
+ it 'should persist the changes' do
1558
+ resource = @article_model.get(*@article.key)
1559
+ @attributes.each { |key, value| resource.__send__(key).should == value }
1560
+ end
1561
+ end
1562
+
1563
+ describe 'with attributes where one is a parent association' do
1564
+ before :all do
1565
+ @attributes = { :original => @other }
1566
+
1567
+ @return = @articles.send(method, @attributes)
1568
+ end
1569
+
1570
+ if method == :update!
1571
+ should_not_be_a_kicker
1572
+ end
1573
+
1574
+ it 'should return true' do
1575
+ @return.should be(true)
1576
+ end
1577
+
1578
+ it 'should update attributes of all Resources' do
1579
+ @articles.each { |resource| @attributes.each { |key, value| resource.__send__(key).should == value } }
1580
+ end
1581
+
1582
+ it 'should persist the changes' do
1583
+ resource = @article_model.get(*@article.key)
1584
+ @attributes.each { |key, value| resource.__send__(key).should == value }
1585
+ end
1586
+ end
1587
+
1588
+ describe 'with attributes where a required property is nil' do
1589
+ before :all do
1590
+ expect { @articles.send(method, :title => nil) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
1591
+ error.property.should == @articles.model.title
1592
+ end)
1593
+ end
1594
+
1595
+ if method == :update!
1596
+ should_not_be_a_kicker
1597
+ end
1598
+ end
1599
+
1600
+ describe 'on a limited collection' do
1601
+ before :all do
1602
+ @other = @articles.create
1603
+ @limited = @articles.all(:limit => 1)
1604
+ @attributes = { :content => 'Updated Content' }
1605
+
1606
+ @return = @limited.send(method, @attributes)
1607
+ end
1608
+
1609
+ if method == :update!
1610
+ should_not_be_a_kicker(:@limited)
1611
+ end
1612
+
1613
+ it 'should return true' do
1614
+ @return.should be(true)
1615
+ end
1616
+
1617
+ it 'should bypass validation' do
1618
+ pending 'TODO: not sure how to best spec this'
1619
+ end
1620
+
1621
+ it 'should update attributes of all Resources' do
1622
+ @limited.each { |resource| @attributes.each { |key, value| resource.__send__(key).should == value } }
1623
+ end
1624
+
1625
+ it 'should persist the changes' do
1626
+ resource = @article_model.get(*@article.key)
1627
+ @attributes.each { |key, value| resource.__send__(key).should == value }
1628
+ end
1629
+
1630
+ it 'should not update the other Resource' do
1631
+ @other.reload
1632
+ @attributes.each { |key, value| @other.__send__(key).should_not == value }
1633
+ end
1634
+ end
1635
+
1636
+ describe 'on a dirty collection' do
1637
+ before :all do
1638
+ @articles.each { |r| r.content = 'Changed' }
1639
+ end
1640
+
1641
+ it 'should raise an exception' do
1642
+ lambda {
1643
+ @articles.send(method, :content => 'New Content')
1644
+ }.should raise_error(DataMapper::UpdateConflictError, "#{@articles.class}##{method} cannot be called on a dirty collection")
1645
+ end
1646
+ end
1647
+ end
1648
+ end
1649
+
1650
+ it 'should respond to a public model method with #method_missing' do
1651
+ @articles.should respond_to(:base_model)
1652
+ end
1653
+
1654
+ describe '#method_missing' do
1655
+ describe 'with a public model method' do
1656
+ before :all do
1657
+ @return = @articles.model.base_model
1658
+ end
1659
+
1660
+ should_not_be_a_kicker
1661
+
1662
+ it 'should return expected object' do
1663
+ @return.should == @article_model
1664
+ end
1665
+ end
1666
+ end
1667
+ end