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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -1
- data/lib/mongoid/orderable/{engine.rb → handlers/base.rb} +42 -81
- data/lib/mongoid/orderable/handlers/document.rb +24 -0
- data/lib/mongoid/orderable/handlers/document_embedded.rb +14 -0
- data/lib/mongoid/orderable/handlers/document_transactional.rb +35 -0
- data/lib/mongoid/orderable/handlers/transaction.rb +71 -0
- data/lib/mongoid/orderable/mixins/callbacks.rb +21 -7
- data/lib/mongoid/orderable/version.rb +1 -1
- data/lib/mongoid_orderable.rb +5 -1
- data/spec/integration/concurrency_spec.rb +232 -0
- data/spec/integration/customized_spec.rb +31 -0
- data/spec/integration/embedded_spec.rb +41 -0
- data/spec/integration/foreign_key_spec.rb +33 -0
- data/spec/integration/inherited_spec.rb +54 -0
- data/spec/integration/multiple_fields_spec.rb +554 -0
- data/spec/integration/multiple_scoped_spec.rb +63 -0
- data/spec/integration/no_indexed_spec.rb +23 -0
- data/spec/integration/scoped_spec.rb +151 -0
- data/spec/integration/simple_spec.rb +184 -0
- data/spec/integration/string_scoped_spec.rb +28 -0
- data/spec/integration/zero_based_spec.rb +161 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/models.rb +122 -0
- metadata +33 -5
- data/spec/mongoid/orderable_spec.rb +0 -1486
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -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.
|
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-
|
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/
|
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/
|
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
|