sbf-dm-core 1.3.0.beta

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