mongoid_orderable 6.0.1 → 6.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZeroBasedOrderable do
4
+
5
+ shared_examples_for 'zero_based_orderable' do
6
+
7
+ before :each do
8
+ 5.times { ZeroBasedOrderable.create! }
9
+ end
10
+
11
+ def positions
12
+ ZeroBasedOrderable.pluck(:position).sort
13
+ end
14
+
15
+ it 'should have a orderable base of 0' do
16
+ expect(ZeroBasedOrderable.create!.orderable_top).to eq(0)
17
+ end
18
+
19
+ it 'should set proper position while creation' do
20
+ expect(positions).to eq([0, 1, 2, 3, 4])
21
+ end
22
+
23
+ describe 'reset position' do
24
+ before { ZeroBasedOrderable.update_all(position: nil) }
25
+ it 'should properly reset position' do
26
+ ZeroBasedOrderable.all.map(&:save)
27
+ expect(positions).to eq([0, 1, 2, 3, 4])
28
+ end
29
+ end
30
+
31
+ describe 'removement' do
32
+ it 'top' do
33
+ ZeroBasedOrderable.where(position: 0).destroy
34
+ expect(positions).to eq([0, 1, 2, 3])
35
+ end
36
+
37
+ it 'bottom' do
38
+ ZeroBasedOrderable.where(position: 4).destroy
39
+ expect(positions).to eq([0, 1, 2, 3])
40
+ end
41
+
42
+ it 'middle' do
43
+ ZeroBasedOrderable.where(position: 2).destroy
44
+ expect(positions).to eq([0, 1, 2, 3])
45
+ end
46
+ end
47
+
48
+ describe 'inserting' do
49
+ it 'top' do
50
+ newbie = ZeroBasedOrderable.create! move_to: :top
51
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
52
+ expect(newbie.position).to eq(0)
53
+ end
54
+
55
+ it 'bottom' do
56
+ newbie = ZeroBasedOrderable.create! move_to: :bottom
57
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
58
+ expect(newbie.position).to eq(5)
59
+ end
60
+
61
+ it 'middle' do
62
+ newbie = ZeroBasedOrderable.create! move_to: 3
63
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
64
+ expect(newbie.position).to eq(3)
65
+ end
66
+
67
+ it 'middle (with a numeric string)' do
68
+ newbie = ZeroBasedOrderable.create! move_to: '3'
69
+ expect(positions).to eq([0, 1, 2, 3, 4, 5])
70
+ expect(newbie.position).to eq(3)
71
+ end
72
+
73
+ it 'middle (with a non-numeric string)' do
74
+ expect do
75
+ ZeroBasedOrderable.create! move_to: 'three'
76
+ end.to raise_error Mongoid::Orderable::Errors::InvalidTargetPosition
77
+ end
78
+ end
79
+
80
+ describe 'movement' do
81
+ it 'higher from top' do
82
+ record = ZeroBasedOrderable.where(position: 0).first
83
+ record.update_attributes move_to: :higher
84
+ expect(positions).to eq([0, 1, 2, 3, 4])
85
+ expect(record.reload.position).to eq(0)
86
+ end
87
+
88
+ it 'higher from bottom' do
89
+ record = ZeroBasedOrderable.where(position: 4).first
90
+ record.update_attributes move_to: :higher
91
+ expect(positions).to eq([0, 1, 2, 3, 4])
92
+ expect(record.reload.position).to eq(3)
93
+ end
94
+
95
+ it 'higher from middle' do
96
+ record = ZeroBasedOrderable.where(position: 3).first
97
+ record.update_attributes move_to: :higher
98
+ expect(positions).to eq([0, 1, 2, 3, 4])
99
+ expect(record.reload.position).to eq(2)
100
+ end
101
+
102
+ it 'lower from top' do
103
+ record = ZeroBasedOrderable.where(position: 0).first
104
+ record.update_attributes move_to: :lower
105
+ expect(positions).to eq([0, 1, 2, 3, 4])
106
+ expect(record.reload.position).to eq(1)
107
+ end
108
+
109
+ it 'lower from bottom' do
110
+ record = ZeroBasedOrderable.where(position: 4).first
111
+ record.update_attributes move_to: :lower
112
+ expect(positions).to eq([0, 1, 2, 3, 4])
113
+ expect(record.reload.position).to eq(4)
114
+ end
115
+
116
+ it 'lower from middle' do
117
+ record = ZeroBasedOrderable.where(position: 2).first
118
+ record.update_attributes move_to: :lower
119
+ expect(positions).to eq([0, 1, 2, 3, 4])
120
+ expect(record.reload.position).to eq(3)
121
+ end
122
+
123
+ it 'does nothing if position not change' do
124
+ record = ZeroBasedOrderable.where(position: 3).first
125
+ record.save
126
+ expect(positions).to eq([0, 1, 2, 3, 4])
127
+ expect(record.reload.position).to eq(3)
128
+ end
129
+ end
130
+
131
+ describe 'utility methods' do
132
+ it 'should return a collection of items lower/higher on the list for next_items/previous_items' do
133
+ record1 = ZeroBasedOrderable.where(position: 0).first
134
+ record2 = ZeroBasedOrderable.where(position: 1).first
135
+ record3 = ZeroBasedOrderable.where(position: 2).first
136
+ record4 = ZeroBasedOrderable.where(position: 3).first
137
+ record5 = ZeroBasedOrderable.where(position: 4).first
138
+ expect(record1.next_items.to_a).to eq([record2, record3, record4, record5])
139
+ expect(record5.previous_items.to_a).to eq([record1, record2, record3, record4])
140
+ expect(record3.previous_items.to_a).to eq([record1, record2])
141
+ expect(record3.next_items.to_a).to eq([record4, record5])
142
+ expect(record1.next_item).to eq(record2)
143
+ expect(record2.previous_item).to eq(record1)
144
+ expect(record1.previous_item).to eq(nil)
145
+ expect(record5.next_item).to eq(nil)
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'with transactions' do
151
+ enable_transactions!
152
+
153
+ it_behaves_like 'zero_based_orderable'
154
+ end
155
+
156
+ context 'without transactions' do
157
+ disable_transactions!
158
+
159
+ it_behaves_like 'zero_based_orderable'
160
+ end
161
+ end
@@ -12,6 +12,8 @@ Mongo::Logger.logger.level = Logger::INFO
12
12
  Mongoid::Config.belongs_to_required_by_default = false
