mongoid_orderable 4.1.1 → 6.0.1

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 (54) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +77 -17
  3. data/LICENSE.txt +20 -0
  4. data/README.md +256 -127
  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/engine.rb +206 -0
  11. data/lib/mongoid/orderable/errors/invalid_target_position.rb +19 -18
  12. data/lib/mongoid/orderable/errors/transaction_failed.rb +20 -0
  13. data/lib/mongoid/orderable/generators/base.rb +21 -0
  14. data/lib/mongoid/orderable/generators/helpers.rb +29 -0
  15. data/lib/mongoid/orderable/generators/listable.rb +41 -0
  16. data/lib/mongoid/orderable/generators/lock_collection.rb +37 -0
  17. data/lib/mongoid/orderable/generators/movable.rb +62 -0
  18. data/lib/mongoid/orderable/generators/position.rb +26 -0
  19. data/lib/mongoid/orderable/generators/scope.rb +26 -0
  20. data/lib/mongoid/orderable/installer.rb +63 -0
  21. data/lib/mongoid/orderable/mixins/callbacks.rb +29 -0
  22. data/lib/mongoid/orderable/mixins/helpers.rb +39 -0
  23. data/lib/mongoid/orderable/mixins/listable.rb +49 -0
  24. data/lib/mongoid/orderable/mixins/movable.rb +60 -0
  25. data/lib/mongoid/orderable/version.rb +7 -0
  26. data/lib/mongoid_orderable.rb +29 -56
  27. data/spec/mongoid/orderable_spec.rb +1486 -1408
  28. data/spec/spec_helper.rb +21 -37
  29. metadata +62 -42
  30. data/.gitignore +0 -4
  31. data/.rspec +0 -1
  32. data/.rvmrc +0 -1
  33. data/.travis.yml +0 -20
  34. data/Gemfile +0 -15
  35. data/lib/mongoid/orderable/callbacks.rb +0 -75
  36. data/lib/mongoid/orderable/configuration.rb +0 -60
  37. data/lib/mongoid/orderable/errors.rb +0 -2
  38. data/lib/mongoid/orderable/errors/mongoid_orderable_error.rb +0 -14
  39. data/lib/mongoid/orderable/generator.rb +0 -34
  40. data/lib/mongoid/orderable/generator/helpers.rb +0 -29
  41. data/lib/mongoid/orderable/generator/listable.rb +0 -41
  42. data/lib/mongoid/orderable/generator/movable.rb +0 -62
  43. data/lib/mongoid/orderable/generator/position.rb +0 -26
  44. data/lib/mongoid/orderable/generator/scope.rb +0 -17
  45. data/lib/mongoid/orderable/helpers.rb +0 -50
  46. data/lib/mongoid/orderable/listable.rb +0 -49
  47. data/lib/mongoid/orderable/movable.rb +0 -58
  48. data/lib/mongoid/orderable/orderable_class.rb +0 -51
  49. data/lib/mongoid_orderable/mongoid/contexts/enumerable.rb +0 -15
  50. data/lib/mongoid_orderable/mongoid/contexts/mongo.rb +0 -18
  51. data/lib/mongoid_orderable/mongoid/contextual/memory.rb +0 -15
  52. data/lib/mongoid_orderable/mongoid/criteria.rb +0 -4
  53. data/lib/mongoid_orderable/version.rb +0 -3
  54. data/mongoid_orderable.gemspec +0 -25
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Orderable
5
+ module Mixins
6
+ module Movable
7
+ def move_to!(target_position, options = {})
8
+ move_field_to target_position, options
9
+ save
10
+ end
11
+ alias insert_at! move_to!
12
+
13
+ def move_to(target_position, options = {})
14
+ move_field_to target_position, options
15
+ end
16
+ alias insert_at move_to
17
+
18
+ def move_to=(target_position, options = {})
19
+ move_field_to target_position, options
20
+ end
21
+ alias insert_at= move_to=
22
+
23
+ %i[top bottom].each do |symbol|
24
+ class_eval <<~KLASS, __FILE__, __LINE__ + 1
25
+ def move_to_#{symbol}(options = {})
26
+ move_to :#{symbol}, options
27
+ end
28
+
29
+ def move_to_#{symbol}!(options = {})
30
+ move_to! :#{symbol}, options
31
+ end
32
+ KLASS
33
+ end
34
+
35
+ %i[higher lower].each do |symbol|
36
+ class_eval <<~KLASS, __FILE__, __LINE__ + 1
37
+ def move_#{symbol}(options = {})
38
+ move_to :#{symbol}, options
39
+ end
40
+
41
+ def move_#{symbol}!(options = {})
42
+ move_to! :#{symbol}, options
43
+ end
44
+ KLASS
45
+ end
46
+
47
+ protected
48
+
49
+ def move_all
50
+ @move_all || {}
51
+ end
52
+
53
+ def move_field_to(position, options)
54
+ field = options[:field] || default_orderable_field
55
+ @move_all = move_all.merge(field => position)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Orderable
5
+ VERSION = '6.0.1'
6
+ end
7
+ end
@@ -1,56 +1,29 @@
1
- require 'active_support'
2
- I18n.enforce_available_locales = false
3
- I18n.load_path << File.join(File.dirname(__FILE__), 'config', 'locales', 'en.yml')
4
-
5
- module MongoidOrderable
6
- def self.mongoid2?
7
- ::Mongoid.const_defined? :Contexts
8
- end
9
- def self.mongoid3?
10
- ::Mongoid.const_defined? :Observer
11
- end
12
-
13
- def self.inc instance, attribute, value
14
- if MongoidOrderable.mongoid2? || MongoidOrderable.mongoid3?
15
- instance.inc attribute, value
16
- else
17
- instance.inc(attribute => value)
18
- end
19
- end
20
-
21
- def self.metadata instance
22
- if MongoidOrderable.mongoid2? || MongoidOrderable.mongoid3?
23
- instance.metadata
24
- else
25
- instance.relation_metadata
26
- end
27
- end
28
- end
29
-
30
- require 'mongoid'
31
- require 'mongoid_orderable/version'
32
-
33
- if MongoidOrderable.mongoid2?
34
- require 'mongoid_orderable/mongoid/contexts/mongo'
35
- require 'mongoid_orderable/mongoid/contexts/enumerable'
36
- require 'mongoid_orderable/mongoid/criteria'
37
- else
38
- require 'mongoid_orderable/mongoid/contextual/memory'
39
- end
40
-
41
- require 'mongoid/orderable'
42
- require 'mongoid/orderable/errors'
43
- require 'mongoid/orderable/configuration'
44
- require 'mongoid/orderable/helpers'
45
- require 'mongoid/orderable/callbacks'
46
- require 'mongoid/orderable/listable'
47
- require 'mongoid/orderable/movable'
48
-
49
- require 'mongoid/orderable/generator/listable'
50
- require 'mongoid/orderable/generator/movable'
51
- require 'mongoid/orderable/generator/position'
52
- require 'mongoid/orderable/generator/scope'
53
- require 'mongoid/orderable/generator/helpers'
54
- require 'mongoid/orderable/generator'
55
-
56
- require 'mongoid/orderable/orderable_class'
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ I18n.enforce_available_locales = false if I18n.respond_to?(:enforce_available_locales)
6
+ I18n.load_path << File.join(File.dirname(__FILE__), 'config', 'locales', 'en.yml')
7
+
8
+ require 'mongoid'
9
+
10
+ require 'mongoid/orderable/version'
11
+
12
+ require 'mongoid/orderable'
13
+ require 'mongoid/orderable/configs/global_config'
14
+ require 'mongoid/orderable/configs/field_config'
15
+ require 'mongoid/orderable/errors/invalid_target_position'
16
+ require 'mongoid/orderable/errors/transaction_failed'
17
+ require 'mongoid/orderable/mixins/helpers'
18
+ require 'mongoid/orderable/mixins/callbacks'
19
+ require 'mongoid/orderable/mixins/listable'
20
+ require 'mongoid/orderable/mixins/movable'
21
+ require 'mongoid/orderable/generators/base'
22
+ require 'mongoid/orderable/generators/listable'
23
+ require 'mongoid/orderable/generators/lock_collection'
24
+ require 'mongoid/orderable/generators/movable'
25
+ require 'mongoid/orderable/generators/position'
26
+ require 'mongoid/orderable/generators/scope'
27
+ require 'mongoid/orderable/generators/helpers'
28
+ require 'mongoid/orderable/engine'
29
+ require 'mongoid/orderable/installer'
@@ -1,1408 +1,1486 @@
1
- require 'spec_helper'
2
-
3
- describe Mongoid::Orderable do
4
-
5
- class SimpleOrderable
6
- include Mongoid::Document
7
- include Mongoid::Orderable
8
-
9
- orderable
10
- end
11
-
12
- class ScopedGroup
13
- include Mongoid::Document
14
-
15
- has_many :scoped_orderables
16
- has_many :multiple_columns_orderables
17
- end
18
-
19
- class ScopedOrderable
20
- include Mongoid::Document
21
- include Mongoid::Orderable
22
-
23
- field :group_id
24
- belongs_to :scoped_group
25
-
26
- orderable :scope => :group
27
- end
28
-
29
- class StringScopedOrderable
30
- include Mongoid::Document
31
- include Mongoid::Orderable
32
-
33
- field :some_scope, :type => Integer
34
-
35
- orderable :scope => 'some_scope'
36
- end
37
-
38
- class EmbedsOrderable
39
- include Mongoid::Document
40
-
41
- embeds_many :embedded_orderables
42
- end
43
-
44
- class EmbeddedOrderable
45
- include Mongoid::Document
46
- include Mongoid::Orderable
47
-
48
- embedded_in :embeds_orderable
49
-
50
- orderable
51
- end
52
-
53
- class CustomizedOrderable
54
- include Mongoid::Document
55
- include Mongoid::Orderable
56
-
57
- orderable :column => :pos, :as => :my_position
58
- end
59
-
60
- class NoIndexOrderable
61
- include Mongoid::Document
62
- include Mongoid::Orderable
63
-
64
- orderable :index => false
65
- end
66
-
67
- class ZeroBasedOrderable
68
- include Mongoid::Document
69
- include Mongoid::Orderable
70
-
71
- orderable :base => 0
72
- end
73
-
74
- class Fruit
75
- include Mongoid::Document
76
- include Mongoid::Orderable
77
-
78
- orderable :inherited => true
79
- end
80
-
81
- class Apple < Fruit
82
- end
83
-
84
- class Orange < Fruit
85
- end
86
-
87
- class ForeignKeyDiffersOrderable
88
- include Mongoid::Document
89
- include Mongoid::Orderable
90
-
91
- belongs_to :different_scope, :class_name => "ForeignKeyDiffersOrderable",
92
- :foreign_key => "different_orderable_id"
93
-
94
- orderable :scope => :different_scope
95
- end
96
-
97
- class MultipleColumnsOrderable
98
- include Mongoid::Document
99
- include Mongoid::Orderable
100
-
101
- field :group_id
102
-
103
- belongs_to :scoped_group
104
-
105
- orderable :column => :pos, :base => 0, :index => false, :as => :position
106
- orderable :column => :serial_no, :default => true
107
- orderable :column => :groups, :scope => :group
108
- end
109
-
110
- class MultipleScopedOrderable
111
- include Mongoid::Document
112
- include Mongoid::Orderable
113
-
114
- belongs_to :apple
115
- belongs_to :orange
116
-
117
- orderable :column => :posa, :scope => :apple_id
118
- orderable :column => :poso, :scope => :orange_id
119
- end
120
-
121
- describe SimpleOrderable do
122
-
123
- before :each do
124
- SimpleOrderable.delete_all
125
- 5.times do
126
- SimpleOrderable.create!
127
- end
128
- end
129
-
130
- def positions
131
- SimpleOrderable.all.map(&:position).sort
132
- end
133
-
134
- it 'should have proper position column' do
135
- expect(SimpleOrderable.fields.key?('position')).to be true
136
- expect(SimpleOrderable.fields['position'].options[:type]).to eq(Integer)
137
- end
138
-
139
- it 'should have index on position column' do
140
- if MongoidOrderable.mongoid2?
141
- expect(SimpleOrderable.index_options[[[:position, 1]]]).not_to be_nil
142
- elsif MongoidOrderable.mongoid3?
143
- expect(SimpleOrderable.index_options[{:position => 1}]).not_to be_nil
144
- else
145
- expect(SimpleOrderable.index_specifications.detect { |spec| spec.key == {:position => 1} }).not_to be_nil
146
- end
147
- end
148
-
149
- it 'should have a orderable base of 1' do
150
- expect(SimpleOrderable.create!.orderable_base).to eq(1)
151
- end
152
-
153
- it 'should set proper position while creation' do
154
- expect(positions).to eq([1, 2, 3, 4, 5])
155
- end
156
-
157
- describe 'removement' do
158
-
159
- it 'top' do
160
- SimpleOrderable.where(:position => 1).destroy
161
- expect(positions).to eq([1, 2, 3, 4])
162
- end
163
-
164
- it 'bottom' do
165
- SimpleOrderable.where(:position => 5).destroy
166
- expect(positions).to eq([1, 2, 3, 4])
167
- end
168
-
169
- it 'middle' do
170
- SimpleOrderable.where(:position => 3).destroy
171
- expect(positions).to eq([1, 2, 3, 4])
172
- end
173
- end
174
-
175
- describe 'inserting' do
176
-
177
- it 'top' do
178
- newbie = SimpleOrderable.create! :move_to => :top
179
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
180
- expect(newbie.position).to eq(1)
181
- end
182
-
183
- it 'bottom' do
184
- newbie = SimpleOrderable.create! :move_to => :bottom
185
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
186
- expect(newbie.position).to eq(6)
187
- end
188
-
189
- it 'middle' do
190
- newbie = SimpleOrderable.create! :move_to => 4
191
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
192
- expect(newbie.position).to eq(4)
193
- end
194
-
195
- it 'middle (with a numeric string)' do
196
- newbie = SimpleOrderable.create! :move_to => '4'
197
- expect(positions).to eq([1, 2, 3, 4, 5, 6])
198
- expect(newbie.position).to eq(4)
199
- end
200
-
201
- it 'middle (with a non-numeric string)' do
202
- expect do
203
- SimpleOrderable.create! :move_to => 'four'
204
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
205
- end
206
- end
207
-
208
- describe 'movement' do
209
-
210
- it 'higher from top' do
211
- record = SimpleOrderable.where(:position => 1).first
212
- record.update_attributes :move_to => :higher
213
- expect(positions).to eq([1, 2, 3, 4, 5])
214
- expect(record.reload.position).to eq(1)
215
- end
216
-
217
- it 'higher from bottom' do
218
- record = SimpleOrderable.where(:position => 5).first
219
- record.update_attributes :move_to => :higher
220
- expect(positions).to eq([1, 2, 3, 4, 5])
221
- expect(record.reload.position).to eq(4)
222
- end
223
-
224
- it 'higher from middle' do
225
- record = SimpleOrderable.where(:position => 3).first
226
- record.update_attributes :move_to => :higher
227
- expect(positions).to eq([1, 2, 3, 4, 5])
228
- expect(record.reload.position).to eq(2)
229
- end
230
-
231
- it 'lower from top' do
232
- record = SimpleOrderable.where(:position => 1).first
233
- record.update_attributes :move_to => :lower
234
- expect(positions).to eq([1, 2, 3, 4, 5])
235
- expect(record.reload.position).to eq(2)
236
- end
237
-
238
- it 'lower from bottom' do
239
- record = SimpleOrderable.where(:position => 5).first
240
- record.update_attributes :move_to => :lower
241
- expect(positions).to eq([1, 2, 3, 4, 5])
242
- expect(record.reload.position).to eq(5)
243
- end
244
-
245
- it 'lower from middle' do
246
- record = SimpleOrderable.where(:position => 3).first
247
- record.update_attributes :move_to => :lower
248
- expect(positions).to eq([1, 2, 3, 4, 5])
249
- expect(record.reload.position).to eq(4)
250
- end
251
-
252
- it 'does nothing if position not change' do
253
- record = SimpleOrderable.where(:position => 3).first
254
- record.save
255
- expect(positions).to eq([1, 2, 3, 4, 5])
256
- expect(record.reload.position).to eq(3)
257
- end
258
- end
259
-
260
- describe 'utiity methods' do
261
-
262
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
263
- record_1 = SimpleOrderable.where(:position => 1).first
264
- record_2 = SimpleOrderable.where(:position => 2).first
265
- record_3 = SimpleOrderable.where(:position => 3).first
266
- record_4 = SimpleOrderable.where(:position => 4).first
267
- record_5 = SimpleOrderable.where(:position => 5).first
268
- expect(record_1.next_items.to_a).to eq([record_2, record_3, record_4, record_5])
269
- expect(record_5.previous_items.to_a).to eq([record_1, record_2, record_3, record_4])
270
- expect(record_3.previous_items.to_a).to eq([record_1, record_2])
271
- expect(record_3.next_items.to_a).to eq([record_4, record_5])
272
- end
273
- end
274
- end
275
-
276
- describe ScopedOrderable do
277
-
278
- before :each do
279
- ScopedOrderable.delete_all
280
- 2.times do
281
- ScopedOrderable.create! :group_id => 1
282
- end
283
- 3.times do
284
- ScopedOrderable.create! :group_id => 2
285
- end
286
- end
287
-
288
- def positions
289
- ScopedOrderable.order_by([:group_id, :asc], [:position, :asc]).map(&:position)
290
- end
291
-
292
- it 'should set proper position while creation' do
293
- expect(positions).to eq([1, 2, 1, 2, 3])
294
- end
295
-
296
- describe 'removement' do
297
-
298
- it 'top' do
299
- ScopedOrderable.where(:position => 1, :group_id => 1).destroy
300
- expect(positions).to eq([1, 1, 2, 3])
301
- end
302
-
303
- it 'bottom' do
304
- ScopedOrderable.where(:position => 3, :group_id => 2).destroy
305
- expect(positions).to eq([1, 2, 1, 2])
306
- end
307
-
308
- it 'middle' do
309
- ScopedOrderable.where(:position => 2, :group_id => 2).destroy
310
- expect(positions).to eq([1, 2, 1, 2])
311
- end
312
- end
313
-
314
- describe 'inserting' do
315
-
316
- it 'top' do
317
- newbie = ScopedOrderable.create! :move_to => :top, :group_id => 1
318
- expect(positions).to eq([1, 2, 3, 1, 2, 3])
319
- expect(newbie.position).to eq(1)
320
- end
321
-
322
- it 'bottom' do
323
- newbie = ScopedOrderable.create! :move_to => :bottom, :group_id => 2
324
- expect(positions).to eq([1, 2, 1, 2, 3, 4])
325
- expect(newbie.position).to eq(4)
326
- end
327
-
328
- it 'middle' do
329
- newbie = ScopedOrderable.create! :move_to => 2, :group_id => 2
330
- expect(positions).to eq([1, 2, 1, 2, 3, 4])
331
- expect(newbie.position).to eq(2)
332
- end
333
-
334
- it 'middle (with a numeric string)' do
335
- newbie = ScopedOrderable.create! :move_to => '2', :group_id => 2
336
- expect(positions).to eq([1, 2, 1, 2, 3, 4])
337
- expect(newbie.position).to eq(2)
338
- end
339
-
340
- it 'middle (with a non-numeric string)' do
341
- expect do
342
- ScopedOrderable.create! :move_to => 'two', :group_id => 2
343
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
344
- end
345
- end
346
-
347
- describe 'index' do
348
-
349
- it 'is not on position alone' do
350
- if MongoidOrderable.mongoid2?
351
- expect(ScopedOrderable.index_options[[[:position, 1]]]).to be_nil
352
- elsif MongoidOrderable.mongoid3?
353
- expect(ScopedOrderable.index_options[{:position => 1}]).to be_nil
354
- else
355
- expect(ScopedOrderable.index_specifications.detect { |spec| spec.key == {:position => 1} }).to be_nil
356
- end
357
- end
358
-
359
- it 'is on compound fields' do
360
- if MongoidOrderable.mongoid2?
361
- expect(ScopedOrderable.index_options[[[:group_id, 1], [:position, 1]]]).to_not be_nil
362
- elsif MongoidOrderable.mongoid3?
363
- expect(ScopedOrderable.index_options[{:group_id => 1, :position => 1}]).to_not be_nil
364
- else
365
- expect(ScopedOrderable.index_specifications.detect { |spec| spec.key == {:group_id => 1, :position => 1} }).to_not be_nil
366
- end
367
- end
368
- end
369
-
370
- describe 'scope movement' do
371
-
372
- let(:record){ ScopedOrderable.where(:group_id => 2, :position => 2).first }
373
-
374
- it 'to a new scope group' do
375
- record.update_attributes :group_id => 3
376
- expect(positions).to eq([1, 2, 1, 2, 1])
377
- expect(record.position).to eq(1)
378
- end
379
-
380
- context 'when moving to an existing scope group' do
381
-
382
- it 'without a position' do
383
- record.update_attributes :group_id => 1
384
- expect(positions).to eq([1, 2, 3, 1, 2])
385
- expect(record.reload.position).to eq(3)
386
- end
387
-
388
- it 'with symbol position' do
389
- record.update_attributes :group_id => 1, :move_to => :top
390
- expect(positions).to eq([1, 2, 3, 1, 2])
391
- expect(record.reload.position).to eq(1)
392
- end
393
-
394
- it 'with point position' do
395
- record.update_attributes :group_id => 1, :move_to => 2
396
- expect(positions).to eq([1, 2, 3, 1, 2])
397
- expect(record.reload.position).to eq(2)
398
- end
399
-
400
- it 'with point position (with a numeric string)' do
401
- record.update_attributes :group_id => 1, :move_to => '2'
402
- expect(positions).to eq([1, 2, 3, 1, 2])
403
- expect(record.reload.position).to eq(2)
404
- end
405
-
406
- it 'with point position (with a non-numeric string)' do
407
- expect do
408
- record.update_attributes :group_id => 1, :move_to => 'two'
409
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
410
- end
411
- end
412
- end
413
-
414
- if defined?(Mongoid::IdentityMap)
415
-
416
- context 'when identity map is enabled' do
417
-
418
- let(:record){ ScopedOrderable.where(:group_id => 2, :position => 2).first }
419
-
420
- before do
421
- Mongoid.identity_map_enabled = true
422
- Mongoid::IdentityMap[ScopedOrderable.collection_name] = { record.id => record }
423
- end
424
-
425
- after do
426
- Mongoid.identity_map_enabled = false
427
- end
428
-
429
- it 'to a new scope group' do
430
- record.update_attributes :group_id => 3
431
- expect(positions).to eq([1, 2, 1, 2, 1])
432
- expect(record.position).to eq(1)
433
- end
434
-
435
- it 'to an existing scope group' do
436
- record.update_attributes :group_id => 1, :move_to => 2
437
- expect(positions).to eq([1, 2, 3, 1, 2])
438
- expect(record.reload.position).to eq(2)
439
- end
440
-
441
- it 'to an existing scope group (with a numeric string)' do
442
- record.update_attributes :group_id => 1, :move_to => '2'
443
- expect(positions).to eq([1, 2, 3, 1, 2])
444
- expect(record.reload.position).to eq(2)
445
- end
446
-
447
- it 'to an existing scope group (with a non-numeric string)' do
448
- expect do
449
- record.update_attributes :group_id => 1, :move_to => 'two'
450
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
451
- end
452
- end
453
- end
454
-
455
- describe 'utiity methods' do
456
-
457
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
458
- record_1 = SimpleOrderable.where(:position => 1).first
459
- record_2 = SimpleOrderable.where(:position => 2).first
460
- record_3 = SimpleOrderable.where(:position => 3).first
461
- record_4 = SimpleOrderable.where(:position => 4).first
462
- record_5 = SimpleOrderable.where(:position => 5).first
463
- expect(record_1.next_items.to_a).to eq([record_2, record_3, record_4, record_5])
464
- expect(record_5.previous_items.to_a).to eq([record_1, record_2, record_3, record_4])
465
- expect(record_3.previous_items.to_a).to eq([record_1, record_2])
466
- expect(record_3.next_items.to_a).to eq([record_4, record_5])
467
- # next_item & previous_item testing
468
- expect(record_1.next_item).to eq(record_2)
469
- expect(record_2.previous_item).to eq(record_1)
470
- expect(record_1.previous_item).to eq(nil)
471
- expect(record_5.next_item).to eq(nil)
472
- end
473
- end
474
- end
475
-
476
- describe StringScopedOrderable do
477
-
478
- it 'uses the foreign key of the relationship as scope' do
479
- orderable1 = StringScopedOrderable.create(:some_scope => 1)
480
- orderable2 = StringScopedOrderable.create(:some_scope => 1)
481
- orderable3 = StringScopedOrderable.create(:some_scope => 2)
482
- expect(orderable1.position).to eq 1
483
- expect(orderable2.position).to eq 2
484
- expect(orderable3.position).to eq 1
485
- end
486
- end
487
-
488
- describe EmbeddedOrderable do
489
-
490
- before :each do
491
- EmbedsOrderable.delete_all
492
- eo = EmbedsOrderable.create!
493
- 2.times do
494
- eo.embedded_orderables.create!
495
- end
496
- eo = EmbedsOrderable.create!
497
- 3.times do
498
- eo.embedded_orderables.create!
499
- end
500
- end
501
-
502
- def positions
503
- EmbedsOrderable.order_by(:position => 1).all.map { |eo| eo.embedded_orderables.map(&:position).sort }
504
- end
505
-
506
- it 'sets proper position while creation' do
507
- expect(positions).to eq([[1, 2], [1, 2, 3]])
508
- end
509
-
510
- it 'moves an item returned by a query to position' do
511
- embedded_orderable_1 = EmbedsOrderable.first.embedded_orderables.where(:position => 1).first
512
- embedded_orderable_2 = EmbedsOrderable.first.embedded_orderables.where(:position => 2).first
513
- embedded_orderable_1.move_to! 2
514
- expect(embedded_orderable_2.reload.position).to eq(1)
515
- end
516
- end
517
-
518
- describe CustomizedOrderable do
519
-
520
- it 'does not have default position field' do
521
- expect(CustomizedOrderable.fields).not_to have_key('position')
522
- end
523
-
524
- it 'should have custom pos field' do
525
- expect(CustomizedOrderable.fields).to have_key('pos')
526
- end
527
-
528
- it 'should have an alias my_position which points to pos field on Mongoid 3+' do
529
- if CustomizedOrderable.respond_to?(:database_field_name)
530
- expect(CustomizedOrderable.database_field_name('my_position')).to eq('pos')
531
- end
532
- end
533
- end
534
-
535
- describe NoIndexOrderable do
536
-
537
- it 'should not have index on position column' do
538
- if MongoidOrderable.mongoid2? || MongoidOrderable.mongoid3?
539
- expect(NoIndexOrderable.index_options[[[:position, 1]]]).to be_nil
540
- else
541
- expect(NoIndexOrderable.index_specifications.detect { |spec| spec.key == :position }).to be_nil
542
- end
543
- end
544
- end
545
-
546
- describe ZeroBasedOrderable do
547
-
548
- before :each do
549
- ZeroBasedOrderable.delete_all
550
- 5.times do
551
- ZeroBasedOrderable.create!
552
- end
553
- end
554
-
555
- def positions
556
- ZeroBasedOrderable.all.map(&:position).sort
557
- end
558
-
559
- it 'should have a orderable base of 0' do
560
- expect(ZeroBasedOrderable.create!.orderable_base).to eq(0)
561
- end
562
-
563
- it 'should set proper position while creation' do
564
- expect(positions).to eq([0, 1, 2, 3, 4])
565
- end
566
-
567
- describe 'reset position' do
568
- before{ ZeroBasedOrderable.update_all({:position => nil}) }
569
- it 'should properly reset position' do
570
- ZeroBasedOrderable.all.map(&:save)
571
- expect(positions).to eq([0, 1, 2, 3, 4])
572
- end
573
- end
574
-
575
- describe 'removement' do
576
-
577
- it 'top' do
578
- ZeroBasedOrderable.where(:position => 0).destroy
579
- expect(positions).to eq([0, 1, 2, 3])
580
- end
581
-
582
- it 'bottom' do
583
- ZeroBasedOrderable.where(:position => 4).destroy
584
- expect(positions).to eq([0, 1, 2, 3])
585
- end
586
-
587
- it 'middle' do
588
- ZeroBasedOrderable.where(:position => 2).destroy
589
- expect(positions).to eq([0, 1, 2, 3])
590
- end
591
- end
592
-
593
- describe 'inserting' do
594
-
595
- it 'top' do
596
- newbie = ZeroBasedOrderable.create! :move_to => :top
597
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
598
- expect(newbie.position).to eq(0)
599
- end
600
-
601
- it 'bottom' do
602
- newbie = ZeroBasedOrderable.create! :move_to => :bottom
603
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
604
- expect(newbie.position).to eq(5)
605
- end
606
-
607
- it 'middle' do
608
- newbie = ZeroBasedOrderable.create! :move_to => 3
609
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
610
- expect(newbie.position).to eq(3)
611
- end
612
-
613
- it 'middle (with a numeric string)' do
614
- newbie = ZeroBasedOrderable.create! :move_to => '3'
615
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
616
- expect(newbie.position).to eq(3)
617
- end
618
-
619
- it 'middle (with a non-numeric string)' do
620
- expect do
621
- ZeroBasedOrderable.create! :move_to => 'three'
622
- end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
623
- end
624
- end
625
-
626
- describe 'movement' do
627
-
628
- it 'higher from top' do
629
- record = ZeroBasedOrderable.where(:position => 0).first
630
- record.update_attributes :move_to => :higher
631
- expect(positions).to eq([0, 1, 2, 3, 4])
632
- expect(record.reload.position).to eq(0)
633
- end
634
-
635
- it 'higher from bottom' do
636
- record = ZeroBasedOrderable.where(:position => 4).first
637
- record.update_attributes :move_to => :higher
638
- expect(positions).to eq([0, 1, 2, 3, 4])
639
- expect(record.reload.position).to eq(3)
640
- end
641
-
642
- it 'higher from middle' do
643
- record = ZeroBasedOrderable.where(:position => 3).first
644
- record.update_attributes :move_to => :higher
645
- expect(positions).to eq([0, 1, 2, 3, 4])
646
- expect(record.reload.position).to eq(2)
647
- end
648
-
649
- it 'lower from top' do
650
- record = ZeroBasedOrderable.where(:position => 0).first
651
- record.update_attributes :move_to => :lower
652
- expect(positions).to eq([0, 1, 2, 3, 4])
653
- expect(record.reload.position).to eq(1)
654
- end
655
-
656
- it 'lower from bottom' do
657
- record = ZeroBasedOrderable.where(:position => 4).first
658
- record.update_attributes :move_to => :lower
659
- expect(positions).to eq([0, 1, 2, 3, 4])
660
- expect(record.reload.position).to eq(4)
661
- end
662
-
663
- it 'lower from middle' do
664
- record = ZeroBasedOrderable.where(:position => 2).first
665
- record.update_attributes :move_to => :lower
666
- expect(positions).to eq([0, 1, 2, 3, 4])
667
- expect(record.reload.position).to eq(3)
668
- end
669
-
670
- it 'does nothing if position not change' do
671
- record = ZeroBasedOrderable.where(:position => 3).first
672
- record.save
673
- expect(positions).to eq([0, 1, 2, 3, 4])
674
- expect(record.reload.position).to eq(3)
675
- end
676
- end
677
-
678
- describe 'utiity methods' do
679
-
680
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
681
- record_1 = SimpleOrderable.where(:position => 1).first
682
- record_2 = SimpleOrderable.where(:position => 2).first
683
- record_3 = SimpleOrderable.where(:position => 3).first
684
- record_4 = SimpleOrderable.where(:position => 4).first
685
- record_5 = SimpleOrderable.where(:position => 5).first
686
- expect(record_1.next_items.to_a).to eq([record_2, record_3, record_4, record_5])
687
- expect(record_5.previous_items.to_a).to eq([record_1, record_2, record_3, record_4])
688
- expect(record_3.previous_items.to_a).to eq([record_1, record_2])
689
- expect(record_3.next_items.to_a).to eq([record_4, record_5])
690
- # next_item & previous_item testing
691
- expect(record_1.next_item).to eq(record_2)
692
- expect(record_2.previous_item).to eq(record_1)
693
- expect(record_1.previous_item).to eq(nil)
694
- expect(record_5.next_item).to eq(nil)
695
- end
696
- end
697
- end
698
-
699
- describe Fruit do
700
-
701
- it 'should set proper position' do
702
- fruit1 = Apple.create
703
- fruit2 = Orange.create
704
- expect(fruit1.position).to eq(1)
705
- expect(fruit2.position).to eq(2)
706
- end
707
-
708
- describe 'movement' do
709
- before :each do
710
- Fruit.delete_all
711
- 5.times do
712
- Apple.create!
713
- end
714
- end
715
-
716
- it 'with symbol position' do
717
- first_apple = Apple.first
718
- top_pos = first_apple.position
719
- bottom_pos = Apple.last.position
720
- expect do
721
- first_apple.move_to! :bottom
722
- end.to change(first_apple, :position).from(top_pos).to bottom_pos
723
- end
724
-
725
- it 'with point position' do
726
- first_apple = Apple.first
727
- top_pos = first_apple.position
728
- bottom_pos = Apple.last.position
729
- expect do
730
- first_apple.move_to! bottom_pos
731
- end.to change(first_apple, :position).from(top_pos).to bottom_pos
732
- end
733
- end
734
-
735
- describe 'add orderable configurations in inherited class' do
736
- it 'does not affect the orderable configurations of parent class and sibling class' do
737
- class Apple
738
- orderable :column => :serial
739
- end
740
- expect(Fruit.orderable_configurations).not_to eq Apple.orderable_configurations
741
- expect(Orange.orderable_configurations).not_to eq Apple.orderable_configurations
742
- expect(Fruit.orderable_configurations).to eq Orange.orderable_configurations
743
- end
744
- end
745
- end
746
-
747
- describe ForeignKeyDiffersOrderable do
748
-
749
- it 'uses the foreign key of the relationship as scope' do
750
- orderable1, orderable2, orderable3 = nil
751
- parent_scope1 = ForeignKeyDiffersOrderable.create
752
- parent_scope2 = ForeignKeyDiffersOrderable.create
753
- expect do
754
- orderable1 = ForeignKeyDiffersOrderable.create(:different_scope => parent_scope1)
755
- orderable2 = ForeignKeyDiffersOrderable.create(:different_scope => parent_scope1)
756
- orderable3 = ForeignKeyDiffersOrderable.create(:different_scope => parent_scope2)
757
- end.to_not raise_error
758
- expect(orderable1.position).to eq 1
759
- expect(orderable2.position).to eq 2
760
- expect(orderable3.position).to eq 1
761
- end
762
- end
763
-
764
- describe MultipleColumnsOrderable do
765
-
766
- before :each do
767
- MultipleColumnsOrderable.delete_all
768
- 5.times do
769
- MultipleColumnsOrderable.create!
770
- end
771
- end
772
-
773
- context 'default orderable' do
774
- let(:serial_nos){ MultipleColumnsOrderable.all.map(&:serial_no).sort }
775
-
776
- describe 'inserting' do
777
- let(:newbie){ MultipleColumnsOrderable.create! }
778
-
779
- before { @position = newbie.position }
780
-
781
- it 'top' do
782
- newbie.move_to! :top
783
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
784
- expect(newbie.serial_no).to eq(1)
785
- expect(newbie.position).to eq(@position)
786
- end
787
-
788
- it 'bottom' do
789
- newbie.move_to! :bottom
790
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
791
- expect(newbie.serial_no).to eq(6)
792
- expect(newbie.position).to eq(@position)
793
- end
794
-
795
- it 'middle' do
796
- newbie.move_to! 4
797
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
798
- expect(newbie.serial_no).to eq(4)
799
- expect(newbie.position).to eq(@position)
800
- end
801
- end
802
-
803
- describe 'movement' do
804
- it 'higher from top' do
805
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
806
- position = record.position
807
- record.move_higher!
808
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
809
- expect(record.serial_no).to eq(1)
810
- expect(record.position).to eq(position)
811
- end
812
-
813
- it 'higher from bottom' do
814
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
815
- position = record.position
816
- record.move_higher!
817
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
818
- expect(record.serial_no).to eq(4)
819
- expect(record.position).to eq(position)
820
- end
821
-
822
- it 'higher from middle' do
823
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
824
- position = record.position
825
- record.move_higher!
826
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
827
- expect(record.serial_no).to eq(2)
828
- expect(record.position).to eq(position)
829
- end
830
-
831
- it 'lower from top' do
832
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
833
- position = record.position
834
- record.move_lower!
835
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
836
- expect(record.serial_no).to eq(2)
837
- expect(record.position).to eq(position)
838
- end
839
-
840
- it 'lower from bottom' do
841
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
842
- position = record.position
843
- record.move_lower!
844
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
845
- expect(record.serial_no).to eq(5)
846
- expect(record.position).to eq(position)
847
- end
848
-
849
- it 'lower from middle' do
850
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
851
- position = record.position
852
- record.move_lower!
853
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
854
- expect(record.serial_no).to eq(4)
855
- expect(record.position).to eq(position)
856
- end
857
- end
858
-
859
- describe 'utility methods' do
860
-
861
- before do
862
- @record_1 = MultipleColumnsOrderable.where(:serial_no => 1).first
863
- @record_2 = MultipleColumnsOrderable.where(:serial_no => 2).first
864
- @record_3 = MultipleColumnsOrderable.where(:serial_no => 3).first
865
- @record_4 = MultipleColumnsOrderable.where(:serial_no => 4).first
866
- @record_5 = MultipleColumnsOrderable.where(:serial_no => 5).first
867
- end
868
-
869
- it "should return the lower/higher item on the list for next_item/previous_item" do
870
- expect(@record_1.next_item).to eq(@record_2)
871
- expect(@record_3.next_item).to eq(@record_4)
872
- expect(@record_5.next_item).to eq(nil)
873
- expect(@record_1.prev_item).to eq(nil)
874
- expect(@record_3.prev_item).to eq(@record_2)
875
- expect(@record_5.prev_item).to eq(@record_4)
876
- end
877
-
878
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
879
- expect(@record_1.next_items.to_a).to eq([@record_2, @record_3, @record_4, @record_5])
880
- expect(@record_3.next_items.to_a).to eq([@record_4, @record_5])
881
- expect(@record_5.next_items.to_a).to eq([])
882
- expect(@record_1.previous_items.to_a).to eq([])
883
- expect(@record_3.previous_items.to_a).to eq([@record_1, @record_2])
884
- expect(@record_5.previous_items.to_a).to eq([@record_1, @record_2, @record_3, @record_4])
885
- end
886
- end
887
- end
888
-
889
- context 'serial_no orderable' do
890
-
891
- let(:serial_nos){ MultipleColumnsOrderable.all.map(&:serial_no).sort }
892
-
893
- it 'should have proper serial_no column' do
894
- expect(MultipleColumnsOrderable.fields.key?('serial_no')).to be true
895
- expect(MultipleColumnsOrderable.fields['serial_no'].options[:type]).to eq(Integer)
896
- end
897
-
898
- it 'should have index on serial_no column' do
899
- if MongoidOrderable.mongoid2?
900
- expect(MultipleColumnsOrderable.index_options[[[:serial_no, 1]]]).not_to be_nil
901
- elsif MongoidOrderable.mongoid3?
902
- expect(MultipleColumnsOrderable.index_options[{:serial_no => 1}]).not_to be_nil
903
- else
904
- expect(MultipleColumnsOrderable.index_specifications.detect { |spec| spec.key == {:serial_no => 1} }).not_to be_nil
905
- end
906
- end
907
-
908
- it 'should have a orderable base of 1' do
909
- expect(MultipleColumnsOrderable.first.orderable_base(:serial_no)).to eq(1)
910
- end
911
-
912
- it 'should set proper position while creation' do
913
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
914
- end
915
-
916
- describe 'removement' do
917
-
918
- it 'top' do
919
- MultipleColumnsOrderable.where(:serial_no => 1).destroy
920
- expect(serial_nos).to eq([1, 2, 3, 4])
921
- end
922
-
923
- it 'bottom' do
924
- MultipleColumnsOrderable.where(:serial_no => 5).destroy
925
- expect(serial_nos).to eq([1, 2, 3, 4])
926
- end
927
-
928
- it 'middle' do
929
- MultipleColumnsOrderable.where(:serial_no => 3).destroy
930
- expect(serial_nos).to eq([1, 2, 3, 4])
931
- end
932
- end
933
-
934
- describe 'inserting' do
935
- let(:newbie){ MultipleColumnsOrderable.create! }
936
-
937
- before { @position = newbie.position }
938
-
939
- it 'top' do
940
- newbie.move_serial_no_to! :top
941
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
942
- expect(newbie.serial_no).to eq(1)
943
- expect(newbie.position).to eq(@position)
944
- end
945
-
946
- it 'bottom' do
947
- newbie.move_serial_no_to! :bottom
948
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
949
- expect(newbie.serial_no).to eq(6)
950
- expect(newbie.position).to eq(@position)
951
- end
952
-
953
- it 'middle' do
954
- newbie.move_serial_no_to! 4
955
- expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
956
- expect(newbie.serial_no).to eq(4)
957
- expect(newbie.position).to eq(@position)
958
- end
959
- end
960
-
961
- describe 'movement' do
962
- it 'higher from top' do
963
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
964
- position = record.position
965
- record.move_serial_no_higher!
966
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
967
- expect(record.serial_no).to eq(1)
968
- expect(record.position).to eq(position)
969
- end
970
-
971
- it 'higher from bottom' do
972
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
973
- position = record.position
974
- record.move_serial_no_higher!
975
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
976
- expect(record.serial_no).to eq(4)
977
- expect(record.position).to eq(position)
978
- end
979
-
980
- it 'higher from middle' do
981
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
982
- position = record.position
983
- record.move_serial_no_higher!
984
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
985
- expect(record.serial_no).to eq(2)
986
- expect(record.position).to eq(position)
987
- end
988
-
989
- it 'lower from top' do
990
- record = MultipleColumnsOrderable.where(:serial_no => 1).first
991
- position = record.position
992
- record.move_serial_no_lower!
993
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
994
- expect(record.serial_no).to eq(2)
995
- expect(record.position).to eq(position)
996
- end
997
-
998
- it 'lower from bottom' do
999
- record = MultipleColumnsOrderable.where(:serial_no => 5).first
1000
- position = record.position
1001
- record.move_serial_no_lower!
1002
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
1003
- expect(record.serial_no).to eq(5)
1004
- expect(record.position).to eq(position)
1005
- end
1006
-
1007
- it 'lower from middle' do
1008
- record = MultipleColumnsOrderable.where(:serial_no => 3).first
1009
- position = record.position
1010
- record.move_serial_no_lower!
1011
- expect(serial_nos).to eq([1, 2, 3, 4, 5])
1012
- expect(record.serial_no).to eq(4)
1013
- expect(record.position).to eq(position)
1014
- end
1015
- end
1016
-
1017
- describe 'utility methods' do
1018
-
1019
- before do
1020
- @record_1 = MultipleColumnsOrderable.where(:serial_no => 1).first
1021
- @record_2 = MultipleColumnsOrderable.where(:serial_no => 2).first
1022
- @record_3 = MultipleColumnsOrderable.where(:serial_no => 3).first
1023
- @record_4 = MultipleColumnsOrderable.where(:serial_no => 4).first
1024
- @record_5 = MultipleColumnsOrderable.where(:serial_no => 5).first
1025
- end
1026
-
1027
- it "should return the lower/higher item on the list for next_item/previous_item" do
1028
- expect(@record_1.next_serial_no_item).to eq(@record_2)
1029
- expect(@record_3.next_serial_no_item).to eq(@record_4)
1030
- expect(@record_5.next_serial_no_item).to eq(nil)
1031
- expect(@record_1.prev_serial_no_item).to eq(nil)
1032
- expect(@record_3.prev_serial_no_item).to eq(@record_2)
1033
- expect(@record_5.prev_serial_no_item).to eq(@record_4)
1034
- end
1035
-
1036
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
1037
- expect(@record_1.next_serial_no_items.to_a).to eq([@record_2, @record_3, @record_4, @record_5])
1038
- expect(@record_3.next_serial_no_items.to_a).to eq([@record_4, @record_5])
1039
- expect(@record_5.next_serial_no_items.to_a).to eq([])
1040
- expect(@record_1.previous_serial_no_items.to_a).to eq([])
1041
- expect(@record_3.previous_serial_no_items.to_a).to eq([@record_1, @record_2])
1042
- expect(@record_5.previous_serial_no_items.to_a).to eq([@record_1, @record_2, @record_3, @record_4])
1043
- end
1044
- end
1045
-
1046
- end
1047
-
1048
- context 'position orderable' do
1049
-
1050
- let(:positions){ MultipleColumnsOrderable.all.map(&:position).sort }
1051
-
1052
- it 'should not have default position field' do
1053
- expect(MultipleColumnsOrderable.fields).not_to have_key('position')
1054
- end
1055
-
1056
- it 'should have custom pos field' do
1057
- expect(MultipleColumnsOrderable.fields).to have_key('pos')
1058
- expect(MultipleColumnsOrderable.fields['pos'].options[:type]).to eq(Integer)
1059
- end
1060
-
1061
- it 'should have index on position column' do
1062
- if MongoidOrderable.mongoid2?
1063
- expect(MultipleColumnsOrderable.index_options[[[:position, 1]]]).to be_nil
1064
- elsif MongoidOrderable.mongoid3?
1065
- expect(MultipleColumnsOrderable.index_options[{:position => 1}]).to be_nil
1066
- else
1067
- expect(MultipleColumnsOrderable.index_specifications.detect { |spec| spec.key == {:position => 1} }).to be_nil
1068
- end
1069
- end
1070
-
1071
- it 'should have a orderable base of 0' do
1072
- expect(MultipleColumnsOrderable.first.orderable_base(:position)).to eq(0)
1073
- end
1074
-
1075
- it 'should set proper position while creation' do
1076
- expect(positions).to eq([0, 1, 2, 3, 4])
1077
- end
1078
-
1079
- describe 'removement' do
1080
-
1081
- it 'top' do
1082
- MultipleColumnsOrderable.where(:pos => 1).destroy
1083
- expect(positions).to eq([0, 1, 2, 3])
1084
- end
1085
-
1086
- it 'bottom' do
1087
- MultipleColumnsOrderable.where(:pos => 4).destroy
1088
- expect(positions).to eq([0, 1, 2, 3])
1089
- end
1090
-
1091
- it 'middle' do
1092
- MultipleColumnsOrderable.where(:pos => 3).destroy
1093
- expect(positions).to eq([0, 1, 2, 3])
1094
- end
1095
- end
1096
-
1097
- describe 'inserting' do
1098
- let(:newbie){ MultipleColumnsOrderable.create! }
1099
-
1100
- before { @serial_no = newbie.serial_no }
1101
-
1102
- it 'top' do
1103
- newbie.move_position_to! :top
1104
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
1105
- expect(newbie.position).to eq(0)
1106
- expect(newbie.serial_no).to eq(@serial_no)
1107
- end
1108
-
1109
- it 'bottom' do
1110
- newbie.move_position_to! :bottom
1111
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
1112
- expect(newbie.position).to eq(5)
1113
- expect(newbie.serial_no).to eq(@serial_no)
1114
- end
1115
-
1116
- it 'middle' do
1117
- newbie.move_position_to! 4
1118
- expect(positions).to eq([0, 1, 2, 3, 4, 5])
1119
- expect(newbie.position).to eq(4)
1120
- expect(newbie.serial_no).to eq(@serial_no)
1121
- end
1122
- end
1123
-
1124
- describe 'movement' do
1125
- it 'higher from top' do
1126
- record = MultipleColumnsOrderable.where(:pos => 0).first
1127
- position = record.serial_no
1128
- record.move_position_higher!
1129
- expect(positions).to eq([0, 1, 2, 3, 4])
1130
- expect(record.position).to eq(0)
1131
- expect(record.serial_no).to eq(position)
1132
- end
1133
-
1134
- it 'higher from bottom' do
1135
- record = MultipleColumnsOrderable.where(:pos => 4).first
1136
- position = record.serial_no
1137
- record.move_position_higher!
1138
- expect(positions).to eq([0, 1, 2, 3, 4])
1139
- expect(record.position).to eq(3)
1140
- expect(record.serial_no).to eq(position)
1141
- end
1142
-
1143
- it 'higher from middle' do
1144
- record = MultipleColumnsOrderable.where(:pos => 3).first
1145
- position = record.serial_no
1146
- record.move_position_higher!
1147
- expect(positions).to eq([0, 1, 2, 3, 4])
1148
- expect(record.position).to eq(2)
1149
- expect(record.serial_no).to eq(position)
1150
- end
1151
-
1152
- it 'lower from top' do
1153
- record = MultipleColumnsOrderable.where(:pos => 0).first
1154
- position = record.serial_no
1155
- record.move_position_lower!
1156
- expect(positions).to eq([0, 1, 2, 3, 4])
1157
- expect(record.position).to eq(1)
1158
- expect(record.serial_no).to eq(position)
1159
- end
1160
-
1161
- it 'lower from bottom' do
1162
- record = MultipleColumnsOrderable.where(:pos => 4).first
1163
- position = record.serial_no
1164
- record.move_position_lower!
1165
- expect(positions).to eq([0, 1, 2, 3, 4])
1166
- expect(record.position).to eq(4)
1167
- expect(record.serial_no).to eq(position)
1168
- end
1169
-
1170
- it 'lower from middle' do
1171
- record = MultipleColumnsOrderable.where(:pos => 3).first
1172
- position = record.serial_no
1173
- record.move_position_lower!
1174
- expect(positions).to eq([0, 1, 2, 3, 4])
1175
- expect(record.position).to eq(4)
1176
- expect(record.serial_no).to eq(position)
1177
- end
1178
- end
1179
-
1180
- describe 'utility methods' do
1181
-
1182
- before do
1183
- @record_1 = MultipleColumnsOrderable.where(:pos => 0).first
1184
- @record_2 = MultipleColumnsOrderable.where(:pos => 1).first
1185
- @record_3 = MultipleColumnsOrderable.where(:pos => 2).first
1186
- @record_4 = MultipleColumnsOrderable.where(:pos => 3).first
1187
- @record_5 = MultipleColumnsOrderable.where(:pos => 4).first
1188
- end
1189
-
1190
- it "should return the lower/higher item on the list for next_item/previous_item" do
1191
- expect(@record_1.next_position_item).to eq(@record_2)
1192
- expect(@record_3.next_position_item).to eq(@record_4)
1193
- expect(@record_5.next_position_item).to eq(nil)
1194
- expect(@record_1.prev_position_item).to eq(nil)
1195
- expect(@record_3.prev_position_item).to eq(@record_2)
1196
- expect(@record_5.prev_position_item).to eq(@record_4)
1197
- end
1198
-
1199
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
1200
- expect(@record_1.next_position_items.to_a).to eq([@record_2, @record_3, @record_4, @record_5])
1201
- expect(@record_3.next_position_items.to_a).to eq([@record_4, @record_5])
1202
- expect(@record_5.next_position_items.to_a).to eq([])
1203
- expect(@record_1.previous_position_items.to_a).to eq([])
1204
- expect(@record_3.previous_position_items.to_a).to eq([@record_1, @record_2])
1205
- expect(@record_5.previous_position_items.to_a).to eq([@record_1, @record_2, @record_3, @record_4])
1206
- end
1207
- end
1208
-
1209
- end
1210
-
1211
- context 'group_count orderable' do
1212
- before :each do
1213
- MultipleColumnsOrderable.delete_all
1214
- 2.times { MultipleColumnsOrderable.create! :group_id => 1 }
1215
- 3.times { MultipleColumnsOrderable.create! :group_id => 2 }
1216
- end
1217
-
1218
- let(:all_groups){ MultipleColumnsOrderable.order_by([:group_id, :asc], [:groups, :asc]).map(&:groups) }
1219
-
1220
- it 'should set proper position while creation' do
1221
- expect(all_groups).to eq([1, 2, 1, 2, 3])
1222
- end
1223
-
1224
- describe 'removement' do
1225
-
1226
- it 'top' do
1227
- MultipleColumnsOrderable.where(:groups => 1, :group_id => 1).destroy
1228
- expect(all_groups).to eq([1, 1, 2, 3])
1229
- end
1230
-
1231
- it 'bottom' do
1232
- MultipleColumnsOrderable.where(:groups => 3, :group_id => 2).destroy
1233
- expect(all_groups).to eq([1, 2, 1, 2])
1234
- end
1235
-
1236
- it 'middle' do
1237
- MultipleColumnsOrderable.where(:groups => 2, :group_id => 2).destroy
1238
- expect(all_groups).to eq([1, 2, 1, 2])
1239
- end
1240
- end
1241
-
1242
- describe 'inserting' do
1243
-
1244
- it 'top' do
1245
- newbie = MultipleColumnsOrderable.create! :group_id => 1
1246
- newbie.move_groups_to! :top
1247
- expect(all_groups).to eq([1, 2, 3, 1, 2, 3])
1248
- expect(newbie.groups).to eq(1)
1249
- end
1250
-
1251
- it 'bottom' do
1252
- newbie = MultipleColumnsOrderable.create! :group_id => 2
1253
- newbie.move_groups_to! :bottom
1254
- expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
1255
- expect(newbie.groups).to eq(4)
1256
- end
1257
-
1258
- it 'middle' do
1259
- newbie = MultipleColumnsOrderable.create! :group_id => 2
1260
- newbie.move_groups_to! 2
1261
- expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
1262
- expect(newbie.groups).to eq(2)
1263
- end
1264
- end
1265
-
1266
- describe 'scope movement' do
1267
-
1268
- let(:record){ MultipleColumnsOrderable.where(:group_id => 2, :groups => 2).first }
1269
-
1270
- it 'to a new scope group' do
1271
- record.update_attributes :group_id => 3
1272
- expect(all_groups).to eq([1, 2, 1, 2, 1])
1273
- expect(record.groups).to eq(1)
1274
- end
1275
-
1276
- context 'when moving to an existing scope group' do
1277
-
1278
- it 'without a position' do
1279
- record.update_attributes :group_id => 1
1280
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1281
- expect(record.reload.groups).to eq(3)
1282
- end
1283
-
1284
- it 'with symbol position' do
1285
- record.update_attributes :group_id => 1
1286
- record.move_groups_to! :top
1287
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1288
- expect(record.reload.groups).to eq(1)
1289
- end
1290
-
1291
- it 'with point position' do
1292
- record.update_attributes :group_id => 1
1293
- record.move_groups_to! 2
1294
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1295
- expect(record.reload.groups).to eq(2)
1296
- end
1297
- end
1298
- end
1299
-
1300
- if defined?(Mongoid::IdentityMap)
1301
-
1302
- context 'when identity map is enabled' do
1303
-
1304
- let(:record){ MultipleColumnsOrderable.where(:group_id => 2, :groups => 2).first }
1305
-
1306
- before do
1307
- Mongoid.identity_map_enabled = true
1308
- Mongoid::IdentityMap[MultipleColumnsOrderable.collection_name] = { record.id => record }
1309
- end
1310
-
1311
- after { Mongoid.identity_map_enabled = false }
1312
-
1313
- it 'to a new scope group' do
1314
- record.update_attributes :group_id => 3
1315
- expect(all_groups).to eq([1, 2, 1, 2, 1])
1316
- expect(record.groups).to eq(1)
1317
- end
1318
-
1319
- it 'to an existing scope group' do
1320
- record.update_attributes :group_id => 1
1321
- record.move_groups_to! 2
1322
- expect(all_groups).to eq([1, 2, 3, 1, 2])
1323
- expect(record.groups).to eq(2)
1324
- end
1325
- end
1326
- end
1327
-
1328
- describe 'utility methods' do
1329
-
1330
- before do
1331
- @record_1 = MultipleColumnsOrderable.where(:group_id => 2, :groups => 1).first
1332
- @record_2 = MultipleColumnsOrderable.where(:group_id => 2, :groups => 2).first
1333
- @record_3 = MultipleColumnsOrderable.where(:group_id => 2, :groups => 3).first
1334
- @record_4 = MultipleColumnsOrderable.where(:group_id => 1, :groups => 1).first
1335
- @record_5 = MultipleColumnsOrderable.where(:group_id => 1, :groups => 2).first
1336
- end
1337
-
1338
- it "should return the lower/higher item on the list for next_item/previous_item" do
1339
- expect(@record_1.next_groups_item).to eq(@record_2)
1340
- expect(@record_4.next_groups_item).to eq(@record_5)
1341
- expect(@record_3.next_groups_item).to eq(nil)
1342
- expect(@record_1.prev_groups_item).to eq(nil)
1343
- expect(@record_3.prev_groups_item).to eq(@record_2)
1344
- expect(@record_5.prev_groups_item).to eq(@record_4)
1345
- end
1346
-
1347
- it "should return a collection of items lower/higher on the list for next_items/previous_items" do
1348
- expect(@record_1.next_groups_items.to_a).to eq([@record_2, @record_3])
1349
- expect(@record_3.next_groups_items.to_a).to eq([])
1350
- expect(@record_4.next_groups_items.to_a).to eq([@record_5])
1351
- expect(@record_1.previous_groups_items.to_a).to eq([])
1352
- expect(@record_3.previous_groups_items.to_a).to eq([@record_1, @record_2])
1353
- expect(@record_5.previous_groups_items.to_a).to eq([@record_4])
1354
- end
1355
- end
1356
- end
1357
- end
1358
-
1359
- describe MultipleScopedOrderable do
1360
- before :each do
1361
- Apple.delete_all; Orange.delete_all;
1362
- MultipleScopedOrderable.delete_all
1363
-
1364
- 3.times do
1365
- Apple.create; Orange.create
1366
- end
1367
-
1368
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 1
1369
- MultipleScopedOrderable.create! :apple_id => 2, :orange_id => 1
1370
- MultipleScopedOrderable.create! :apple_id => 2, :orange_id => 2
1371
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 3
1372
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 1
1373
- MultipleScopedOrderable.create! :apple_id => 3, :orange_id => 3
1374
- MultipleScopedOrderable.create! :apple_id => 2, :orange_id => 3
1375
- MultipleScopedOrderable.create! :apple_id => 3, :orange_id => 2
1376
- MultipleScopedOrderable.create! :apple_id => 1, :orange_id => 3
1377
- end
1378
-
1379
- def apple_positions
1380
- MultipleScopedOrderable.order_by([:apple_id, :asc], [:posa, :asc]).map(&:posa)
1381
- end
1382
-
1383
- def orange_positions
1384
- MultipleScopedOrderable.order_by([:orange_id, :asc], [:poso, :asc]).map(&:poso)
1385
- end
1386
-
1387
- describe 'default positions' do
1388
- it { expect(apple_positions).to eq([1, 2, 3, 4, 1, 2, 3, 1, 2]) }
1389
- it { expect(orange_positions).to eq([1, 2, 3, 1, 2, 1, 2, 3, 4]) }
1390
- end
1391
-
1392
- describe 'change the scope of the apple' do
1393
- let(:record) { MultipleScopedOrderable.first }
1394
- before do
1395
- record.update_attribute(:apple_id, 2)
1396
- end
1397
-
1398
- it 'should properly set the apple positions' do
1399
- expect(apple_positions).to eq([1, 2, 3, 1, 2, 3, 4, 1, 2])
1400
- end
1401
-
1402
- it 'should not affect the orange positions' do
1403
- expect(orange_positions).to eq([1, 2, 3, 1, 2, 1, 2, 3, 4])
1404
- end
1405
- end
1406
- end
1407
-
1408
- end
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Orderable do
4
+ Mongoid::Orderable.configure do |c|
5
+ c.use_transactions = true
6
+ c.transaction_max_retries = 100
7
+ c.lock_collection = :foo_bar_locks
8
+ end
9
+
10
+ class SimpleOrderable
11
+ include Mongoid::Document
12
+ include Mongoid::Orderable
13
+
14
+ orderable
15
+ end
16
+
17
+ class ScopedGroup
18
+ include Mongoid::Document
19
+
20
+ has_many :scoped_orderables
21
+ has_many :multiple_fields_orderables
22
+ end
23
+
24
+ class ScopedOrderable
25
+ include Mongoid::Document
26
+ include Mongoid::Orderable
27
+
28
+ belongs_to :group, class_name: 'ScopedGroup', optional: true
29
+
30
+ orderable scope: :group
31
+ end
32
+
33
+ class StringScopedOrderable
34
+ include Mongoid::Document
35
+ include Mongoid::Orderable
36
+
37
+ field :some_scope, type: Integer
38
+
39
+ orderable scope: 'some_scope'
40
+ end
41
+
42
+ class EmbedsOrderable
43
+ include Mongoid::Document
44
+
45
+ embeds_many :embedded_orderables
46
+ end
47
+
48
+ class EmbeddedOrderable
49
+ include Mongoid::Document
50
+ include Mongoid::Orderable
51
+
52
+ embedded_in :embeds_orderable
53
+
54
+ orderable
55
+ end
56
+
57
+ class CustomizedOrderable
58
+ include Mongoid::Document
59
+ include Mongoid::Orderable
60
+
61
+ orderable field: :pos, as: :my_position
62
+ end
63
+
64
+ class NoIndexOrderable
65
+ include Mongoid::Document
66
+ include Mongoid::Orderable
67
+
68
+ orderable index: false
69
+ end
70
+
71
+ class ZeroBasedOrderable
72
+ include Mongoid::Document
73
+ include Mongoid::Orderable
74
+
75
+ orderable base: 0
76
+ end
77
+
78
+ class Fruit
79
+ include Mongoid::Document
80
+ include Mongoid::Orderable
81
+
82
+ orderable inherited: true
83
+ end
84
+
85
+ class Apple < Fruit
86
+ end
87
+
88
+ class Orange < Fruit
89
+ end
90
+
91
+ class ForeignKeyDiffersOrderable
92
+ include Mongoid::Document
93
+ include Mongoid::Orderable
94
+
95
+ belongs_to :different_scope, class_name: 'ForeignKeyDiffersOrderable',
96
+ foreign_key: 'different_orderable_id',
97
+ optional: true
98
+
99
+ orderable scope: :different_scope
100
+ end
101
+
102
+ class MultipleFieldsOrderable
103
+ include Mongoid::Document
104
+ include Mongoid::Orderable
105
+
106
+ belongs_to :group, class_name: 'ScopedGroup', optional: true
107
+
108
+ orderable field: :pos, base: 0, index: false, as: :position
109
+ orderable field: :serial_no, default: true
110
+ orderable field: :groups, scope: :group
111
+ end
112
+
113
+ class MultipleScopedOrderable
114
+ include Mongoid::Document
115
+ include Mongoid::Orderable
116
+
117
+ belongs_to :apple, optional: true
118
+ belongs_to :orange, optional: true
119
+
120
+ orderable field: :posa, scope: :apple_id
121
+ orderable field: :poso, scope: :orange_id
122
+ end
123
+
124
+ describe SimpleOrderable do
125
+ before :each do
126
+ 5.times { SimpleOrderable.create! }
127
+ end
128
+
129
+ def positions
130
+ SimpleOrderable.pluck(:position).sort
131
+ end
132
+
133
+ it 'should have proper position field' do
134
+ expect(SimpleOrderable.fields.key?('position')).to be true
135
+ expect(SimpleOrderable.fields['position'].options[:type]).to eq(Integer)
136
+ end
137
+
138
+ it 'should have index on position field' do
139
+ expect(SimpleOrderable.index_specifications.detect { |spec| spec.key == { position: 1 } }).not_to be_nil
140
+ end
141
+
142
+ it 'should have a orderable base of 1' do
143
+ expect(SimpleOrderable.create!.orderable_top).to eq(1)
144
+ end
145
+
146
+ it 'should set proper position while creation' do
147
+ expect(positions).to eq([1, 2, 3, 4, 5])
148
+ end
149
+
150
+ describe 'removement' do
151
+ it 'top' do
152
+ SimpleOrderable.where(position: 1).destroy
153
+ expect(positions).to eq([1, 2, 3, 4])
154
+ end
155
+
156
+ it 'bottom' do
157
+ SimpleOrderable.where(position: 5).destroy
158
+ expect(positions).to eq([1, 2, 3, 4])
159
+ end
160
+
161
+ it 'middle' do
162
+ SimpleOrderable.where(position: 3).destroy
163
+ expect(positions).to eq([1, 2, 3, 4])
164
+ end
165
+ end
166
+
167
+ describe 'inserting' do
168
+ it 'top' do
169
+ newbie = SimpleOrderable.create! move_to: :top
170
+ expect(positions).to eq([1, 2, 3, 4, 5, 6])
171
+ expect(newbie.position).to eq(1)
172
+ end
173
+
174
+ it 'bottom' do
175
+ newbie = SimpleOrderable.create! move_to: :bottom
176
+ expect(positions).to eq([1, 2, 3, 4, 5, 6])
177
+ expect(newbie.position).to eq(6)
178
+ end
179
+
180
+ it 'middle' do
181
+ newbie = SimpleOrderable.create! move_to: 4
182
+ expect(positions).to eq([1, 2, 3, 4, 5, 6])
183
+ expect(newbie.position).to eq(4)
184
+ end
185
+
186
+ it 'middle (with a numeric string)' do
187
+ newbie = SimpleOrderable.create! move_to: '4'
188
+ expect(positions).to eq([1, 2, 3, 4, 5, 6])
189
+ expect(newbie.position).to eq(4)
190
+ end
191
+
192
+ it 'middle (with a non-numeric string)' do
193
+ expect do
194
+ SimpleOrderable.create! move_to: 'four'
195
+ end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
196
+ end
197
+
198
+ it 'simultaneous create and update' do
199
+ newbie = SimpleOrderable.new
200
+ newbie.send(:orderable_update_positions) { }
201
+ expect(newbie.position).to eq(6)
202
+ another = SimpleOrderable.create!
203
+ expect(another.position).to eq(6)
204
+ newbie.save!
205
+ expect(positions).to eq([1, 2, 3, 4, 5, 6, 7])
206
+ expect(newbie.position).to eq(7)
207
+ expect(another.position).to eq(6)
208
+ end
209
+
210
+ it 'parallel updates' do
211
+ newbie = SimpleOrderable.new
212
+ newbie.send(:orderable_update_positions) { }
213
+ another = SimpleOrderable.create!
214
+ newbie.save!
215
+ expect(positions).to eq([1, 2, 3, 4, 5, 6, 7])
216
+ expect(newbie.position).to eq(7)
217
+ expect(another.position).to eq(6)
218
+ end
219
+ end
220
+
221
+ describe 'movement' do
222
+ it 'higher from top' do
223
+ record = SimpleOrderable.where(position: 1).first
224
+ record.update_attributes move_to: :higher
225
+ expect(positions).to eq([1, 2, 3, 4, 5])
226
+ expect(record.reload.position).to eq(1)
227
+ end
228
+
229
+ it 'higher from bottom' do
230
+ record = SimpleOrderable.where(position: 5).first
231
+ record.update_attributes move_to: :higher
232
+ expect(positions).to eq([1, 2, 3, 4, 5])
233
+ expect(record.reload.position).to eq(4)
234
+ end
235
+
236
+ it 'higher from middle' do
237
+ record = SimpleOrderable.where(position: 3).first
238
+ record.update_attributes move_to: :higher
239
+ expect(positions).to eq([1, 2, 3, 4, 5])
240
+ expect(record.reload.position).to eq(2)
241
+ end
242
+
243
+ it 'lower from top' do
244
+ record = SimpleOrderable.where(position: 1).first
245
+ record.update_attributes move_to: :lower
246
+ expect(positions).to eq([1, 2, 3, 4, 5])
247
+ expect(record.reload.position).to eq(2)
248
+ end
249
+
250
+ it 'lower from bottom' do
251
+ record = SimpleOrderable.where(position: 5).first
252
+ record.update_attributes move_to: :lower
253
+ expect(positions).to eq([1, 2, 3, 4, 5])
254
+ expect(record.reload.position).to eq(5)
255
+ end
256
+
257
+ it 'lower from middle' do
258
+ record = SimpleOrderable.where(position: 3).first
259
+ record.update_attributes move_to: :lower
260
+ expect(positions).to eq([1, 2, 3, 4, 5])
261
+ expect(record.reload.position).to eq(4)
262
+ end
263
+
264
+ it 'does nothing if position not change' do
265
+ record = SimpleOrderable.where(position: 3).first
266
+ record.save
267
+ expect(positions).to eq([1, 2, 3, 4, 5])
268
+ expect(record.reload.position).to eq(3)
269
+ end
270
+ end
271
+
272
+ describe 'utility methods' do
273
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
274
+ record1 = SimpleOrderable.where(position: 1).first
275
+ record2 = SimpleOrderable.where(position: 2).first
276
+ record3 = SimpleOrderable.where(position: 3).first
277
+ record4 = SimpleOrderable.where(position: 4).first
278
+ record5 = SimpleOrderable.where(position: 5).first
279
+ expect(record1.next_items.to_a).to eq([record2, record3, record4, record5])
280
+ expect(record5.previous_items.to_a).to eq([record1, record2, record3, record4])
281
+ expect(record3.previous_items.to_a).to eq([record1, record2])
282
+ expect(record3.next_items.to_a).to eq([record4, record5])
283
+ expect(record1.next_item).to eq(record2)
284
+ expect(record2.previous_item).to eq(record1)
285
+ expect(record1.previous_item).to eq(nil)
286
+ expect(record5.next_item).to eq(nil)
287
+ end
288
+ end
289
+
290
+ describe 'concurrency' do
291
+ it 'should correctly move items to top' do
292
+ 20.times.map do
293
+ Thread.new do
294
+ record = SimpleOrderable.all.sample
295
+ record.update_attributes move_to: :top
296
+ end
297
+ end.each(&:join)
298
+
299
+ expect(SimpleOrderable.pluck(:position).sort).to eq([1, 2, 3, 4, 5])
300
+ end
301
+
302
+ it 'should correctly move items to bottom' do
303
+ 20.times.map do
304
+ Thread.new do
305
+ record = SimpleOrderable.all.sample
306
+ record.update_attributes move_to: :bottom
307
+ end
308
+ end.each(&:join)
309
+
310
+ expect(SimpleOrderable.pluck(:position).sort).to eq([1, 2, 3, 4, 5])
311
+ end
312
+
313
+ it 'should correctly move items higher' do
314
+ 20.times.map do
315
+ Thread.new do
316
+ record = SimpleOrderable.all.sample
317
+ record.update_attributes move_to: :higher
318
+ end
319
+ end.each(&:join)
320
+
321
+ expect(SimpleOrderable.pluck(:position).sort).to eq([1, 2, 3, 4, 5])
322
+ end
323
+
324
+ it 'should correctly move items lower' do
325
+ 20.times.map do
326
+ Thread.new do
327
+ record = SimpleOrderable.all.sample
328
+ record.update_attributes move_to: :lower
329
+ end
330
+ end.each(&:join)
331
+
332
+ expect(SimpleOrderable.pluck(:position).sort).to eq([1, 2, 3, 4, 5])
333
+ end
334
+
335
+ it 'should correctly insert at the top' do
336
+ 20.times.map do
337
+ Thread.new do
338
+ SimpleOrderable.create!(move_to: :top)
339
+ end
340
+ end.each(&:join)
341
+
342
+ expect(SimpleOrderable.pluck(:position).sort).to eq((1..25).to_a)
343
+ end
344
+
345
+ it 'should correctly insert at the bottom' do
346
+ 20.times.map do
347
+ Thread.new do
348
+ SimpleOrderable.create!
349
+ end
350
+ end.each(&:join)
351
+
352
+ expect(SimpleOrderable.pluck(:position).sort).to eq((1..25).to_a)
353
+ end
354
+
355
+ it 'should correctly insert at a random position' do
356
+ 20.times.map do
357
+ Thread.new do
358
+ SimpleOrderable.create!(move_to: (1..10).to_a.sample)
359
+ end
360
+ end.each(&:join)
361
+
362
+ expect(SimpleOrderable.pluck(:position).sort).to eq((1..25).to_a)
363
+ end
364
+
365
+ it 'should correctly move items to a random position' do
366
+ 20.times.map do
367
+ Thread.new do
368
+ record = SimpleOrderable.all.sample
369
+ record.update_attributes move_to: (1..5).to_a.sample
370
+ end
371
+ end.each(&:join)
372
+
373
+ expect(SimpleOrderable.pluck(:position).sort).to eq([1, 2, 3, 4, 5])
374
+ end
375
+
376
+ context 'empty database' do
377
+ before { SimpleOrderable.delete_all }
378
+
379
+ it 'should correctly insert at the top' do
380
+ 20.times.map do
381
+ Thread.new do
382
+ SimpleOrderable.create!(move_to: :top)
383
+ end
384
+ end.each(&:join)
385
+
386
+ expect(SimpleOrderable.pluck(:position).sort).to eq((1..20).to_a)
387
+ end
388
+
389
+ it 'should correctly insert at the bottom' do
390
+ 20.times.map do
391
+ Thread.new do
392
+ SimpleOrderable.create!
393
+ end
394
+ end.each(&:join)
395
+
396
+ expect(SimpleOrderable.pluck(:position).sort).to eq((1..20).to_a)
397
+ end
398
+
399
+ it 'should correctly insert at a random position' do
400
+ 20.times.map do
401
+ Thread.new do
402
+ SimpleOrderable.create!(move_to: (1..10).to_a.sample)
403
+ end
404
+ end.each(&:join)
405
+
406
+ expect(SimpleOrderable.pluck(:position).sort).to eq((1..20).to_a)
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ describe ScopedOrderable do
413
+ before :each do
414
+ 2.times { ScopedOrderable.create! group_id: 1 }
415
+ 3.times { ScopedOrderable.create! group_id: 2 }
416
+ end
417
+
418
+ def positions
419
+ ScopedOrderable.order_by([:group_id, :asc], [:position, :asc]).map(&:position)
420
+ end
421
+
422
+ it 'should set proper position while creation' do
423
+ expect(positions).to eq([1, 2, 1, 2, 3])
424
+ end
425
+
426
+ describe 'removement' do
427
+ it 'top' do
428
+ ScopedOrderable.where(position: 1, group_id: 1).destroy
429
+ expect(positions).to eq([1, 1, 2, 3])
430
+ end
431
+
432
+ it 'bottom' do
433
+ ScopedOrderable.where(position: 3, group_id: 2).destroy
434
+ expect(positions).to eq([1, 2, 1, 2])
435
+ end
436
+
437
+ it 'middle' do
438
+ ScopedOrderable.where(position: 2, group_id: 2).destroy
439
+ expect(positions).to eq([1, 2, 1, 2])
440
+ end
441
+ end
442
+
443
+ describe 'inserting' do
444
+ it 'top' do
445
+ newbie = ScopedOrderable.create! move_to: :top, group_id: 1
446
+ expect(positions).to eq([1, 2, 3, 1, 2, 3])
447
+ expect(newbie.position).to eq(1)
448
+ end
449
+
450
+ it 'bottom' do
451
+ newbie = ScopedOrderable.create! move_to: :bottom, group_id: 2
452
+ expect(positions).to eq([1, 2, 1, 2, 3, 4])
453
+ expect(newbie.position).to eq(4)
454
+ end
455
+
456
+ it 'middle' do
457
+ newbie = ScopedOrderable.create! move_to: 2, group_id: 2
458
+ expect(positions).to eq([1, 2, 1, 2, 3, 4])
459
+ expect(newbie.position).to eq(2)
460
+ end
461
+
462
+ it 'middle (with a numeric string)' do
463
+ newbie = ScopedOrderable.create! move_to: '2', group_id: 2
464
+ expect(positions).to eq([1, 2, 1, 2, 3, 4])
465
+ expect(newbie.position).to eq(2)
466
+ end
467
+
468
+ it 'middle (with a non-numeric string)' do
469
+ expect do
470
+ ScopedOrderable.create! move_to: 'two', group_id: 2
471
+ end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
472
+ end
473
+ end
474
+
475
+ describe 'index' do
476
+ it 'is not on position alone' do
477
+ expect(ScopedOrderable.index_specifications.detect { |spec| spec.key == { position: 1 } }).to be_nil
478
+ end
479
+
480
+ it 'is on compound fields' do
481
+ expect(ScopedOrderable.index_specifications.detect { |spec| spec.key == { group_id: 1, position: 1 } }).to_not be_nil
482
+ end
483
+ end
484
+
485
+ describe 'scope movement' do
486
+ let(:record) { ScopedOrderable.where(group_id: 2, position: 2).first }
487
+
488
+ it 'to a new scope group' do
489
+ record.update_attributes group_id: 3
490
+ expect(positions).to eq([1, 2, 1, 2, 1])
491
+ expect(record.position).to eq(1)
492
+ end
493
+
494
+ context 'when moving to an existing scope group' do
495
+ it 'without a position' do
496
+ record.update_attributes group_id: 1
497
+ expect(positions).to eq([1, 2, 3, 1, 2])
498
+ expect(record.reload.position).to eq(3)
499
+ end
500
+
501
+ it 'with symbol position' do
502
+ record.update_attributes group_id: 1, move_to: :top
503
+ expect(positions).to eq([1, 2, 3, 1, 2])
504
+ expect(record.reload.position).to eq(1)
505
+ end
506
+
507
+ it 'with point position' do
508
+ record.update_attributes group_id: 1, move_to: 2
509
+ expect(positions).to eq([1, 2, 3, 1, 2])
510
+ expect(record.reload.position).to eq(2)
511
+ end
512
+
513
+ it 'with point position (with a numeric string)' do
514
+ record.update_attributes group_id: 1, move_to: '2'
515
+ expect(positions).to eq([1, 2, 3, 1, 2])
516
+ expect(record.reload.position).to eq(2)
517
+ end
518
+
519
+ it 'with point position (with a non-numeric string)' do
520
+ expect do
521
+ record.update_attributes group_id: 1, move_to: 'two'
522
+ end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
523
+ end
524
+ end
525
+ end
526
+
527
+ describe 'utility methods' do
528
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
529
+ record1 = ScopedOrderable.where(group_id: 1, position: 1).first
530
+ record2 = ScopedOrderable.where(group_id: 1, position: 2).first
531
+ record3 = ScopedOrderable.where(group_id: 2, position: 1).first
532
+ record4 = ScopedOrderable.where(group_id: 2, position: 2).first
533
+ record5 = ScopedOrderable.where(group_id: 2, position: 3).first
534
+ expect(record1.next_items.to_a).to eq([record2])
535
+ expect(record5.previous_items.to_a).to eq([record3, record4])
536
+ expect(record3.previous_items.to_a).to eq([])
537
+ expect(record3.next_items.to_a).to eq([record4, record5])
538
+ expect(record1.next_item).to eq(record2)
539
+ expect(record2.previous_item).to eq(record1)
540
+ expect(record1.previous_item).to eq(nil)
541
+ expect(record2.next_item).to eq(nil)
542
+ end
543
+ end
544
+
545
+ describe 'concurrency' do
546
+ it 'should correctly move items to top' do
547
+ 20.times.map do
548
+ Thread.new do
549
+ record = ScopedOrderable.all.sample
550
+ record.update_attributes move_to: :top
551
+ end
552
+ end.each(&:join)
553
+
554
+ expect(ScopedOrderable.pluck(:position).sort).to eq([1, 1, 2, 2, 3])
555
+ end
556
+
557
+ it 'should correctly move items to bottom' do
558
+ 20.times.map do
559
+ Thread.new do
560
+ record = ScopedOrderable.all.sample
561
+ record.update_attributes move_to: :bottom
562
+ end
563
+ end.each(&:join)
564
+
565
+ expect(ScopedOrderable.pluck(:position).sort).to eq([1, 1, 2, 2, 3])
566
+ end
567
+
568
+ it 'should correctly move items higher' do
569
+ 20.times.map do
570
+ Thread.new do
571
+ record = ScopedOrderable.all.sample
572
+ record.update_attributes move_to: :higher
573
+ end
574
+ end.each(&:join)
575
+
576
+ expect(ScopedOrderable.pluck(:position).sort).to eq([1, 1, 2, 2, 3])
577
+ end
578
+
579
+ it 'should correctly move items lower' do
580
+ 20.times.map do
581
+ Thread.new do
582
+ record = ScopedOrderable.all.sample
583
+ record.update_attributes move_to: :lower
584
+ end
585
+ end.each(&:join)
586
+
587
+ expect(ScopedOrderable.pluck(:position).sort).to eq([1, 1, 2, 2, 3])
588
+ end
589
+
590
+ it 'should correctly move items to a random position' do
591
+ 20.times.map do
592
+ Thread.new do
593
+ record = ScopedOrderable.all.sample
594
+ record.update_attributes move_to: (1..5).to_a.sample
595
+ end
596
+ end.each(&:join)
597
+
598
+ expect(ScopedOrderable.pluck(:position).sort).to eq([1, 1, 2, 2, 3])
599
+ end
600
+
601
+ # This spec fails randomly
602
+ it 'should correctly move items to a random scope', retry: 5 do
603
+ 20.times.map do
604
+ Thread.new do
605
+ record = ScopedOrderable.all.sample
606
+ group_id = ([1, 2, 3] - [record.group_id]).sample
607
+ record.update_attributes group_id: group_id
608
+ end
609
+ end.each(&:join)
610
+
611
+ result = ScopedOrderable.all.to_a.each_with_object({}) do |obj, hash|
612
+ hash[obj.group_id] ||= []
613
+ hash[obj.group_id] << obj.position
614
+ end
615
+
616
+ result.values.each do |ary|
617
+ expect(ary.sort).to eq((1..(ary.size)).to_a)
618
+ end
619
+ end
620
+
621
+ it 'should correctly move items to a random position and scope' do
622
+ 20.times.map do
623
+ Thread.new do
624
+ record = ScopedOrderable.all.sample
625
+ group_id = ([1, 2, 3] - [record.group_id]).sample
626
+ position = (1..5).to_a.sample
627
+ record.update_attributes group_id: group_id, move_to: position
628
+ end
629
+ end.each(&:join)
630
+
631
+ result = ScopedOrderable.all.to_a.each_with_object({}) do |obj, hash|
632
+ hash[obj.group_id] ||= []
633
+ hash[obj.group_id] << obj.position
634
+ end
635
+
636
+ result.values.each do |ary|
637
+ expect(ary.sort).to eq((1..(ary.size)).to_a)
638
+ end
639
+ end
640
+ end
641
+ end
642
+
643
+ describe StringScopedOrderable do
644
+ it 'uses the foreign key of the relationship as scope' do
645
+ orderable1 = StringScopedOrderable.create!(some_scope: 1)
646
+ orderable2 = StringScopedOrderable.create!(some_scope: 1)
647
+ orderable3 = StringScopedOrderable.create!(some_scope: 2)
648
+ expect(orderable1.position).to eq 1
649
+ expect(orderable2.position).to eq 2
650
+ expect(orderable3.position).to eq 1
651
+ end
652
+ end
653
+
654
+ describe EmbeddedOrderable do
655
+ before :each do
656
+ eo = EmbedsOrderable.create!
657
+ 2.times { eo.embedded_orderables.create! }
658
+ eo = EmbedsOrderable.create!
659
+ 3.times { eo.embedded_orderables.create! }
660
+ end
661
+
662
+ def positions
663
+ EmbedsOrderable.order_by(position: 1).all.map { |eo| eo.embedded_orderables.map(&:position).sort }
664
+ end
665
+
666
+ it 'sets proper position while creation' do
667
+ expect(positions).to eq([[1, 2], [1, 2, 3]])
668
+ end
669
+
670
+ it 'moves an item returned by a query to position' do
671
+ embedded_orderable1 = EmbedsOrderable.first.embedded_orderables.where(position: 1).first
672
+ embedded_orderable2 = EmbedsOrderable.first.embedded_orderables.where(position: 2).first
673
+ embedded_orderable1.move_to! 2
674
+ expect(embedded_orderable2.reload.position).to eq(1)
675
+ end
676
+ end
677
+
678
+ describe CustomizedOrderable do
679
+ it 'does not have default position field' do
680
+ expect(CustomizedOrderable.fields).not_to have_key('position')
681
+ end
682
+
683
+ it 'should have custom pos field' do
684
+ expect(CustomizedOrderable.fields).to have_key('pos')
685
+ end
686
+
687
+ it 'should have an alias my_position which points to pos field on Mongoid 3+' do
688
+ if CustomizedOrderable.respond_to?(:database_field_name)
689
+ expect(CustomizedOrderable.database_field_name('my_position')).to eq('pos')
690
+ end
691
+ end
692
+ end
693
+
694
+ describe NoIndexOrderable do
695
+ it 'should not have index on position field' do
696
+ expect(NoIndexOrderable.index_specifications.detect { |spec| spec.key == :position }).to be_nil
697
+ end
698
+ end
699
+
700
+ describe ZeroBasedOrderable do
701
+ before :each do
702
+ 5.times { ZeroBasedOrderable.create! }
703
+ end
704
+
705
+ def positions
706
+ ZeroBasedOrderable.pluck(:position).sort
707
+ end
708
+
709
+ it 'should have a orderable base of 0' do
710
+ expect(ZeroBasedOrderable.create!.orderable_top).to eq(0)
711
+ end
712
+
713
+ it 'should set proper position while creation' do
714
+ expect(positions).to eq([0, 1, 2, 3, 4])
715
+ end
716
+
717
+ describe 'reset position' do
718
+ before { ZeroBasedOrderable.update_all(position: nil) }
719
+ it 'should properly reset position' do
720
+ ZeroBasedOrderable.all.map(&:save)
721
+ expect(positions).to eq([0, 1, 2, 3, 4])
722
+ end
723
+ end
724
+
725
+ describe 'removement' do
726
+ it 'top' do
727
+ ZeroBasedOrderable.where(position: 0).destroy
728
+ expect(positions).to eq([0, 1, 2, 3])
729
+ end
730
+
731
+ it 'bottom' do
732
+ ZeroBasedOrderable.where(position: 4).destroy
733
+ expect(positions).to eq([0, 1, 2, 3])
734
+ end
735
+
736
+ it 'middle' do
737
+ ZeroBasedOrderable.where(position: 2).destroy
738
+ expect(positions).to eq([0, 1, 2, 3])
739
+ end
740
+ end
741
+
742
+ describe 'inserting' do
743
+ it 'top' do
744
+ newbie = ZeroBasedOrderable.create! move_to: :top
745
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
746
+ expect(newbie.position).to eq(0)
747
+ end
748
+
749
+ it 'bottom' do
750
+ newbie = ZeroBasedOrderable.create! move_to: :bottom
751
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
752
+ expect(newbie.position).to eq(5)
753
+ end
754
+
755
+ it 'middle' do
756
+ newbie = ZeroBasedOrderable.create! move_to: 3
757
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
758
+ expect(newbie.position).to eq(3)
759
+ end
760
+
761
+ it 'middle (with a numeric string)' do
762
+ newbie = ZeroBasedOrderable.create! move_to: '3'
763
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
764
+ expect(newbie.position).to eq(3)
765
+ end
766
+
767
+ it 'middle (with a non-numeric string)' do
768
+ expect do
769
+ ZeroBasedOrderable.create! move_to: 'three'
770
+ end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
771
+ end
772
+ end
773
+
774
+ describe 'movement' do
775
+ it 'higher from top' do
776
+ record = ZeroBasedOrderable.where(position: 0).first
777
+ record.update_attributes move_to: :higher
778
+ expect(positions).to eq([0, 1, 2, 3, 4])
779
+ expect(record.reload.position).to eq(0)
780
+ end
781
+
782
+ it 'higher from bottom' do
783
+ record = ZeroBasedOrderable.where(position: 4).first
784
+ record.update_attributes move_to: :higher
785
+ expect(positions).to eq([0, 1, 2, 3, 4])
786
+ expect(record.reload.position).to eq(3)
787
+ end
788
+
789
+ it 'higher from middle' do
790
+ record = ZeroBasedOrderable.where(position: 3).first
791
+ record.update_attributes move_to: :higher
792
+ expect(positions).to eq([0, 1, 2, 3, 4])
793
+ expect(record.reload.position).to eq(2)
794
+ end
795
+
796
+ it 'lower from top' do
797
+ record = ZeroBasedOrderable.where(position: 0).first
798
+ record.update_attributes move_to: :lower
799
+ expect(positions).to eq([0, 1, 2, 3, 4])
800
+ expect(record.reload.position).to eq(1)
801
+ end
802
+
803
+ it 'lower from bottom' do
804
+ record = ZeroBasedOrderable.where(position: 4).first
805
+ record.update_attributes move_to: :lower
806
+ expect(positions).to eq([0, 1, 2, 3, 4])
807
+ expect(record.reload.position).to eq(4)
808
+ end
809
+
810
+ it 'lower from middle' do
811
+ record = ZeroBasedOrderable.where(position: 2).first
812
+ record.update_attributes move_to: :lower
813
+ expect(positions).to eq([0, 1, 2, 3, 4])
814
+ expect(record.reload.position).to eq(3)
815
+ end
816
+
817
+ it 'does nothing if position not change' do
818
+ record = ZeroBasedOrderable.where(position: 3).first
819
+ record.save
820
+ expect(positions).to eq([0, 1, 2, 3, 4])
821
+ expect(record.reload.position).to eq(3)
822
+ end
823
+ end
824
+
825
+ describe 'utility methods' do
826
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
827
+ record1 = ZeroBasedOrderable.where(position: 0).first
828
+ record2 = ZeroBasedOrderable.where(position: 1).first
829
+ record3 = ZeroBasedOrderable.where(position: 2).first
830
+ record4 = ZeroBasedOrderable.where(position: 3).first
831
+ record5 = ZeroBasedOrderable.where(position: 4).first
832
+ expect(record1.next_items.to_a).to eq([record2, record3, record4, record5])
833
+ expect(record5.previous_items.to_a).to eq([record1, record2, record3, record4])
834
+ expect(record3.previous_items.to_a).to eq([record1, record2])
835
+ expect(record3.next_items.to_a).to eq([record4, record5])
836
+ expect(record1.next_item).to eq(record2)
837
+ expect(record2.previous_item).to eq(record1)
838
+ expect(record1.previous_item).to eq(nil)
839
+ expect(record5.next_item).to eq(nil)
840
+ end
841
+ end
842
+ end
843
+
844
+ describe Fruit do
845
+ it 'should set proper position' do
846
+ fruit1 = Apple.create
847
+ fruit2 = Orange.create
848
+ expect(fruit1.position).to eq(1)
849
+ expect(fruit2.position).to eq(2)
850
+ end
851
+
852
+ describe 'movement' do
853
+ before :each do
854
+ 5.times { Apple.create! }
855
+ end
856
+
857
+ it 'with symbol position' do
858
+ first_apple = Apple.asc(:_id).first
859
+ top_pos = first_apple.position
860
+ bottom_pos = Apple.asc(:_id).last.position
861
+ expect do
862
+ first_apple.move_to! :bottom
863
+ end.to change(first_apple, :position).from(top_pos).to bottom_pos
864
+ end
865
+
866
+ it 'with point position' do
867
+ first_apple = Apple.asc(:_id).first
868
+ top_pos = first_apple.position
869
+ bottom_pos = Apple.asc(:_id).last.position
870
+ expect do
871
+ first_apple.move_to! bottom_pos
872
+ end.to change(first_apple, :position).from(top_pos).to bottom_pos
873
+ end
874
+ end
875
+
876
+ describe 'add orderable configs in inherited class' do
877
+ it 'does not affect the orderable configs of parent class and sibling class' do
878
+ class Apple
879
+ orderable field: :serial
880
+ end
881
+ expect(Fruit.orderable_configs).not_to eq Apple.orderable_configs
882
+ expect(Orange.orderable_configs).not_to eq Apple.orderable_configs
883
+ expect(Fruit.orderable_configs).to eq Orange.orderable_configs
884
+ end
885
+ end
886
+ end
887
+
888
+ describe ForeignKeyDiffersOrderable do
889
+ it 'uses the foreign key of the relationship as scope' do
890
+ orderable1, orderable2, orderable3 = nil
891
+ parent_scope1 = ForeignKeyDiffersOrderable.create
892
+ parent_scope2 = ForeignKeyDiffersOrderable.create
893
+ expect do
894
+ orderable1 = ForeignKeyDiffersOrderable.create!(different_scope: parent_scope1)
895
+ orderable2 = ForeignKeyDiffersOrderable.create!(different_scope: parent_scope1)
896
+ orderable3 = ForeignKeyDiffersOrderable.create!(different_scope: parent_scope2)
897
+ end.to_not raise_error
898
+ expect(orderable1.position).to eq 1
899
+ expect(orderable2.position).to eq 2
900
+ expect(orderable3.position).to eq 1
901
+ end
902
+ end
903
+
904
+ describe MultipleFieldsOrderable do
905
+ before :each do
906
+ 5.times { MultipleFieldsOrderable.create! }
907
+ end
908
+
909
+ context 'default orderable' do
910
+ let(:serial_nos) { MultipleFieldsOrderable.pluck(:serial_no).sort }
911
+
912
+ describe 'inserting' do
913
+ let(:newbie) { MultipleFieldsOrderable.create! }
914
+
915
+ before { @position = newbie.position }
916
+
917
+ it 'top' do
918
+ newbie.move_to! :top
919
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
920
+ expect(newbie.serial_no).to eq(1)
921
+ expect(newbie.position).to eq(@position)
922
+ end
923
+
924
+ it 'bottom' do
925
+ newbie.move_to! :bottom
926
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
927
+ expect(newbie.serial_no).to eq(6)
928
+ expect(newbie.position).to eq(@position)
929
+ end
930
+
931
+ it 'middle' do
932
+ newbie.move_to! 4
933
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
934
+ expect(newbie.serial_no).to eq(4)
935
+ expect(newbie.position).to eq(@position)
936
+ end
937
+ end
938
+
939
+ describe 'movement' do
940
+ it 'higher from top' do
941
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
942
+ position = record.position
943
+ record.move_higher!
944
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
945
+ expect(record.serial_no).to eq(1)
946
+ expect(record.position).to eq(position)
947
+ end
948
+
949
+ it 'higher from bottom' do
950
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
951
+ position = record.position
952
+ record.move_higher!
953
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
954
+ expect(record.serial_no).to eq(4)
955
+ expect(record.position).to eq(position)
956
+ end
957
+
958
+ it 'higher from middle' do
959
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
960
+ position = record.position
961
+ record.move_higher!
962
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
963
+ expect(record.serial_no).to eq(2)
964
+ expect(record.position).to eq(position)
965
+ end
966
+
967
+ it 'lower from top' do
968
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
969
+ position = record.position
970
+ record.move_lower!
971
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
972
+ expect(record.serial_no).to eq(2)
973
+ expect(record.position).to eq(position)
974
+ end
975
+
976
+ it 'lower from bottom' do
977
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
978
+ position = record.position
979
+ record.move_lower!
980
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
981
+ expect(record.serial_no).to eq(5)
982
+ expect(record.position).to eq(position)
983
+ end
984
+
985
+ it 'lower from middle' do
986
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
987
+ position = record.position
988
+ record.move_lower!
989
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
990
+ expect(record.serial_no).to eq(4)
991
+ expect(record.position).to eq(position)
992
+ end
993
+ end
994
+
995
+ describe 'utility methods' do
996
+ before do
997
+ @record1 = MultipleFieldsOrderable.where(serial_no: 1).first
998
+ @record2 = MultipleFieldsOrderable.where(serial_no: 2).first
999
+ @record3 = MultipleFieldsOrderable.where(serial_no: 3).first
1000
+ @record4 = MultipleFieldsOrderable.where(serial_no: 4).first
1001
+ @record5 = MultipleFieldsOrderable.where(serial_no: 5).first
1002
+ end
1003
+
1004
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
1005
+ expect(@record1.next_item).to eq(@record2)
1006
+ expect(@record3.next_item).to eq(@record4)
1007
+ expect(@record5.next_item).to eq(nil)
1008
+ expect(@record1.prev_item).to eq(nil)
1009
+ expect(@record3.prev_item).to eq(@record2)
1010
+ expect(@record5.prev_item).to eq(@record4)
1011
+ end
1012
+
1013
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
1014
+ expect(@record1.next_items.to_a).to eq([@record2, @record3, @record4, @record5])
1015
+ expect(@record3.next_items.to_a).to eq([@record4, @record5])
1016
+ expect(@record5.next_items.to_a).to eq([])
1017
+ expect(@record1.previous_items.to_a).to eq([])
1018
+ expect(@record3.previous_items.to_a).to eq([@record1, @record2])
1019
+ expect(@record5.previous_items.to_a).to eq([@record1, @record2, @record3, @record4])
1020
+ end
1021
+ end
1022
+ end
1023
+
1024
+ context 'serial_no orderable' do
1025
+ let(:serial_nos) { MultipleFieldsOrderable.pluck(:serial_no).sort }
1026
+
1027
+ it 'should have proper serial_no field' do
1028
+ expect(MultipleFieldsOrderable.fields.key?('serial_no')).to be true
1029
+ expect(MultipleFieldsOrderable.fields['serial_no'].options[:type]).to eq(Integer)
1030
+ end
1031
+
1032
+ it 'should have index on serial_no field' do
1033
+ expect(MultipleFieldsOrderable.index_specifications.detect { |spec| spec.key == { serial_no: 1 } }).not_to be_nil
1034
+ end
1035
+
1036
+ it 'should have a orderable base of 1' do
1037
+ expect(MultipleFieldsOrderable.first.orderable_top(:serial_no)).to eq(1)
1038
+ end
1039
+
1040
+ it 'should set proper position while creation' do
1041
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
1042
+ end
1043
+
1044
+ describe 'removement' do
1045
+ it 'top' do
1046
+ MultipleFieldsOrderable.where(serial_no: 1).destroy
1047
+ expect(serial_nos).to eq([1, 2, 3, 4])
1048
+ end
1049
+
1050
+ it 'bottom' do
1051
+ MultipleFieldsOrderable.where(serial_no: 5).destroy
1052
+ expect(serial_nos).to eq([1, 2, 3, 4])
1053
+ end
1054
+
1055
+ it 'middle' do
1056
+ MultipleFieldsOrderable.where(serial_no: 3).destroy
1057
+ expect(serial_nos).to eq([1, 2, 3, 4])
1058
+ end
1059
+ end
1060
+
1061
+ describe 'inserting' do
1062
+ let(:newbie) { MultipleFieldsOrderable.create! }
1063
+
1064
+ before { @position = newbie.position }
1065
+
1066
+ it 'top' do
1067
+ newbie.move_serial_no_to! :top
1068
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
1069
+ expect(newbie.serial_no).to eq(1)
1070
+ expect(newbie.position).to eq(@position)
1071
+ end
1072
+
1073
+ it 'bottom' do
1074
+ newbie.move_serial_no_to! :bottom
1075
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
1076
+ expect(newbie.serial_no).to eq(6)
1077
+ expect(newbie.position).to eq(@position)
1078
+ end
1079
+
1080
+ it 'middle' do
1081
+ newbie.move_serial_no_to! 4
1082
+ expect(serial_nos).to eq([1, 2, 3, 4, 5, 6])
1083
+ expect(newbie.serial_no).to eq(4)
1084
+ expect(newbie.position).to eq(@position)
1085
+ end
1086
+ end
1087
+
1088
+ describe 'movement' do
1089
+ it 'higher from top' do
1090
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
1091
+ position = record.position
1092
+ record.move_serial_no_higher!
1093
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
1094
+ expect(record.serial_no).to eq(1)
1095
+ expect(record.position).to eq(position)
1096
+ end
1097
+
1098
+ it 'higher from bottom' do
1099
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
1100
+ position = record.position
1101
+ record.move_serial_no_higher!
1102
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
1103
+ expect(record.serial_no).to eq(4)
1104
+ expect(record.position).to eq(position)
1105
+ end
1106
+
1107
+ it 'higher from middle' do
1108
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
1109
+ position = record.position
1110
+ record.move_serial_no_higher!
1111
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
1112
+ expect(record.serial_no).to eq(2)
1113
+ expect(record.position).to eq(position)
1114
+ end
1115
+
1116
+ it 'lower from top' do
1117
+ record = MultipleFieldsOrderable.where(serial_no: 1).first
1118
+ position = record.position
1119
+ record.move_serial_no_lower!
1120
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
1121
+ expect(record.serial_no).to eq(2)
1122
+ expect(record.position).to eq(position)
1123
+ end
1124
+
1125
+ it 'lower from bottom' do
1126
+ record = MultipleFieldsOrderable.where(serial_no: 5).first
1127
+ position = record.position
1128
+ record.move_serial_no_lower!
1129
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
1130
+ expect(record.serial_no).to eq(5)
1131
+ expect(record.position).to eq(position)
1132
+ end
1133
+
1134
+ it 'lower from middle' do
1135
+ record = MultipleFieldsOrderable.where(serial_no: 3).first
1136
+ position = record.position
1137
+ record.move_serial_no_lower!
1138
+ expect(serial_nos).to eq([1, 2, 3, 4, 5])
1139
+ expect(record.serial_no).to eq(4)
1140
+ expect(record.position).to eq(position)
1141
+ end
1142
+ end
1143
+
1144
+ describe 'utility methods' do
1145
+ before do
1146
+ @record1 = MultipleFieldsOrderable.where(serial_no: 1).first
1147
+ @record2 = MultipleFieldsOrderable.where(serial_no: 2).first
1148
+ @record3 = MultipleFieldsOrderable.where(serial_no: 3).first
1149
+ @record4 = MultipleFieldsOrderable.where(serial_no: 4).first
1150
+ @record5 = MultipleFieldsOrderable.where(serial_no: 5).first
1151
+ end
1152
+
1153
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
1154
+ expect(@record1.next_serial_no_item).to eq(@record2)
1155
+ expect(@record3.next_serial_no_item).to eq(@record4)
1156
+ expect(@record5.next_serial_no_item).to eq(nil)
1157
+ expect(@record1.prev_serial_no_item).to eq(nil)
1158
+ expect(@record3.prev_serial_no_item).to eq(@record2)
1159
+ expect(@record5.prev_serial_no_item).to eq(@record4)
1160
+ end
1161
+
1162
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
1163
+ expect(@record1.next_serial_no_items.to_a).to eq([@record2, @record3, @record4, @record5])
1164
+ expect(@record3.next_serial_no_items.to_a).to eq([@record4, @record5])
1165
+ expect(@record5.next_serial_no_items.to_a).to eq([])
1166
+ expect(@record1.previous_serial_no_items.to_a).to eq([])
1167
+ expect(@record3.previous_serial_no_items.to_a).to eq([@record1, @record2])
1168
+ expect(@record5.previous_serial_no_items.to_a).to eq([@record1, @record2, @record3, @record4])
1169
+ end
1170
+ end
1171
+ end
1172
+
1173
+ context 'position orderable' do
1174
+ let(:positions) { MultipleFieldsOrderable.pluck(:position).sort }
1175
+
1176
+ it 'should not have default position field' do
1177
+ expect(MultipleFieldsOrderable.fields).not_to have_key('position')
1178
+ end
1179
+
1180
+ it 'should have custom pos field' do
1181
+ expect(MultipleFieldsOrderable.fields).to have_key('pos')
1182
+ expect(MultipleFieldsOrderable.fields['pos'].options[:type]).to eq(Integer)
1183
+ end
1184
+
1185
+ it 'should have index on position field' do
1186
+ expect(MultipleFieldsOrderable.index_specifications.detect { |spec| spec.key == { position: 1 } }).to be_nil
1187
+ end
1188
+
1189
+ it 'should have a orderable base of 0' do
1190
+ expect(MultipleFieldsOrderable.first.orderable_top(:position)).to eq(0)
1191
+ end
1192
+
1193
+ it 'should set proper position while creation' do
1194
+ expect(positions).to eq([0, 1, 2, 3, 4])
1195
+ end
1196
+
1197
+ describe 'removement' do
1198
+ it 'top' do
1199
+ MultipleFieldsOrderable.where(pos: 1).destroy
1200
+ expect(positions).to eq([0, 1, 2, 3])
1201
+ end
1202
+
1203
+ it 'bottom' do
1204
+ MultipleFieldsOrderable.where(pos: 4).destroy
1205
+ expect(positions).to eq([0, 1, 2, 3])
1206
+ end
1207
+
1208
+ it 'middle' do
1209
+ MultipleFieldsOrderable.where(pos: 3).destroy
1210
+ expect(positions).to eq([0, 1, 2, 3])
1211
+ end
1212
+ end
1213
+
1214
+ describe 'inserting' do
1215
+ let(:newbie) { MultipleFieldsOrderable.create! }
1216
+
1217
+ before { @serial_no = newbie.serial_no }
1218
+
1219
+ it 'top' do
1220
+ newbie.move_position_to! :top
1221
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
1222
+ expect(newbie.position).to eq(0)
1223
+ expect(newbie.serial_no).to eq(@serial_no)
1224
+ end
1225
+
1226
+ it 'bottom' do
1227
+ newbie.move_position_to! :bottom
1228
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
1229
+ expect(newbie.position).to eq(5)
1230
+ expect(newbie.serial_no).to eq(@serial_no)
1231
+ end
1232
+
1233
+ it 'middle' do
1234
+ newbie.move_position_to! 4
1235
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
1236
+ expect(newbie.position).to eq(4)
1237
+ expect(newbie.serial_no).to eq(@serial_no)
1238
+ end
1239
+ end
1240
+
1241
+ describe 'movement' do
1242
+ it 'higher from top' do
1243
+ record = MultipleFieldsOrderable.where(pos: 0).first
1244
+ position = record.serial_no
1245
+ record.move_position_higher!
1246
+ expect(positions).to eq([0, 1, 2, 3, 4])
1247
+ expect(record.position).to eq(0)
1248
+ expect(record.serial_no).to eq(position)
1249
+ end
1250
+
1251
+ it 'higher from bottom' do
1252
+ record = MultipleFieldsOrderable.where(pos: 4).first
1253
+ position = record.serial_no
1254
+ record.move_position_higher!
1255
+ expect(positions).to eq([0, 1, 2, 3, 4])
1256
+ expect(record.position).to eq(3)
1257
+ expect(record.serial_no).to eq(position)
1258
+ end
1259
+
1260
+ it 'higher from middle' do
1261
+ record = MultipleFieldsOrderable.where(pos: 3).first
1262
+ position = record.serial_no
1263
+ record.move_position_higher!
1264
+ expect(positions).to eq([0, 1, 2, 3, 4])
1265
+ expect(record.position).to eq(2)
1266
+ expect(record.serial_no).to eq(position)
1267
+ end
1268
+
1269
+ it 'lower from top' do
1270
+ record = MultipleFieldsOrderable.where(pos: 0).first
1271
+ position = record.serial_no
1272
+ record.move_position_lower!
1273
+ expect(positions).to eq([0, 1, 2, 3, 4])
1274
+ expect(record.position).to eq(1)
1275
+ expect(record.serial_no).to eq(position)
1276
+ end
1277
+
1278
+ it 'lower from bottom' do
1279
+ record = MultipleFieldsOrderable.where(pos: 4).first
1280
+ position = record.serial_no
1281
+ record.move_position_lower!
1282
+ expect(positions).to eq([0, 1, 2, 3, 4])
1283
+ expect(record.position).to eq(4)
1284
+ expect(record.serial_no).to eq(position)
1285
+ end
1286
+
1287
+ it 'lower from middle' do
1288
+ record = MultipleFieldsOrderable.where(pos: 3).first
1289
+ position = record.serial_no
1290
+ record.move_position_lower!
1291
+ expect(positions).to eq([0, 1, 2, 3, 4])
1292
+ expect(record.position).to eq(4)
1293
+ expect(record.serial_no).to eq(position)
1294
+ end
1295
+ end
1296
+
1297
+ describe 'utility methods' do
1298
+ before do
1299
+ @record1 = MultipleFieldsOrderable.where(pos: 0).first
1300
+ @record2 = MultipleFieldsOrderable.where(pos: 1).first
1301
+ @record3 = MultipleFieldsOrderable.where(pos: 2).first
1302
+ @record4 = MultipleFieldsOrderable.where(pos: 3).first
1303
+ @record5 = MultipleFieldsOrderable.where(pos: 4).first
1304
+ end
1305
+
1306
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
1307
+ expect(@record1.next_position_item).to eq(@record2)
1308
+ expect(@record3.next_position_item).to eq(@record4)
1309
+ expect(@record5.next_position_item).to eq(nil)
1310
+ expect(@record1.prev_position_item).to eq(nil)
1311
+ expect(@record3.prev_position_item).to eq(@record2)
1312
+ expect(@record5.prev_position_item).to eq(@record4)
1313
+ end
1314
+
1315
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
1316
+ expect(@record1.next_position_items.to_a).to eq([@record2, @record3, @record4, @record5])
1317
+ expect(@record3.next_position_items.to_a).to eq([@record4, @record5])
1318
+ expect(@record5.next_position_items.to_a).to eq([])
1319
+ expect(@record1.previous_position_items.to_a).to eq([])
1320
+ expect(@record3.previous_position_items.to_a).to eq([@record1, @record2])
1321
+ expect(@record5.previous_position_items.to_a).to eq([@record1, @record2, @record3, @record4])
1322
+ end
1323
+ end
1324
+ end
1325
+
1326
+ context 'group_count orderable' do
1327
+ before :each do
1328
+ MultipleFieldsOrderable.delete_all
1329
+ 2.times { MultipleFieldsOrderable.create! group_id: 1 }
1330
+ 3.times { MultipleFieldsOrderable.create! group_id: 2 }
1331
+ end
1332
+
1333
+ let(:all_groups) { MultipleFieldsOrderable.order_by([:group_id, :asc], [:groups, :asc]).map(&:groups) }
1334
+
1335
+ it 'should set proper position while creation' do
1336
+ expect(all_groups).to eq([1, 2, 1, 2, 3])
1337
+ end
1338
+
1339
+ describe 'removement' do
1340
+ it 'top' do
1341
+ MultipleFieldsOrderable.where(groups: 1, group_id: 1).destroy
1342
+ expect(all_groups).to eq([1, 1, 2, 3])
1343
+ end
1344
+
1345
+ it 'bottom' do
1346
+ MultipleFieldsOrderable.where(groups: 3, group_id: 2).destroy
1347
+ expect(all_groups).to eq([1, 2, 1, 2])
1348
+ end
1349
+
1350
+ it 'middle' do
1351
+ MultipleFieldsOrderable.where(groups: 2, group_id: 2).destroy
1352
+ expect(all_groups).to eq([1, 2, 1, 2])
1353
+ end
1354
+ end
1355
+
1356
+ describe 'inserting' do
1357
+ it 'top' do
1358
+ newbie = MultipleFieldsOrderable.create! group_id: 1
1359
+ newbie.move_groups_to! :top
1360
+ expect(all_groups).to eq([1, 2, 3, 1, 2, 3])
1361
+ expect(newbie.groups).to eq(1)
1362
+ end
1363
+
1364
+ it 'bottom' do
1365
+ newbie = MultipleFieldsOrderable.create! group_id: 2
1366
+ newbie.move_groups_to! :bottom
1367
+ expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
1368
+ expect(newbie.groups).to eq(4)
1369
+ end
1370
+
1371
+ it 'middle' do
1372
+ newbie = MultipleFieldsOrderable.create! group_id: 2
1373
+ newbie.move_groups_to! 2
1374
+ expect(all_groups).to eq([1, 2, 1, 2, 3, 4])
1375
+ expect(newbie.groups).to eq(2)
1376
+ end
1377
+ end
1378
+
1379
+ describe 'scope movement' do
1380
+ let(:record) { MultipleFieldsOrderable.where(group_id: 2, groups: 2).first }
1381
+
1382
+ it 'to a new scope group' do
1383
+ record.update_attributes group_id: 3
1384
+ expect(all_groups).to eq([1, 2, 1, 2, 1])
1385
+ expect(record.groups).to eq(1)
1386
+ end
1387
+
1388
+ context 'when moving to an existing scope group' do
1389
+ it 'without a position' do
1390
+ record.update_attributes group_id: 1
1391
+ expect(all_groups).to eq([1, 2, 3, 1, 2])
1392
+ expect(record.reload.groups).to eq(3)
1393
+ end
1394
+
1395
+ it 'with symbol position' do
1396
+ record.update_attributes group_id: 1
1397
+ record.move_groups_to! :top
1398
+ expect(all_groups).to eq([1, 2, 3, 1, 2])
1399
+ expect(record.reload.groups).to eq(1)
1400
+ end
1401
+
1402
+ it 'with point position' do
1403
+ record.update_attributes group_id: 1
1404
+ record.move_groups_to! 2
1405
+ expect(all_groups).to eq([1, 2, 3, 1, 2])
1406
+ expect(record.reload.groups).to eq(2)
1407
+ end
1408
+ end
1409
+ end
1410
+
1411
+ describe 'utility methods' do
1412
+ before do
1413
+ @record1 = MultipleFieldsOrderable.where(group_id: 2, groups: 1).first
1414
+ @record2 = MultipleFieldsOrderable.where(group_id: 2, groups: 2).first
1415
+ @record3 = MultipleFieldsOrderable.where(group_id: 2, groups: 3).first
1416
+ @record4 = MultipleFieldsOrderable.where(group_id: 1, groups: 1).first
1417
+ @record5 = MultipleFieldsOrderable.where(group_id: 1, groups: 2).first
1418
+ end
1419
+
1420
+ it 'should return the lower/higher item on the list for next_item/previous_item' do
1421
+ expect(@record1.next_groups_item).to eq(@record2)
1422
+ expect(@record4.next_groups_item).to eq(@record5)
1423
+ expect(@record3.next_groups_item).to eq(nil)
1424
+ expect(@record1.prev_groups_item).to eq(nil)
1425
+ expect(@record3.prev_groups_item).to eq(@record2)
1426
+ expect(@record5.prev_groups_item).to eq(@record4)
1427
+ end
1428
+
1429
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
1430
+ expect(@record1.next_groups_items.to_a).to eq([@record2, @record3])
1431
+ expect(@record3.next_groups_items.to_a).to eq([])
1432
+ expect(@record4.next_groups_items.to_a).to eq([@record5])
1433
+ expect(@record1.previous_groups_items.to_a).to eq([])
1434
+ expect(@record3.previous_groups_items.to_a).to eq([@record1, @record2])
1435
+ expect(@record5.previous_groups_items.to_a).to eq([@record4])
1436
+ end
1437
+ end
1438
+ end
1439
+ end
1440
+
1441
+ describe MultipleScopedOrderable do
1442
+ before :each do
1443
+ 3.times do
1444
+ Apple.create
1445
+ Orange.create
1446
+ end
1447
+ MultipleScopedOrderable.create! apple_id: 1, orange_id: 1
1448
+ MultipleScopedOrderable.create! apple_id: 2, orange_id: 1
1449
+ MultipleScopedOrderable.create! apple_id: 2, orange_id: 2
1450
+ MultipleScopedOrderable.create! apple_id: 1, orange_id: 3
1451
+ MultipleScopedOrderable.create! apple_id: 1, orange_id: 1
1452
+ MultipleScopedOrderable.create! apple_id: 3, orange_id: 3
1453
+ MultipleScopedOrderable.create! apple_id: 2, orange_id: 3
1454
+ MultipleScopedOrderable.create! apple_id: 3, orange_id: 2
1455
+ MultipleScopedOrderable.create! apple_id: 1, orange_id: 3
1456
+ end
1457
+
1458
+ def apple_positions
1459
+ MultipleScopedOrderable.order_by([:apple_id, :asc], [:posa, :asc]).map(&:posa)
1460
+ end
1461
+
1462
+ def orange_positions
1463
+ MultipleScopedOrderable.order_by([:orange_id, :asc], [:poso, :asc]).map(&:poso)
1464
+ end
1465
+
1466
+ describe 'default positions' do
1467
+ it { expect(apple_positions).to eq([1, 2, 3, 4, 1, 2, 3, 1, 2]) }
1468
+ it { expect(orange_positions).to eq([1, 2, 3, 1, 2, 1, 2, 3, 4]) }
1469
+ end
1470
+
1471
+ describe 'change the scope of the apple' do
1472
+ let(:record) { MultipleScopedOrderable.first }
1473
+ before do
1474
+ record.update_attribute(:apple_id, 2)
1475
+ end
1476
+
1477
+ it 'should properly set the apple positions' do
1478
+ expect(apple_positions).to eq([1, 2, 3, 1, 2, 3, 4, 1, 2])
1479
+ end
1480
+
1481
+ it 'should not affect the orange positions' do
1482
+ expect(orange_positions).to eq([1, 2, 3, 1, 2, 1, 2, 3, 4])
1483
+ end
1484
+ end
1485
+ end
1486
+ end