mongoid_orderable 5.0.0 → 6.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +83 -22
  3. data/LICENSE.txt +20 -0
  4. data/README.md +256 -149
  5. data/Rakefile +24 -6
  6. data/lib/config/locales/en.yml +12 -9
  7. data/lib/mongoid/orderable.rb +29 -22
  8. data/lib/mongoid/orderable/configs/field_config.rb +79 -0
  9. data/lib/mongoid/orderable/configs/global_config.rb +26 -0
  10. data/lib/mongoid/orderable/errors/invalid_target_position.rb +19 -18
  11. data/lib/mongoid/orderable/errors/transaction_failed.rb +20 -0
  12. data/lib/mongoid/orderable/generators/base.rb +21 -0
  13. data/lib/mongoid/orderable/generators/helpers.rb +29 -0
  14. data/lib/mongoid/orderable/generators/listable.rb +41 -0
  15. data/lib/mongoid/orderable/generators/lock_collection.rb +37 -0
  16. data/lib/mongoid/orderable/generators/movable.rb +62 -0
  17. data/lib/mongoid/orderable/generators/position.rb +26 -0
  18. data/lib/mongoid/orderable/generators/scope.rb +26 -0
  19. data/lib/mongoid/orderable/handlers/base.rb +167 -0
  20. data/lib/mongoid/orderable/handlers/document.rb +24 -0
  21. data/lib/mongoid/orderable/handlers/document_embedded.rb +14 -0
  22. data/lib/mongoid/orderable/handlers/document_transactional.rb +35 -0
  23. data/lib/mongoid/orderable/handlers/transaction.rb +71 -0
  24. data/lib/mongoid/orderable/installer.rb +63 -0
  25. data/lib/mongoid/orderable/mixins/callbacks.rb +43 -0
  26. data/lib/mongoid/orderable/mixins/helpers.rb +39 -0
  27. data/lib/mongoid/orderable/mixins/listable.rb +49 -0
  28. data/lib/mongoid/orderable/mixins/movable.rb +60 -0
  29. data/lib/mongoid/orderable/version.rb +7 -0
  30. data/lib/mongoid_orderable.rb +33 -54
  31. data/spec/integration/concurrency_spec.rb +232 -0
  32. data/spec/integration/customized_spec.rb +31 -0
  33. data/spec/integration/embedded_spec.rb +41 -0
  34. data/spec/integration/foreign_key_spec.rb +33 -0
  35. data/spec/integration/inherited_spec.rb +54 -0
  36. data/spec/integration/multiple_fields_spec.rb +554 -0
  37. data/spec/integration/multiple_scoped_spec.rb +63 -0
  38. data/spec/integration/no_indexed_spec.rb +23 -0
  39. data/spec/integration/scoped_spec.rb +151 -0
  40. data/spec/integration/simple_spec.rb +184 -0
  41. data/spec/integration/string_scoped_spec.rb +28 -0
  42. data/spec/integration/zero_based_spec.rb +161 -0
  43. data/spec/spec_helper.rb +42 -30
  44. data/spec/support/models.rb +122 -0
  45. metadata +75 -41
  46. data/.gitignore +0 -4
  47. data/.rspec +0 -2
  48. data/.rvmrc +0 -1
  49. data/.travis.yml +0 -14
  50. data/Gemfile +0 -18
  51. data/lib/mongoid/orderable/callbacks.rb +0 -75
  52. data/lib/mongoid/orderable/configuration.rb +0 -60
  53. data/lib/mongoid/orderable/errors.rb +0 -2
  54. data/lib/mongoid/orderable/errors/mongoid_orderable_error.rb +0 -14
  55. data/lib/mongoid/orderable/generator.rb +0 -34
  56. data/lib/mongoid/orderable/generator/helpers.rb +0 -29
  57. data/lib/mongoid/orderable/generator/listable.rb +0 -41
  58. data/lib/mongoid/orderable/generator/movable.rb +0 -62
  59. data/lib/mongoid/orderable/generator/position.rb +0 -26
  60. data/lib/mongoid/orderable/generator/scope.rb +0 -17
  61. data/lib/mongoid/orderable/helpers.rb +0 -50
  62. data/lib/mongoid/orderable/listable.rb +0 -49
  63. data/lib/mongoid/orderable/movable.rb +0 -58
  64. data/lib/mongoid/orderable/orderable_class.rb +0 -51
  65. data/lib/mongoid_orderable/mongoid/contexts/enumerable.rb +0 -15
  66. data/lib/mongoid_orderable/mongoid/contexts/mongo.rb +0 -18
  67. data/lib/mongoid_orderable/mongoid/contextual/memory.rb +0 -15
  68. data/lib/mongoid_orderable/mongoid/criteria.rb +0 -4
  69. data/lib/mongoid_orderable/version.rb +0 -3
  70. data/mongoid_orderable.gemspec +0 -26
  71. data/spec/mongoid/orderable_spec.rb +0 -1413
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe CustomizedOrderable do
4
+
5
+ shared_examples_for 'customized_orderable' do
6
+
7
+ it 'does not have default position field' do
8
+ expect(CustomizedOrderable.fields).not_to have_key('position')
9
+ end
10
+
11
+ it 'should have custom pos field' do
12
+ expect(CustomizedOrderable.fields).to have_key('pos')
13
+ end
14
+
15
+ it 'should have an alias my_position which points to pos field on Mongoid 3+' do
16
+ expect(CustomizedOrderable.database_field_name('my_position')).to eq('pos')
17
+ end
18
+ end
19
+
20
+ context 'with transactions' do
21
+ enable_transactions!
22
+
23
+ it_behaves_like 'customized_orderable'
24
+ end
25
+
26
+ context 'without transactions' do
27
+ disable_transactions!
28
+
29
+ it_behaves_like 'customized_orderable'
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe EmbeddedOrderable do
4
+
5
+ shared_examples_for 'embedded_orderable' do
6
+
7
+ before :each do
8
+ eo = EmbedsOrderable.create!
9
+ 2.times { eo.embedded_orderables.create! }
10
+ eo = EmbedsOrderable.create!
11
+ 3.times { eo.embedded_orderables.create! }
12
+ end
13
+
14
+ def positions
15
+ EmbedsOrderable.order_by(position: 1).all.map { |eo| eo.embedded_orderables.map(&:position).sort }
16
+ end
17
+
18
+ it 'sets proper position while creation' do
19
+ expect(positions).to eq([[1, 2], [1, 2, 3]])
20
+ end
21
+
22
+ it 'moves an item returned by a query to position' do
23
+ embedded_orderable1 = EmbedsOrderable.first.embedded_orderables.where(position: 1).first
24
+ embedded_orderable2 = EmbedsOrderable.first.embedded_orderables.where(position: 2).first
25
+ embedded_orderable1.move_to! 2
26
+ expect(embedded_orderable2.reload.position).to eq(1)
27
+ end
28
+ end
29
+
30
+ context 'with transactions' do
31
+ enable_transactions!
32
+
33
+ it_behaves_like 'embedded_orderable'
34
+ end
35
+
36
+ context 'without transactions' do
37
+ disable_transactions!
38
+
39
+ it_behaves_like 'embedded_orderable'
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe ForeignKeyDiffersOrderable do
4
+
5
+ shared_examples_for 'foreign_key_orderable' do
6
+
7
+ it 'uses the foreign key of the relationship as scope' do
8
+ orderable1, orderable2, orderable3 = nil
9
+ parent_scope1 = ForeignKeyDiffersOrderable.create
10
+ parent_scope2 = ForeignKeyDiffersOrderable.create
11
+ expect do
12
+ orderable1 = ForeignKeyDiffersOrderable.create!(different_scope: parent_scope1)
13
+ orderable2 = ForeignKeyDiffersOrderable.create!(different_scope: parent_scope1)
14
+ orderable3 = ForeignKeyDiffersOrderable.create!(different_scope: parent_scope2)
15
+ end.to_not raise_error
16
+ expect(orderable1.position).to eq 1
17
+ expect(orderable2.position).to eq 2
18
+ expect(orderable3.position).to eq 1
19
+ end
20
+ end
21
+
22
+ context 'with transactions' do
23
+ enable_transactions!
24
+
25
+ it_behaves_like 'foreign_key_orderable'
26
+ end
27
+
28
+ context 'without transactions' do
29
+ disable_transactions!
30
+
31
+ it_behaves_like 'foreign_key_orderable'
32
+ end
33
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe InheritedOrderable do
4
+
5
+ shared_examples_for 'inherited_orderable' do
6
+
7
+ it 'should set proper position' do
8
+ fruit1 = Apple.create
9
+ fruit2 = Orange.create
10
+ expect(fruit1.position).to eq(1)
11
+ expect(fruit2.position).to eq(2)
12
+ end
13
+
14
+ describe 'movement' do
15
+ before :each do
16
+ 5.times { Apple.create! }
17
+ end
18
+
19
+ it 'with symbol position' do
20
+ first_apple = Apple.asc(:_id).first
21
+ top_pos = first_apple.position
22
+ bottom_pos = Apple.asc(:_id).last.position
23
+ expect { first_apple.move_to!(:bottom) }.to change(first_apple, :position).from(top_pos).to bottom_pos
24
+ end
25
+
26
+ it 'with point position' do
27
+ first_apple = Apple.asc(:_id).first
28
+ top_pos = first_apple.position
29
+ bottom_pos = Apple.asc(:_id).last.position
30
+ expect { first_apple.move_to!(bottom_pos) }.to change(first_apple, :position).from(top_pos).to bottom_pos
31
+ end
32
+ end
33
+
34
+ describe 'add orderable configs in inherited class' do
35
+ it 'does not affect the orderable configs of parent class and sibling class' do
36
+ expect(InheritedOrderable.orderable_configs).not_to eq Apple.orderable_configs
37
+ expect(Orange.orderable_configs).not_to eq Apple.orderable_configs
38
+ expect(InheritedOrderable.orderable_configs).to eq Orange.orderable_configs
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'with transactions' do
44
+ enable_transactions!
45
+
46
+ it_behaves_like 'inherited_orderable'
47
+ end
48
+
49
+ context 'without transactions' do
50
+ disable_transactions!
51
+
52
+ it_behaves_like 'inherited_orderable'
53
+ end
54
+ end
@@ -0,0 +1,554 @@
1
+ require 'spec_helper'
2
+
3
+ describe MultipleFieldsOrderable do
4
+
5
+ shared_examples_for 'multiple_fields_orderable' do
6
+
7
+ before :each do
8
+ 5.times { MultipleFieldsOrderable.create! }
9
+ end
10
+
11
+ context 'default orderable' do
12
+ let(:serial_nos) { MultipleFieldsOrderable.pluck(:serial_no).sort }
13
+
14
+ describe 'inserting' do
15
+ let(:newbie) { MultipleFieldsOrderable.create! }
16
+
17
+ before { @position = newbie.position }
18
+
19
+ it 'top' do
20
+ newbie.move_to! :top
21
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
22
+ expect(newbie.serial_no).to eq(1)
23
+ expect(newbie.position).to eq(@position)
24
+ end
25
+
26
+ it 'bottom' do
27
+ newbie.move_to! :bottom
28
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
29
+ expect(newbie.serial_no).to eq(6)
30
+ expect(newbie.position).to eq(@position)
31
+ end
32
+
33
+ it 'middle' do
34
+ newbie.move_to! 4
35
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
36
+ expect(newbie.serial_no).to eq(4)
37
+ expect(newbie.position).to eq(@position)
38
+ end
39
+ end
40
+
41
+ describe 'movement' do
42
+ it 'higher from top' do
43
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
44
+ position = record.position
45
+ record.move_higher!
46
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
47
+ expect(record.serial_no).to eq(1)
48
+ expect(record.position).to eq(position)
49
+ end
50
+
51
+ it 'higher from bottom' do
52
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
53
+ position = record.position
54
+ record.move_higher!
55
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
56
+ expect(record.serial_no).to eq(4)
57
+ expect(record.position).to eq(position)
58
+ end
59
+
60
+ it 'higher from middle' do
61
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
62
+ position = record.position
63
+ record.move_higher!
64
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
65
+ expect(record.serial_no).to eq(2)
66
+ expect(record.position).to eq(position)
67
+ end
68
+
69
+ it 'lower from top' do
70
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
71
+ position = record.position
72
+ record.move_lower!
73
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
74
+ expect(record.serial_no).to eq(2)
75
+ expect(record.position).to eq(position)
76
+ end
77
+
78
+ it 'lower from bottom' do
79
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
80
+ position = record.position
81
+ record.move_lower!
82
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
83
+ expect(record.serial_no).to eq(5)
84
+ expect(record.position).to eq(position)
85
+ end
86
+
87
+ it 'lower from middle' do
88
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
89
+ position = record.position
90
+ record.move_lower!
91
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
92
+ expect(record.serial_no).to eq(4)
93
+ expect(record.position).to eq(position)
94
+ end
95
+ end
96
+
97
+ describe 'utility methods' do
98
+ before do
99
+ @record1 = MultipleFieldsOrderable.where(serial_no: 1).first
100
+ @record2 = MultipleFieldsOrderable.where(serial_no: 2).first
101
+ @record3 = MultipleFieldsOrderable.where(serial_no: 3).first
102
+ @record4 = MultipleFieldsOrderable.where(serial_no: 4).first
103
+ @record5 = MultipleFieldsOrderable.where(serial_no: 5).first
104
+ end
105
+
106
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
107
+ expect(@record1.next_item).to eq(@record2)
108
+ expect(@record3.next_item).to eq(@record4)
109
+ expect(@record5.next_item).to eq(nil)
110
+ expect(@record1.prev_item).to eq(nil)
111
+ expect(@record3.prev_item).to eq(@record2)
112
+ expect(@record5.prev_item).to eq(@record4)
113
+ end
114
+
115
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
116
+ expect(@record1.next_items.to_a).to eq([@record2, @record3, @record4, @record5])
117
+ expect(@record3.next_items.to_a).to eq([@record4, @record5])
118
+ expect(@record5.next_items.to_a).to eq([])
119
+ expect(@record1.previous_items.to_a).to eq([])
120
+ expect(@record3.previous_items.to_a).to eq([@record1, @record2])
121
+ expect(@record5.previous_items.to_a).to eq([@record1, @record2, @record3, @record4])
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'serial_no orderable' do
127
+ let(:serial_nos) { MultipleFieldsOrderable.pluck(:serial_no).sort }
128
+
129
+ it 'should have proper serial_no field' do
130
+ expect(MultipleFieldsOrderable.fields.key?('serial_no')).to be true
131
+ expect(MultipleFieldsOrderable.fields['serial_no'].options[:type]).to eq(Integer)
132
+ end
133
+
134
+ it 'should have index on serial_no field' do
135
+ expect(MultipleFieldsOrderable.index_specifications.detect { |spec| spec.key == { serial_no: 1 } }).not_to be_nil
136
+ end
137
+
138
+ it 'should have a orderable base of 1' do
139
+ expect(MultipleFieldsOrderable.first.orderable_top(:serial_no)).to eq(1)
140
+ end
141
+
142
+ it 'should set proper position while creation' do
143
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
144
+ end
145
+
146
+ describe 'removement' do
147
+ it 'top' do
148
+ MultipleFieldsOrderable.where(serial_no: 1).destroy
149
+ expect(serial_nos).to eq([1, 2, 3, 4])
150
+ end
151
+
152
+ it 'bottom' do
153
+ MultipleFieldsOrderable.where(serial_no: 5).destroy
154
+ expect(serial_nos).to eq([1, 2, 3, 4])
155
+ end
156
+
157
+ it 'middle' do
158
+ MultipleFieldsOrderable.where(serial_no: 3).destroy
159
+ expect(serial_nos).to eq([1, 2, 3, 4])
160
+ end
161
+ end
162
+
163
+ describe 'inserting' do
164
+ let(:newbie) { MultipleFieldsOrderable.create! }
165
+
166
+ before { @position = newbie.position }
167
+
168
+ it 'top' do
169
+ newbie.move_serial_no_to! :top
170
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
171
+ expect(newbie.serial_no).to eq(1)
172
+ expect(newbie.position).to eq(@position)
173
+ end
174
+
175
+ it 'bottom' do
176
+ newbie.move_serial_no_to! :bottom
177
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
178
+ expect(newbie.serial_no).to eq(6)
179
+ expect(newbie.position).to eq(@position)
180
+ end
181
+
182
+ it 'middle' do
183
+ newbie.move_serial_no_to! 4
184
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
185
+ expect(newbie.serial_no).to eq(4)
186
+ expect(newbie.position).to eq(@position)
187
+ end
188
+ end
189
+
190
+ describe 'movement' do
191
+ it 'higher from top' do
192
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
193
+ position = record.position
194
+ record.move_serial_no_higher!
195
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
196
+ expect(record.serial_no).to eq(1)
197
+ expect(record.position).to eq(position)
198
+ end
199
+
200
+ it 'higher from bottom' do
201
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
202
+ position = record.position
203
+ record.move_serial_no_higher!
204
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
205
+ expect(record.serial_no).to eq(4)
206
+ expect(record.position).to eq(position)
207
+ end
208
+
209
+ it 'higher from middle' do
210
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
211
+ position = record.position
212
+ record.move_serial_no_higher!
213
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
214
+ expect(record.serial_no).to eq(2)
215
+ expect(record.position).to eq(position)
216
+ end
217
+
218
+ it 'lower from top' do
219
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
220
+ position = record.position
221
+ record.move_serial_no_lower!
222
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
223
+ expect(record.serial_no).to eq(2)
224
+ expect(record.position).to eq(position)
225
+ end
226
+
227
+ it 'lower from bottom' do
228
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
229
+ position = record.position
230
+ record.move_serial_no_lower!
231
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
232
+ expect(record.serial_no).to eq(5)
233
+ expect(record.position).to eq(position)
234
+ end
235
+
236
+ it 'lower from middle' do
237
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
238
+ position = record.position
239
+ record.move_serial_no_lower!
240
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
241
+ expect(record.serial_no).to eq(4)
242
+ expect(record.position).to eq(position)
243
+ end
244
+ end
245
+
246
+ describe 'utility methods' do
247
+ before do
248
+ @record1 = MultipleFieldsOrderable.where(serial_no: 1).first
249
+ @record2 = MultipleFieldsOrderable.where(serial_no: 2).first
250
+ @record3 = MultipleFieldsOrderable.where(serial_no: 3).first
251
+ @record4 = MultipleFieldsOrderable.where(serial_no: 4).first
252
+ @record5 = MultipleFieldsOrderable.where(serial_no: 5).first
253
+ end
254
+
255
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
256
+ expect(@record1.next_serial_no_item).to eq(@record2)
257
+ expect(@record3.next_serial_no_item).to eq(@record4)
258
+ expect(@record5.next_serial_no_item).to eq(nil)
259
+ expect(@record1.prev_serial_no_item).to eq(nil)
260
+ expect(@record3.prev_serial_no_item).to eq(@record2)
261
+ expect(@record5.prev_serial_no_item).to eq(@record4)
262
+ end
263
+
264
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
265
+ expect(@record1.next_serial_no_items.to_a).to eq([@record2, @record3, @record4, @record5])
266
+ expect(@record3.next_serial_no_items.to_a).to eq([@record4, @record5])
267
+ expect(@record5.next_serial_no_items.to_a).to eq([])
268
+ expect(@record1.previous_serial_no_items.to_a).to eq([])
269
+ expect(@record3.previous_serial_no_items.to_a).to eq([@record1, @record2])
270
+ expect(@record5.previous_serial_no_items.to_a).to eq([@record1, @record2, @record3, @record4])
271
+ end
272
+ end
273
+ end
274
+
275
+ context 'position orderable' do
276
+ let(:positions) { MultipleFieldsOrderable.pluck(:position).sort }
277
+
278
+ it 'should not have default position field' do
279
+ expect(MultipleFieldsOrderable.fields).not_to have_key('position')
280
+ end
281
+
282
+ it 'should have custom pos field' do
283
+ expect(MultipleFieldsOrderable.fields).to have_key('pos')
284
+ expect(MultipleFieldsOrderable.fields['pos'].options[:type]).to eq(Integer)
285
+ end
286
+
287
+ it 'should have index on position field' do
288
+ expect(MultipleFieldsOrderable.index_specifications.detect { |spec| spec.key == { position: 1 } }).to be_nil
289
+ end
290
+
291
+ it 'should have a orderable base of 0' do
292
+ expect(MultipleFieldsOrderable.first.orderable_top(:position)).to eq(0)
293
+ end
294
+
295
+ it 'should set proper position while creation' do
296
+ expect(positions).to eq([0, 1, 2, 3, 4])
297
+ end
298
+
299
+ describe 'removement' do
300
+ it 'top' do
301
+ MultipleFieldsOrderable.where(pos: 1).destroy
302
+ expect(positions).to eq([0, 1, 2, 3])
303
+ end
304
+
305
+ it 'bottom' do
306
+ MultipleFieldsOrderable.where(pos: 4).destroy
307
+ expect(positions).to eq([0, 1, 2, 3])
308
+ end
309
+
310
+ it 'middle' do
311
+ MultipleFieldsOrderable.where(pos: 3).destroy
312
+ expect(positions).to eq([0, 1, 2, 3])
313
+ end
314
+ end
315
+
316
+ describe 'inserting' do
317
+ let(:newbie) { MultipleFieldsOrderable.create! }
318
+
319
+ before { @serial_no = newbie.serial_no }
320
+
321
+ it 'top' do
322
+ newbie.move_position_to! :top
323
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
324
+ expect(newbie.position).to eq(0)
325
+ expect(newbie.serial_no).to eq(@serial_no)
326
+ end
327
+
328
+ it 'bottom' do
329
+ newbie.move_position_to! :bottom
330
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
331
+ expect(newbie.position).to eq(5)
332
+ expect(newbie.serial_no).to eq(@serial_no)
333
+ end
334
+
335
+ it 'middle' do
336
+ newbie.move_position_to! 4
337
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
338
+ expect(newbie.position).to eq(4)
339
+ expect(newbie.serial_no).to eq(@serial_no)
340
+ end
341
+ end
342
+
343
+ describe 'movement' do
344
+ it 'higher from top' do
345
+ record = MultipleFieldsOrderable.where(pos: 0).first
346
+ position = record.serial_no
347
+ record.move_position_higher!
348
+ expect(positions).to eq([0, 1, 2, 3, 4])
349
+ expect(record.position).to eq(0)
350
+ expect(record.serial_no).to eq(position)
351
+ end
352
+
353
+ it 'higher from bottom' do
354
+ record = MultipleFieldsOrderable.where(pos: 4).first
355
+ position = record.serial_no
356
+ record.move_position_higher!
357
+ expect(positions).to eq([0, 1, 2, 3, 4])
358
+ expect(record.position).to eq(3)
359
+ expect(record.serial_no).to eq(position)
360
+ end
361
+
362
+ it 'higher from middle' do
363
+ record = MultipleFieldsOrderable.where(pos: 3).first
364
+ position = record.serial_no
365
+ record.move_position_higher!
366
+ expect(positions).to eq([0, 1, 2, 3, 4])
367
+ expect(record.position).to eq(2)
368
+ expect(record.serial_no).to eq(position)
369
+ end
370
+
371
+ it 'lower from top' do
372
+ record = MultipleFieldsOrderable.where(pos: 0).first
373
+ position = record.serial_no
374
+ record.move_position_lower!
375
+ expect(positions).to eq([0, 1, 2, 3, 4])
376
+ expect(record.position).to eq(1)
377
+ expect(record.serial_no).to eq(position)
378
+ end
379
+
380
+ it 'lower from bottom' do
381
+ record = MultipleFieldsOrderable.where(pos: 4).first
382
+ position = record.serial_no
383
+ record.move_position_lower!
384
+ expect(positions).to eq([0, 1, 2, 3, 4])
385
+ expect(record.position).to eq(4)
386
+ expect(record.serial_no).to eq(position)
387
+ end
388
+
389
+ it 'lower from middle' do
390
+ record = MultipleFieldsOrderable.where(pos: 3).first
391
+ position = record.serial_no
392
+ record.move_position_lower!
393
+ expect(positions).to eq([0, 1, 2, 3, 4])
394
+ expect(record.position).to eq(4)
395
+ expect(record.serial_no).to eq(position)
396
+ end
397
+ end
398
+
399
+ describe 'utility methods' do
400
+ before do
401
+ @record1 = MultipleFieldsOrderable.where(pos: 0).first
402
+ @record2 = MultipleFieldsOrderable.where(pos: 1).first
403
+ @record3 = MultipleFieldsOrderable.where(pos: 2).first
404
+ @record4 = MultipleFieldsOrderable.where(pos: 3).first
405
+ @record5 = MultipleFieldsOrderable.where(pos: 4).first
406
+ end
407
+
408
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
409
+ expect(@record1.next_position_item).to eq(@record2)
410
+ expect(@record3.next_position_item).to eq(@record4)
411
+ expect(@record5.next_position_item).to eq(nil)
412
+ expect(@record1.prev_position_item).to eq(nil)
413
+ expect(@record3.prev_position_item).to eq(@record2)
414
+ expect(@record5.prev_position_item).to eq(@record4)
415
+ end
416
+
417
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
418
+ expect(@record1.next_position_items.to_a).to eq([@record2, @record3, @record4, @record5])
419
+ expect(@record3.next_position_items.to_a).to eq([@record4, @record5])
420
+ expect(@record5.next_position_items.to_a).to eq([])
421
+ expect(@record1.previous_position_items.to_a).to eq([])
422
+ expect(@record3.previous_position_items.to_a).to eq([@record1, @record2])
423
+ expect(@record5.previous_position_items.to_a).to eq([@record1, @record2, @record3, @record4])
424
+ end
425
+ end
426
+ end
427
+
428
+ context 'group_count orderable' do
429
+ before :each do
430
+ MultipleFieldsOrderable.delete_all
431
+ 2.times { MultipleFieldsOrderable.create! group_id: 1 }
432
+ 3.times { MultipleFieldsOrderable.create! group_id: 2 }
433
+ end
434
+
435
+ let(:all_groups) { MultipleFieldsOrderable.order_by([:group_id, :asc], [:groups, :asc]).map(&:groups) }
436
+
437
+ it 'should set proper position while creation' do
438
+ expect(all_groups).to eq([1, 2, 1, 2, 3])
439
+ end
440
+
441
+ describe 'removement' do
442
+ it 'top' do
443
+ MultipleFieldsOrderable.where(groups: 1, group_id: 1).destroy
444
+ expect(all_groups).to eq([1, 1, 2, 3])
445
+ end
446
+
447
+ it 'bottom' do
448
+ MultipleFieldsOrderable.where(groups: 3, group_id: 2).destroy
449
+ expect(all_groups).to eq([1, 2, 1, 2])
450
+ end
451
+
452
+ it 'middle' do
453
+ MultipleFieldsOrderable.where(groups: 2, group_id: 2).destroy
454
+ expect(all_groups).to eq([1, 2, 1, 2])
455
+ end
456
+ end
457
+
458
+ describe 'inserting' do
459
+ it 'top' do
460
+ newbie = MultipleFieldsOrderable.create! group_id: 1
461
+ newbie.move_groups_to! :top
462
+ expect(all_groups).to eq([1, 2, 3, 1, 2, 3])
463
+ expect(newbie.groups).to eq(1)
464
+ end
465
+
466
+ it 'bottom' do
467
+ newbie = MultipleFieldsOrderable.create! group_id: 2
468
+ newbie.move_groups_to! :bottom
469
+ expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
470
+ expect(newbie.groups).to eq(4)
471
+ end
472
+
473
+ it 'middle' do
474
+ newbie = MultipleFieldsOrderable.create! group_id: 2
475
+ newbie.move_groups_to! 2
476
+ expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
477
+ expect(newbie.groups).to eq(2)
478
+ end
479
+ end
480
+
481
+ describe 'scope movement' do
482
+ let(:record) { MultipleFieldsOrderable.where(group_id: 2, groups: 2).first }
483
+
484
+ it 'to a new scope group' do
485
+ record.update_attributes group_id: 3
486
+ expect(all_groups).to eq([1, 2, 1, 2, 1])
487
+ expect(record.groups).to eq(1)
488
+ end
489
+
490
+ context 'when moving to an existing scope group' do
491
+ it 'without a position' do
492
+ record.update_attributes group_id: 1
493
+ expect(all_groups).to eq([1, 2, 3, 1, 2])
494
+ expect(record.reload.groups).to eq(3)
495
+ end
496
+
497
+ it 'with symbol position' do
498
+ record.update_attributes group_id: 1
499
+ record.move_groups_to! :top
500
+ expect(all_groups).to eq([1, 2, 3, 1, 2])
501
+ expect(record.reload.groups).to eq(1)
502
+ end
503
+
504
+ it 'with point position' do
505
+ record.update_attributes group_id: 1
506
+ record.move_groups_to! 2
507
+ expect(all_groups).to eq([1, 2, 3, 1, 2])
508
+ expect(record.reload.groups).to eq(2)
509
+ end
510
+ end
511
+ end
512
+
513
+ describe 'utility methods' do
514
+ before do
515
+ @record1 = MultipleFieldsOrderable.where(group_id: 2, groups: 1).first
516
+ @record2 = MultipleFieldsOrderable.where(group_id: 2, groups: 2).first
517
+ @record3 = MultipleFieldsOrderable.where(group_id: 2, groups: 3).first
518
+ @record4 = MultipleFieldsOrderable.where(group_id: 1, groups: 1).first
519
+ @record5 = MultipleFieldsOrderable.where(group_id: 1, groups: 2).first
520
+ end
521
+
522
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
523
+ expect(@record1.next_groups_item).to eq(@record2)
524
+ expect(@record4.next_groups_item).to eq(@record5)
525
+ expect(@record3.next_groups_item).to eq(nil)
526
+ expect(@record1.prev_groups_item).to eq(nil)
527
+ expect(@record3.prev_groups_item).to eq(@record2)
528
+ expect(@record5.prev_groups_item).to eq(@record4)
529
+ end
530
+
531
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
532
+ expect(@record1.next_groups_items.to_a).to eq([@record2, @record3])
533
+ expect(@record3.next_groups_items.to_a).to eq([])
534
+ expect(@record4.next_groups_items.to_a).to eq([@record5])
535
+ expect(@record1.previous_groups_items.to_a).to eq([])
536
+ expect(@record3.previous_groups_items.to_a).to eq([@record1, @record2])
537
+ expect(@record5.previous_groups_items.to_a).to eq([@record4])
538
+ end
539
+ end
540
+ end
541
+ end
542
+
543
+ context 'with transactions' do
544
+ enable_transactions!
545
+
546
+ it_behaves_like 'multiple_fields_orderable'
547
+ end
548
+
549
+ context 'without transactions' do
550
+ disable_transactions!
551
+
552
+ it_behaves_like 'multiple_fields_orderable'
553
+ end
554
+ end