13
13
 
14
14
  RSpec.configure do |config|
15
+ config.order = 'random'
16
+
15
17
  config.before(:each) do
16
18
  Mongoid.purge!
17
19
  Mongoid.models.each do |model|
@@ -19,3 +21,22 @@ RSpec.configure do |config|
19
21
  end
20
22
  end
21
23
  end
24
+
25
+ require_relative 'support/models'
26
+
27
+ def set_transactions(enabled)
28
+ Mongoid.models.each do |model|
29
+ next unless model.respond_to?(:orderable_configs)
30
+ model.orderable_configs.values.each do |config|
31
+ config[:use_transactions] = enabled
32
+ end
33
+ end
34
+ end
35
+
36
+ def enable_transactions!
37
+ before { set_transactions(true) }
38
+ end
39
+
40
+ def disable_transactions!
41
+ before { set_transactions(false) }
42
+ end
@@ -0,0 +1,122 @@
1
+
2
+
3
+ Mongoid::Orderable.configure do |c|
4
+ c.use_transactions = true
5
+ c.transaction_max_retries = 100
6
+ c.lock_collection = :foo_bar_locks
7
+ end
8
+
9
+ class SimpleOrderable
10
+ include Mongoid::Document
11
+ include Mongoid::Orderable
12
+
13
+ orderable
14
+ end
15
+
16
+ class ScopedGroup
17
+ include Mongoid::Document
18
+
19
+ has_many :scoped_orderables
20
+ has_many :multiple_fields_orderables
21
+ end
22
+
23
+ class ScopedOrderable
24
+ include Mongoid::Document
25
+ include Mongoid::Orderable
26
+
27
+ belongs_to :group, class_name: 'ScopedGroup', optional: true
28
+
29
+ orderable scope: :group
30
+ end
31
+
32
+ class StringScopedOrderable
33
+ include Mongoid::Document
34
+ include Mongoid::Orderable
35
+
36
+ field :some_scope, type: Integer
37
+
38
+ orderable scope: 'some_scope'
39
+ end
40
+
41
+ class EmbedsOrderable
42
+ include Mongoid::Document
43
+
44
+ embeds_many :embedded_orderables
45
+ end
46
+
47
+ class EmbeddedOrderable
48
+ include Mongoid::Document
49
+ include Mongoid::Orderable
50
+
51
+ embedded_in :embeds_orderable
52
+
53
+ orderable
54
+ end
55
+
56
+ class CustomizedOrderable
57
+ include Mongoid::Document
58
+ include Mongoid::Orderable
59
+
60
+ orderable field: :pos, as: :my_position
61
+ end
62
+
63
+ class NoIndexOrderable
64
+ include Mongoid::Document
65
+ include Mongoid::Orderable
66
+
67
+ orderable index: false
68
+ end
69
+
70
+ class ZeroBasedOrderable
71
+ include Mongoid::Document
72
+ include Mongoid::Orderable
73
+
74
+ orderable base: 0
75
+ end
76
+
77
+ class InheritedOrderable
78
+ include Mongoid::Document
79
+ include Mongoid::Orderable
80
+
81
+ orderable inherited: true
82
+ end
83
+
84
+ class Apple < InheritedOrderable
85
+ orderable field: :serial
86
+ end
87
+
88
+ class Orange < InheritedOrderable
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
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_orderable
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.1
4
+ version: 6.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - pyromaniac
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-26 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -95,7 +95,6 @@ files:
95
95
  - lib/mongoid/orderable.rb
