mongoid_orderable 6.0.1 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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