mongoid_orderable 4.1.1 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
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