mongoid_orderable 6.0.4 → 6.0.6
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 +101 -93
- data/LICENSE.txt +20 -20
- data/README.md +280 -275
- data/Rakefile +24 -24
- data/lib/config/locales/en.yml +12 -12
- data/lib/mongoid/orderable/configs/field_config.rb +81 -81
- data/lib/mongoid/orderable/configs/global_config.rb +26 -26
- data/lib/mongoid/orderable/errors/invalid_target_position.rb +19 -19
- data/lib/mongoid/orderable/errors/transaction_failed.rb +20 -20
- data/lib/mongoid/orderable/generators/base.rb +21 -21
- data/lib/mongoid/orderable/generators/helpers.rb +37 -37
- data/lib/mongoid/orderable/generators/listable.rb +41 -41
- data/lib/mongoid/orderable/generators/lock_collection.rb +39 -39
- data/lib/mongoid/orderable/generators/movable.rb +62 -62
- data/lib/mongoid/orderable/generators/position.rb +26 -26
- data/lib/mongoid/orderable/generators/scope.rb +26 -26
- data/lib/mongoid/orderable/handlers/base.rb +215 -189
- data/lib/mongoid/orderable/handlers/document.rb +32 -24
- data/lib/mongoid/orderable/handlers/document_embedded.rb +14 -14
- data/lib/mongoid/orderable/handlers/document_transactional.rb +32 -35
- data/lib/mongoid/orderable/handlers/transaction.rb +75 -71
- data/lib/mongoid/orderable/installer.rb +64 -64
- data/lib/mongoid/orderable/mixins/callbacks.rb +45 -43
- data/lib/mongoid/orderable/mixins/cascadeable.rb +15 -15
- data/lib/mongoid/orderable/mixins/helpers.rb +39 -39
- data/lib/mongoid/orderable/mixins/listable.rb +49 -49
- data/lib/mongoid/orderable/mixins/movable.rb +64 -60
- data/lib/mongoid/orderable/version.rb +7 -7
- data/lib/mongoid/orderable.rb +29 -29
- data/lib/mongoid_orderable.rb +34 -34
- metadata +6 -93
- data/spec/integration/cascadeable_spec.rb +0 -12
- data/spec/integration/concurrency_spec.rb +0 -232
- data/spec/integration/conditional_spec.rb +0 -36
- data/spec/integration/customized_spec.rb +0 -31
- data/spec/integration/embedded_spec.rb +0 -63
- data/spec/integration/foreign_key_spec.rb +0 -33
- data/spec/integration/inherited_spec.rb +0 -54
- data/spec/integration/multiple_fields_spec.rb +0 -554
- data/spec/integration/multiple_scoped_spec.rb +0 -63
- data/spec/integration/no_indexed_spec.rb +0 -23
- data/spec/integration/scoped_spec.rb +0 -151
- data/spec/integration/simple_spec.rb +0 -184
- data/spec/integration/string_scoped_spec.rb +0 -28
- data/spec/integration/zero_based_spec.rb +0 -161
- data/spec/spec_helper.rb +0 -42
- data/spec/support/models.rb +0 -134
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Mongoid
|
|
4
|
-
module Orderable
|
|
5
|
-
module Generators
|
|
6
|
-
class Movable < Base
|
|
7
|
-
def generate(field_name)
|
|
8
|
-
generate_move_to_helpers(field_name)
|
|
9
|
-
generate_insert_at_helpers(field_name)
|
|
10
|
-
generate_shorthand_helpers(field_name)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def generate_move_to_helpers(field_name)
|
|
16
|
-
generate_method("move_#{field_name}_to") do |target_position|
|
|
17
|
-
move_field_to target_position, field: field_name
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
generate_method("move_#{field_name}_to!") do |target_position|
|
|
21
|
-
move_field_to target_position, field: field_name
|
|
22
|
-
save
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
generate_method("move_#{field_name}_to=") do |target_position|
|
|
26
|
-
move_field_to target_position, field: field_name
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def generate_insert_at_helpers(field_name)
|
|
31
|
-
klass.class_eval do
|
|
32
|
-
alias_method "insert_#{field_name}_at!", "move_#{field_name}_to!"
|
|
33
|
-
alias_method "insert_#{field_name}_at", "move_#{field_name}_to"
|
|
34
|
-
alias_method "insert_#{field_name}_at=", "move_#{field_name}_to="
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def generate_shorthand_helpers(field_name)
|
|
39
|
-
%i[top bottom].each do |symbol|
|
|
40
|
-
generate_method "move_#{field_name}_to_#{symbol}" do
|
|
41
|
-
move_to symbol, field: field_name
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
generate_method "move_#{field_name}_to_#{symbol}!" do
|
|
45
|
-
move_to! symbol, field: field_name
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
%i[higher lower].each do |symbol|
|
|
50
|
-
generate_method "move_#{field_name}_#{symbol}" do
|
|
51
|
-
move_to symbol, field: field_name
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
generate_method "move_#{field_name}_#{symbol}!" do
|
|
55
|
-
move_to! symbol, field: field_name
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mongoid
|
|
4
|
+
module Orderable
|
|
5
|
+
module Generators
|
|
6
|
+
class Movable < Base
|
|
7
|
+
def generate(field_name)
|
|
8
|
+
generate_move_to_helpers(field_name)
|
|
9
|
+
generate_insert_at_helpers(field_name)
|
|
10
|
+
generate_shorthand_helpers(field_name)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def generate_move_to_helpers(field_name)
|
|
16
|
+
generate_method("move_#{field_name}_to") do |target_position|
|
|
17
|
+
move_field_to target_position, field: field_name
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
generate_method("move_#{field_name}_to!") do |target_position|
|
|
21
|
+
move_field_to target_position, field: field_name
|
|
22
|
+
save
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
generate_method("move_#{field_name}_to=") do |target_position|
|
|
26
|
+
move_field_to target_position, field: field_name
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate_insert_at_helpers(field_name)
|
|
31
|
+
klass.class_eval do
|
|
32
|
+
alias_method "insert_#{field_name}_at!", "move_#{field_name}_to!"
|
|
33
|
+
alias_method "insert_#{field_name}_at", "move_#{field_name}_to"
|
|
34
|
+
alias_method "insert_#{field_name}_at=", "move_#{field_name}_to="
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def generate_shorthand_helpers(field_name)
|
|
39
|
+
%i[top bottom].each do |symbol|
|
|
40
|
+
generate_method "move_#{field_name}_to_#{symbol}" do
|
|
41
|
+
move_to symbol, field: field_name
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
generate_method "move_#{field_name}_to_#{symbol}!" do
|
|
45
|
+
move_to! symbol, field: field_name
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
%i[higher lower].each do |symbol|
|
|
50
|
+
generate_method "move_#{field_name}_#{symbol}" do
|
|
51
|
+
move_to symbol, field: field_name
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
generate_method "move_#{field_name}_#{symbol}!" do
|
|
55
|
+
move_to! symbol, field: field_name
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Mongoid
|
|
4
|
-
module Orderable
|
|
5
|
-
module Generators
|
|
6
|
-
class Position < Base
|
|
7
|
-
def generate(field_name)
|
|
8
|
-
klass.class_eval <<~KLASS, __FILE__, __LINE__ + 1
|
|
9
|
-
def orderable_position(field = nil)
|
|
10
|
-
field ||= default_orderable_field
|
|
11
|
-
send
|
|
12
|
-
end
|
|
13
|
-
KLASS
|
|
14
|
-
|
|
15
|
-
generate_method("orderable_#{field_name}_position") do
|
|
16
|
-
send
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
generate_method("orderable_#{field_name}_position=") do |value|
|
|
20
|
-
send
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mongoid
|
|
4
|
+
module Orderable
|
|
5
|
+
module Generators
|
|
6
|
+
class Position < Base
|
|
7
|
+
def generate(field_name)
|
|
8
|
+
klass.class_eval <<~KLASS, __FILE__, __LINE__ + 1
|
|
9
|
+
def orderable_position(field = nil)
|
|
10
|
+
field ||= default_orderable_field
|
|
11
|
+
send("orderable_\#{field}_position")
|
|
12
|
+
end
|
|
13
|
+
KLASS
|
|
14
|
+
|
|
15
|
+
generate_method("orderable_#{field_name}_position") do
|
|
16
|
+
send(field_name)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
generate_method("orderable_#{field_name}_position=") do |value|
|
|
20
|
+
send("#{field_name}=", value)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Mongoid
|
|
4
|
-
module Orderable
|
|
5
|
-
module Generators
|
|
6
|
-
class Scope < Base
|
|
7
|
-
def generate(field_name, order_scope)
|
|
8
|
-
criteria = criteria(order_scope)
|
|
9
|
-
klass.class_eval do
|
|
10
|
-
scope "orderable_#{field_name}_scope", criteria
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def criteria(order_scope)
|
|
17
|
-
case order_scope
|
|
18
|
-
when Proc then order_scope
|
|
19
|
-
when Array then ->(doc) { where(order_scope.each_with_object({}) {|f, h| h[f] = doc.send(f) }) }
|
|
20
|
-
else ->(_doc) { where({}) }
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mongoid
|
|
4
|
+
module Orderable
|
|
5
|
+
module Generators
|
|
6
|
+
class Scope < Base
|
|
7
|
+
def generate(field_name, order_scope)
|
|
8
|
+
criteria = criteria(order_scope)
|
|
9
|
+
klass.class_eval do
|
|
10
|
+
scope "orderable_#{field_name}_scope", criteria
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def criteria(order_scope)
|
|
17
|
+
case order_scope
|
|
18
|
+
when Proc then order_scope
|
|
19
|
+
when Array then ->(doc) { where(order_scope.each_with_object({}) {|f, h| h[f] = doc.send(f) }) }
|
|
20
|
+
else ->(_doc) { where({}) }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,189 +1,215 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Mongoid
|
|
4
|
-
module Orderable
|
|
5
|
-
module Handlers
|
|
6
|
-
class Base
|
|
7
|
-
attr_reader :doc
|
|
8
|
-
|
|
9
|
-
def initialize(doc)
|
|
10
|
-
@doc = doc
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
:
|
|
18
|
-
:
|
|
19
|
-
:
|
|
20
|
-
:
|
|
21
|
-
:
|
|
22
|
-
:
|
|
23
|
-
:
|
|
24
|
-
:
|
|
25
|
-
:
|
|
26
|
-
:persisted?,
|
|
27
|
-
:embedded?,
|
|
28
|
-
:collection_name,
|
|
29
|
-
to: :doc
|
|
30
|
-
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def
|
|
40
|
-
orderable_keys.
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def
|
|
44
|
-
return unless
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mongoid
|
|
4
|
+
module Orderable
|
|
5
|
+
module Handlers
|
|
6
|
+
class Base
|
|
7
|
+
attr_reader :doc
|
|
8
|
+
|
|
9
|
+
def initialize(doc)
|
|
10
|
+
@doc = doc
|
|
11
|
+
reset_new_record
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
delegate :orderable_keys,
|
|
17
|
+
:orderable_field,
|
|
18
|
+
:orderable_position,
|
|
19
|
+
:orderable_if,
|
|
20
|
+
:orderable_unless,
|
|
21
|
+
:orderable_scope,
|
|
22
|
+
:orderable_scope_changed?,
|
|
23
|
+
:orderable_top,
|
|
24
|
+
:orderable_bottom,
|
|
25
|
+
:_id,
|
|
26
|
+
:persisted?,
|
|
27
|
+
:embedded?,
|
|
28
|
+
:collection_name,
|
|
29
|
+
to: :doc
|
|
30
|
+
|
|
31
|
+
def new_record?
|
|
32
|
+
use_transactions ? @new_record : doc.new_record?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def use_transactions
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def any_field_changed?
|
|
40
|
+
orderable_keys.any? {|field| changed?(field) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def set_new_record_positions
|
|
44
|
+
return unless new_record?
|
|
45
|
+
|
|
46
|
+
orderable_keys.each do |field|
|
|
47
|
+
next unless (position = doc.send(field))
|
|
48
|
+
|
|
49
|
+
move_all[field] ||= position
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def apply_all_positions
|
|
54
|
+
orderable_keys.each {|field| apply_one_position(field, move_all[field]) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def apply_one_position(field, target_position)
|
|
58
|
+
return unless allowed?(field) && changed?(field)
|
|
59
|
+
|
|
60
|
+
set_lock(field) if use_transactions
|
|
61
|
+
|
|
62
|
+
f = orderable_field(field)
|
|
63
|
+
scope = orderable_scope(field)
|
|
64
|
+
scope_changed = orderable_scope_changed?(field)
|
|
65
|
+
|
|
66
|
+
# Set scope-level lock if scope changed
|
|
67
|
+
if use_transactions && persisted? && scope_changed
|
|
68
|
+
set_lock(field, true)
|
|
69
|
+
scope_changed = orderable_scope_changed?(field)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Get the current position as exists in the database
|
|
73
|
+
current = if new_record? || scope_changed
|
|
74
|
+
nil
|
|
75
|
+
elsif persisted? && !embedded?
|
|
76
|
+
scope.where(_id: _id).pluck(f).first
|
|
77
|
+
else
|
|
78
|
+
orderable_position(field)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# If scope changed, remove the position from the old scope
|
|
82
|
+
if persisted? && !embedded? && scope_changed
|
|
83
|
+
existing_doc = doc.class.unscoped.find(_id)
|
|
84
|
+
self.class.new(existing_doc).send(:remove_one_position, field)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Return if there is no instruction to change the position
|
|
88
|
+
in_list = persisted? && current
|
|
89
|
+
return if in_list && !target_position
|
|
90
|
+
|
|
91
|
+
target = resolve_target_position(field, target_position, in_list)
|
|
92
|
+
|
|
93
|
+
# Use $inc operator to shift the position of the other documents
|
|
94
|
+
if !in_list
|
|
95
|
+
scope.gte(f => target).inc(f => 1)
|
|
96
|
+
elsif target < current
|
|
97
|
+
scope.where(f => { '$gte' => target, '$lt' => current }).inc(f => 1)
|
|
98
|
+
elsif target > current
|
|
99
|
+
scope.where(f => { '$gt' => current, '$lte' => target }).inc(f => -1)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# If persisted, update the field in the database atomically
|
|
103
|
+
doc.set({ f => target }.merge(changed_scope_hash(field))) if use_transactions && persisted?
|
|
104
|
+
doc.send("orderable_#{field}_position=", target)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def remove_all_positions
|
|
108
|
+
orderable_keys.each do |field|
|
|
109
|
+
remove_one_position(field)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def remove_one_position(field)
|
|
114
|
+
return unless allowed?(field)
|
|
115
|
+
f = orderable_field(field)
|
|
116
|
+
current = orderable_position(field)
|
|
117
|
+
set_lock(field) if use_transactions
|
|
118
|
+
orderable_scope(field).gt(f => current).inc(f => -1)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def move_all
|
|
122
|
+
doc.send(:move_all)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def reset
|
|
126
|
+
reset_new_record
|
|
127
|
+
doc.send(:clear_move_all!)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Required for transactions, which perform some actions
|
|
131
|
+
# in the after_create callback.
|
|
132
|
+
def reset_new_record
|
|
133
|
+
@new_record = doc.new_record?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def resolve_target_position(field, target_position, in_list)
|
|
137
|
+
target_position ||= 'bottom'
|
|
138
|
+
|
|
139
|
+
unless target_position.is_a?(Numeric)
|
|
140
|
+
target_position = case target_position.to_s
|
|
141
|
+
when 'top' then (min ||= orderable_top(field))
|
|
142
|
+
when 'bottom' then (max ||= orderable_bottom(field, in_list))
|
|
143
|
+
when 'higher' then orderable_position(field).pred
|
|
144
|
+
when 'lower' then orderable_position(field).next
|
|
145
|
+
when /\A\d+\Z/ then target_position.to_i
|
|
146
|
+
else raise Mongoid::Orderable::Errors::InvalidTargetPosition.new(target_position)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if target_position <= (min ||= orderable_top(field))
|
|
151
|
+
target_position = min
|
|
152
|
+
elsif target_position > (max ||= orderable_bottom(field, in_list))
|
|
153
|
+
target_position = max
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
target_position
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def allowed?(field)
|
|
160
|
+
cond_if = orderable_if(field)
|
|
161
|
+
cond_unless = orderable_unless(field)
|
|
162
|
+
|
|
163
|
+
(cond_if.nil? || resolve_condition(cond_if)) &&
|
|
164
|
+
(cond_unless.nil? || !resolve_condition(cond_unless))
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def resolve_condition(condition)
|
|
168
|
+
case condition
|
|
169
|
+
when Proc
|
|
170
|
+
condition.arity.zero? ? doc.instance_exec(&condition) : condition.call(doc)
|
|
171
|
+
when Symbol
|
|
172
|
+
doc.send(condition)
|
|
173
|
+
else
|
|
174
|
+
condition || false
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def changed?(field)
|
|
179
|
+
return true if new_record? || !doc.send(orderable_field(field)) || move_all[field]
|
|
180
|
+
changeable_keys(field).any? {|f| doc.send("#{f}_changed?") }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def changeable_keys(field)
|
|
184
|
+
[orderable_field(field)] | scope_keys(field)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def scope_keys(field)
|
|
188
|
+
orderable_scope(field).selector.keys.map do |f|
|
|
189
|
+
doc.fields[f]&.options&.[](:as) || f
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def changed_scope_hash(field)
|
|
194
|
+
scope_keys(field).each_with_object({}) do |f, hash|
|
|
195
|
+
hash[f] = doc.send(f) if doc.send("#{f}_changed?")
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def set_lock(field, generic = false)
|
|
200
|
+
return unless use_transactions
|
|
201
|
+
model_name = doc.class.orderable_configs[field][:lock_collection].to_s.singularize.classify
|
|
202
|
+
model = Mongoid::Orderable::Models.const_get(model_name)
|
|
203
|
+
attrs = lock_scope(field, generic)
|
|
204
|
+
model.where(attrs).find_one_and_update(attrs.merge(updated_at: Time.now), { upsert: true })
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def lock_scope(field, generic = false)
|
|
208
|
+
sel = orderable_scope(field).selector
|
|
209
|
+
scope = ([collection_name] + (generic ? [field] : sel.to_a.flatten)).map(&:to_s).join('|')
|
|
210
|
+
{ scope: scope }
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|