96
96
  - lib/mongoid/orderable/configs/field_config.rb
97
97
  - lib/mongoid/orderable/configs/global_config.rb
98
- - lib/mongoid/orderable/engine.rb
99
98
  - lib/mongoid/orderable/errors/invalid_target_position.rb
100
99
  - lib/mongoid/orderable/errors/transaction_failed.rb
101
100
  - lib/mongoid/orderable/generators/base.rb
@@ -105,6 +104,11 @@ files:
105
104
  - lib/mongoid/orderable/generators/movable.rb
106
105
  - lib/mongoid/orderable/generators/position.rb
107
106
  - lib/mongoid/orderable/generators/scope.rb
107
+ - lib/mongoid/orderable/handlers/base.rb
108
+ - lib/mongoid/orderable/handlers/document.rb
109
+ - lib/mongoid/orderable/handlers/document_embedded.rb
110
+ - lib/mongoid/orderable/handlers/document_transactional.rb
111
+ - lib/mongoid/orderable/handlers/transaction.rb
108
112
  - lib/mongoid/orderable/installer.rb
109
113
  - lib/mongoid/orderable/mixins/callbacks.rb
110
114
  - lib/mongoid/orderable/mixins/helpers.rb
@@ -112,8 +116,20 @@ files:
112
116
  - lib/mongoid/orderable/mixins/movable.rb
113
117
  - lib/mongoid/orderable/version.rb
114
118
  - lib/mongoid_orderable.rb
115
- - spec/mongoid/orderable_spec.rb
119
+ - spec/integration/concurrency_spec.rb
120
+ - spec/integration/customized_spec.rb
121
+ - spec/integration/embedded_spec.rb
122
+ - spec/integration/foreign_key_spec.rb
123
+ - spec/integration/inherited_spec.rb
124
+ - spec/integration/multiple_fields_spec.rb
125
+ - spec/integration/multiple_scoped_spec.rb
126
+ - spec/integration/no_indexed_spec.rb
127
+ - spec/integration/scoped_spec.rb
128
+ - spec/integration/simple_spec.rb
129
+ - spec/integration/string_scoped_spec.rb
130
+ - spec/integration/zero_based_spec.rb
116
131
  - spec/spec_helper.rb
132
+ - spec/support/models.rb
117
133
  homepage: https://github.com/mongoid/mongoid_orderable
118
134
  licenses: []
119
135
  metadata: {}
@@ -137,5 +153,17 @@ signing_key:
137
153
  specification_version: 4
138
154
  summary: Mongoid orderable list implementation
139
155
  test_files:
140
- - spec/mongoid/orderable_spec.rb
156
+ - spec/integration/concurrency_spec.rb
157
+ - spec/integration/customized_spec.rb
158
+ - spec/integration/embedded_spec.rb
159
+ - spec/integration/foreign_key_spec.rb
160
+ - spec/integration/inherited_spec.rb
161
+ - spec/integration/multiple_fields_spec.rb
162
+ - spec/integration/multiple_scoped_spec.rb
163
+ - spec/integration/no_indexed_spec.rb
164
+ - spec/integration/scoped_spec.rb
165
+ - spec/integration/simple_spec.rb
166
+ - spec/integration/string_scoped_spec.rb
167
+ - spec/integration/zero_based_spec.rb
141
168
  - spec/spec_helper.rb
169
+ - spec/support/models.rb
@@ -1,1486 +0,0 @@
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