sbf-dm-core 1.3.0.beta

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