narabikae 0.2.1 → 0.3.0
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/README.md +101 -1
- data/lib/narabikae/active_record_extension.rb +32 -8
- data/lib/narabikae/configuration.rb +1 -1
- data/lib/narabikae/option.rb +10 -4
- data/lib/narabikae/position.rb +66 -26
- data/lib/narabikae/version.rb +1 -1
- data/lib/narabikae.rb +36 -6
- metadata +79 -24
- data/lib/narabikae/active_record_handler.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 889a0fb05988806d1ff5ab57e02932cf4bf8a57ad788e3a4f094456e15e57902
|
|
4
|
+
data.tar.gz: 1dfb405bc128aacd6bae61b89e133af0aa40cf67b16638ba475bb1933584c855
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c2e99432062adc3745d8c6641d99bbd7a2b91a021103c552f3c4f9a7d6678370c344af81adf7d10a0ba3103730070bd8f7370fcb9c99a99dd06c8d01d89e882
|
|
7
|
+
data.tar.gz: 0aed46f7bf0ed8ae057324e17b99799d132e77244382ba0d945476221d3edb0d7c2f9444f2281b0d28a9e85e17fce9da1cb0eba13c27c613e4e4fe5da629bdaa
|
data/README.md
CHANGED
|
@@ -69,6 +69,10 @@ class Task < ApplicationRecord
|
|
|
69
69
|
# Used for validation of the internally generated order value.
|
|
70
70
|
# This value should be equivalent to
|
|
71
71
|
# the limit set in the DB column.
|
|
72
|
+
#
|
|
73
|
+
# default_position: optional
|
|
74
|
+
# Set where new/auto-set records are inserted.
|
|
75
|
+
# Accepts :first or :last (default).
|
|
72
76
|
end
|
|
73
77
|
```
|
|
74
78
|
|
|
@@ -88,6 +92,16 @@ Task.order(:position).pluck(:name, :position)
|
|
|
88
92
|
> [!NOTE]
|
|
89
93
|
> The position is set using the before_create callback. Therefore, do not define validations such as presence on the attributes managed by this gem!
|
|
90
94
|
|
|
95
|
+
#### Default position
|
|
96
|
+
|
|
97
|
+
By default, new/auto-set records are inserted at the end of the list. To insert at the beginning instead:
|
|
98
|
+
|
|
99
|
+
```rb
|
|
100
|
+
class Task < ApplicationRecord
|
|
101
|
+
narabikae :position, size: 200, default_position: :first
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
91
105
|
## Usage Details
|
|
92
106
|
|
|
93
107
|
### Reorder
|
|
@@ -157,6 +171,45 @@ target.position
|
|
|
157
171
|
# ex: target.move_to_position_between(tasks.first, nil)
|
|
158
172
|
```
|
|
159
173
|
|
|
174
|
+
### Set without saving
|
|
175
|
+
|
|
176
|
+
If you want to set the new position value and save later (for example, in a form), use `set_<field>_after/before/between`. These methods only assign the new position value and do not persist the record.
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
target.set_position_after(tasks.last)
|
|
180
|
+
target.position
|
|
181
|
+
# => 'a3'
|
|
182
|
+
target.save
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
You can also use setter-style aliases:
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
target.position_after = tasks.last
|
|
189
|
+
target.position_between = [tasks.first, tasks.last]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Form-friendly setters
|
|
193
|
+
|
|
194
|
+
The setter aliases can be used directly in forms or `assign_attributes`. They accept a target record or a position key (string). For `*_between=`, you can pass an array or hash. Primary key inputs are not accepted; do your own lookup and pass the record or its position.
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
# position key input (e.g., from a hidden field)
|
|
198
|
+
task.assign_attributes(position_after: tasks.last.position)
|
|
199
|
+
|
|
200
|
+
# between using an array
|
|
201
|
+
task.position_between = [tasks.first, tasks.last]
|
|
202
|
+
|
|
203
|
+
# between using a hash (string or symbol keys)
|
|
204
|
+
task.position_between = { prev: tasks.first.position, next: tasks.last.position }
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
If you need retries, use the method form and pass `challenge` there:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
task.set_position_between(tasks.first, tasks.last, challenge: 15)
|
|
211
|
+
```
|
|
212
|
+
|
|
160
213
|
### Scope
|
|
161
214
|
|
|
162
215
|
You can use this when you want to manage independent positions within specific scopes, such as foreign keys.
|
|
@@ -170,7 +223,7 @@ end
|
|
|
170
223
|
class Chapter < ApplicationRecord
|
|
171
224
|
belongs_to :course
|
|
172
225
|
|
|
173
|
-
narabikae :position, size: 100, scope:
|
|
226
|
+
narabikae :position, size: 100, scope: :course_id
|
|
174
227
|
end
|
|
175
228
|
|
|
176
229
|
course = Course.create
|
|
@@ -216,6 +269,53 @@ ticket.move_to_position_between(t1, t2, challenge: 15)
|
|
|
216
269
|
|
|
217
270
|
Feel free to message me on Github (kazu-2020)
|
|
218
271
|
|
|
272
|
+
## Development
|
|
273
|
+
|
|
274
|
+
### Supported versions (tested in CI)
|
|
275
|
+
|
|
276
|
+
- Ruby 3.1, 3.2, 3.3, 3.4, 4.0
|
|
277
|
+
- Rails 7.1, 7.2, 8.0, 8.1, and Rails main (via `railties` from `rails/rails`)
|
|
278
|
+
|
|
279
|
+
### Test suite
|
|
280
|
+
|
|
281
|
+
Tests are Minitest-based and run against a dummy Rails app located at `test/dummy`.
|
|
282
|
+
|
|
283
|
+
Database targets:
|
|
284
|
+
|
|
285
|
+
- `TARGET_DB=mysql` (default)
|
|
286
|
+
- `TARGET_DB=postgres`
|
|
287
|
+
- `TARGET_DB=sqlite`
|
|
288
|
+
|
|
289
|
+
To spin up database services locally:
|
|
290
|
+
|
|
291
|
+
```sh
|
|
292
|
+
docker compose up -d
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Run the full matrix locally (all databases):
|
|
296
|
+
|
|
297
|
+
```sh
|
|
298
|
+
bundle exec rake test
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Run a single database:
|
|
302
|
+
|
|
303
|
+
```sh
|
|
304
|
+
bundle exec rake test:postgres
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Run tests directly with Rails for a specific target:
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
TARGET_DB=sqlite bin/rails test
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
To test against a specific Rails version:
|
|
314
|
+
|
|
315
|
+
```sh
|
|
316
|
+
BUNDLE_GEMFILE=gemfiles/rails_8_1.gemfile TARGET_DB=mysql bundle exec rake test
|
|
317
|
+
```
|
|
318
|
+
|
|
219
319
|
## Contributing
|
|
220
320
|
|
|
221
321
|
Please wait a moment... 🙏
|
|
@@ -11,36 +11,60 @@ module Narabikae
|
|
|
11
11
|
# check valid key for fractional_indexer
|
|
12
12
|
# when invalid key, raise FractionalIndexer::Error
|
|
13
13
|
FractionalIndexer.generate_key(prev_key: record.send(option.field))
|
|
14
|
-
|
|
14
|
+
record.send(option.field).nil? ||
|
|
15
|
+
(option.scope.any? { |s| record.will_save_change_to_attribute?(s) } && !record.will_save_change_to_attribute?(option.field))
|
|
15
16
|
rescue FractionalIndexer::Error
|
|
16
17
|
true
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
def set_position
|
|
20
|
-
|
|
20
|
+
def set_position(position = option.default_position)
|
|
21
|
+
new_position =
|
|
22
|
+
case position
|
|
23
|
+
when :first
|
|
24
|
+
position_generator.create_first_position
|
|
25
|
+
when :last
|
|
26
|
+
position_generator.create_last_position
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
record.send("#{option.field}=", new_position)
|
|
21
30
|
end
|
|
22
31
|
|
|
23
|
-
def
|
|
32
|
+
def set_after(target, **args)
|
|
24
33
|
new_position = position_generator.find_position_after(target, **args)
|
|
25
34
|
return false if new_position.blank?
|
|
26
35
|
|
|
27
36
|
record.send("#{option.field}=", new_position)
|
|
28
|
-
record.save
|
|
29
37
|
end
|
|
30
38
|
|
|
31
|
-
def
|
|
39
|
+
def set_before(target, **args)
|
|
32
40
|
new_position = position_generator.find_position_before(target, **args)
|
|
33
41
|
return false if new_position.blank?
|
|
34
42
|
|
|
35
43
|
record.send("#{option.field}=", new_position)
|
|
36
|
-
record.save
|
|
37
44
|
end
|
|
38
45
|
|
|
39
|
-
def
|
|
46
|
+
def set_between(prev_target, next_target, **args)
|
|
40
47
|
new_position = position_generator.find_position_between(prev_target, next_target, **args)
|
|
41
48
|
return false if new_position.blank?
|
|
42
49
|
|
|
43
50
|
record.send("#{option.field}=", new_position)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def move_to_after(target, **args)
|
|
54
|
+
return false unless set_after(target, **args)
|
|
55
|
+
|
|
56
|
+
record.save
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def move_to_before(target, **args)
|
|
60
|
+
return false unless set_before(target, **args)
|
|
61
|
+
|
|
62
|
+
record.save
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def move_to_between(prev_target, next_target, **args)
|
|
66
|
+
return false unless set_between(prev_target, next_target, **args)
|
|
67
|
+
|
|
44
68
|
record.save
|
|
45
69
|
end
|
|
46
70
|
|
|
@@ -2,7 +2,7 @@ module Narabikae
|
|
|
2
2
|
class Configuration
|
|
3
3
|
# Sets the base value for FractionalIndexer configuration.
|
|
4
4
|
#
|
|
5
|
-
# @param int [Integer] The base value can be 10, 62, 94, with the default being
|
|
5
|
+
# @param int [Integer] The base value can be 10, 62, 94, with the default being 62.
|
|
6
6
|
# @return [void]
|
|
7
7
|
def base=(int)
|
|
8
8
|
FractionalIndexer.configure do |config|
|
data/lib/narabikae/option.rb
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
module Narabikae
|
|
2
2
|
class Option
|
|
3
|
-
attr_reader :field, :key_max_size, :scope
|
|
3
|
+
attr_reader :field, :key_max_size, :scope, :default_position
|
|
4
4
|
|
|
5
5
|
# Initializes a new instance of the Option class.
|
|
6
6
|
#
|
|
7
7
|
# @param field [Symbol]
|
|
8
8
|
# @param key_max_size [Integer] The maximum size of the key.
|
|
9
|
-
# @param scope [Array<Symbol>] The scope of the option.
|
|
10
|
-
|
|
9
|
+
# @param scope [Symbol, Array<Symbol>] The scope of the option.
|
|
10
|
+
# @param default_position [Symbol] The default position when creating or auto setting.
|
|
11
|
+
def initialize(field:, key_max_size:, scope: [], default_position: :last)
|
|
11
12
|
@field = field.to_sym
|
|
12
13
|
@key_max_size = key_max_size.to_i
|
|
13
|
-
@scope =
|
|
14
|
+
@scope = Array.wrap(scope).map(&:to_sym)
|
|
15
|
+
@default_position = (default_position || :last).to_sym
|
|
16
|
+
|
|
17
|
+
unless %i[first last].include?(@default_position)
|
|
18
|
+
raise ArgumentError, "default_position must be :first or :last"
|
|
19
|
+
end
|
|
14
20
|
end
|
|
15
21
|
end
|
|
16
22
|
end
|
data/lib/narabikae/position.rb
CHANGED
|
@@ -16,23 +16,28 @@ module Narabikae
|
|
|
16
16
|
FractionalIndexer.generate_key(prev_key: current_last_position)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# Generates a new key for the first position
|
|
20
|
+
#
|
|
21
|
+
# @return [String] The newly generated key for the first position.
|
|
22
|
+
def create_first_position
|
|
23
|
+
FractionalIndexer.generate_key(next_key: current_first_position)
|
|
24
|
+
end
|
|
25
|
+
|
|
19
26
|
# Finds the position after the specified target.
|
|
20
27
|
# If generated key is invalid(ex: it already exists),
|
|
21
28
|
# a new key is generated until the challenge count reaches the limit.
|
|
22
29
|
# challenge count is 10 by default.
|
|
23
30
|
#
|
|
24
|
-
# @param target [
|
|
25
|
-
# @param
|
|
26
|
-
# @option args [Integer] :challenge The number of times to attempt finding a valid position.
|
|
31
|
+
# @param target [ActiveRecord::Base, String]
|
|
32
|
+
# @param challenge [Integer] The number of times to attempt finding a valid position.
|
|
27
33
|
# @return [String, nil] The generated key for the position after the target, or nil if no valid position is found.
|
|
28
|
-
def find_position_after(target,
|
|
29
|
-
merged_args = { challenge: 10 }.merge(args)
|
|
34
|
+
def find_position_after(target, challenge: 10)
|
|
30
35
|
# when target is nil, try to generate key from the last position
|
|
31
|
-
target_key = target
|
|
36
|
+
target_key = extract_target_key(target) || current_last_position
|
|
32
37
|
key = FractionalIndexer.generate_key(prev_key: target_key)
|
|
33
38
|
return key if valid?(key)
|
|
34
39
|
|
|
35
|
-
(
|
|
40
|
+
(challenge || 0).times do |i|
|
|
36
41
|
key = FractionalIndexer.generate_key(prev_key: target_key, next_key: key)
|
|
37
42
|
key += random_fractional
|
|
38
43
|
return key if valid?(key)
|
|
@@ -53,18 +58,16 @@ module Narabikae
|
|
|
53
58
|
# position = Position.new
|
|
54
59
|
# position.find_position_before(target, challenge: 5)
|
|
55
60
|
#
|
|
56
|
-
# @param target [
|
|
57
|
-
# @param
|
|
58
|
-
# @option args [Integer] :challenge The number of times to attempt finding a valid position.
|
|
61
|
+
# @param target [ActiveRecord::Base, String]
|
|
62
|
+
# @param challenge [Integer] The number of times to attempt finding a valid position.
|
|
59
63
|
# @return [String, nil] The generated key for the position before the target, or nil if no valid position is found.
|
|
60
|
-
def find_position_before(target,
|
|
61
|
-
merged_args = { challenge: 10 }.merge(args)
|
|
64
|
+
def find_position_before(target, challenge: 10)
|
|
62
65
|
# when target is nil, try to generate key from the first position
|
|
63
|
-
target_key = target
|
|
66
|
+
target_key = extract_target_key(target) || current_first_position
|
|
64
67
|
key = FractionalIndexer.generate_key(next_key: target_key)
|
|
65
68
|
return key if valid?(key)
|
|
66
69
|
|
|
67
|
-
(
|
|
70
|
+
(challenge || 0).times do |i|
|
|
68
71
|
key = FractionalIndexer.generate_key(prev_key: key, next_key: target_key)
|
|
69
72
|
key += random_fractional
|
|
70
73
|
return key if valid?(key)
|
|
@@ -77,25 +80,24 @@ module Narabikae
|
|
|
77
80
|
|
|
78
81
|
# Finds the position between two targets.
|
|
79
82
|
#
|
|
80
|
-
# @param prev_target [
|
|
81
|
-
# @param next_target [
|
|
82
|
-
# @param
|
|
83
|
-
# @option args [Integer] :challenge The number of times to attempt finding a valid position.
|
|
83
|
+
# @param prev_target [ActiveRecord::Base, String] The previous target.
|
|
84
|
+
# @param next_target [ActiveRecord::Base, String] The next target.
|
|
85
|
+
# @param challenge [Integer] The number of times to attempt finding a valid position.
|
|
84
86
|
# @return [string, nil] The position between the two targets, or nil if no valid position is found.
|
|
85
|
-
def find_position_between(prev_target, next_target,
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
def find_position_between(prev_target, next_target, challenge: 10)
|
|
88
|
+
prev_key = extract_target_key(prev_target)
|
|
89
|
+
next_key = extract_target_key(next_target)
|
|
90
|
+
return find_position_before(next_target, challenge: challenge) if prev_key.blank?
|
|
91
|
+
return find_position_after(prev_target, challenge: challenge) if next_key.blank?
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
prev_key, next_key = [ prev_target.send(option.field), next_target.send(option.field) ].minmax
|
|
93
|
+
prev_key, next_key = [ prev_key, next_key ].minmax
|
|
92
94
|
key = FractionalIndexer.generate_key(
|
|
93
95
|
prev_key: prev_key,
|
|
94
96
|
next_key: next_key,
|
|
95
97
|
)
|
|
96
98
|
return key if valid?(key)
|
|
97
99
|
|
|
98
|
-
(
|
|
100
|
+
(challenge || 0).times do |i|
|
|
99
101
|
key = FractionalIndexer.generate_key(prev_key: key, next_key: next_key)
|
|
100
102
|
key += random_fractional
|
|
101
103
|
return key if valid?(key)
|
|
@@ -123,7 +125,7 @@ module Narabikae
|
|
|
123
125
|
end
|
|
124
126
|
|
|
125
127
|
def model
|
|
126
|
-
record.class.base_class
|
|
128
|
+
record.class.base_class.unscoped
|
|
127
129
|
end
|
|
128
130
|
|
|
129
131
|
# generate a random fractional part
|
|
@@ -148,5 +150,43 @@ module Narabikae
|
|
|
148
150
|
|
|
149
151
|
capable?(key) && uniq?(key)
|
|
150
152
|
end
|
|
153
|
+
|
|
154
|
+
def extract_target_key(target)
|
|
155
|
+
return if target.nil?
|
|
156
|
+
return target if target.is_a?(String)
|
|
157
|
+
unless target.is_a?(ActiveRecord::Base)
|
|
158
|
+
raise Narabikae::Error, "target must be an ActiveRecord object or position key string"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
record_table = table_name_for_class(record)
|
|
162
|
+
target_table = table_name_for_class(target)
|
|
163
|
+
unless target_table && record_table && target_table == record_table
|
|
164
|
+
raise Narabikae::Error,
|
|
165
|
+
"target model mismatch: expected table #{record_table || 'unknown'}, got #{target_table || 'unknown'}"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
mismatched_columns = mismatched_scope_columns(target)
|
|
169
|
+
if mismatched_columns.any?
|
|
170
|
+
raise Narabikae::Error, "target scope mismatch for columns: #{mismatched_columns.join(', ')}"
|
|
171
|
+
end
|
|
172
|
+
raise Narabikae::Error, "target missing #{option.field} field" unless target.respond_to?(option.field)
|
|
173
|
+
|
|
174
|
+
target.send(option.field)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def table_name_for_class(value)
|
|
178
|
+
klass = value.class
|
|
179
|
+
return unless klass.respond_to?(:table_name)
|
|
180
|
+
|
|
181
|
+
klass.table_name
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def mismatched_scope_columns(target)
|
|
185
|
+
option.scope.select do |column|
|
|
186
|
+
!target.respond_to?(column) ||
|
|
187
|
+
!record.respond_to?(column) ||
|
|
188
|
+
target.public_send(column) != record.public_send(column)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
151
191
|
end
|
|
152
192
|
end
|
data/lib/narabikae/version.rb
CHANGED
data/lib/narabikae.rb
CHANGED
|
@@ -29,20 +29,50 @@ module Narabikae
|
|
|
29
29
|
extend ActiveSupport::Concern
|
|
30
30
|
|
|
31
31
|
class_methods do
|
|
32
|
-
def narabikae(field = :position, size:, scope: [])
|
|
32
|
+
def narabikae(field = :position, size:, scope: [], default_position: :last)
|
|
33
33
|
option = narabikae_option_store.register!(
|
|
34
34
|
field.to_sym,
|
|
35
|
-
Narabikae::Option.new(field: field, key_max_size: size, scope: scope)
|
|
35
|
+
Narabikae::Option.new(field: field, key_max_size: size, scope: scope, default_position: default_position)
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
before_save -> {
|
|
39
39
|
extension = Narabikae::ActiveRecordExtension.new(self, option)
|
|
40
|
-
extension.set_position
|
|
40
|
+
extension.set_position(option.default_position) if extension.auto_set_position?
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
define_method :"set_#{field}_after" do |target = nil, **args|
|
|
44
|
+
extension = Narabikae::ActiveRecordExtension.new(self, option)
|
|
45
|
+
extension.set_after(target, **args)
|
|
46
|
+
end
|
|
47
|
+
alias_method :"#{field}_after=", :"set_#{field}_after"
|
|
48
|
+
|
|
49
|
+
define_method :"set_#{field}_before" do |target = nil, **args|
|
|
50
|
+
extension = Narabikae::ActiveRecordExtension.new(self, option)
|
|
51
|
+
extension.set_before(target, **args)
|
|
52
|
+
end
|
|
53
|
+
alias_method :"#{field}_before=", :"set_#{field}_before"
|
|
54
|
+
|
|
55
|
+
define_method :"set_#{field}_between" do |prev_target = nil, next_target = nil, **args|
|
|
56
|
+
extension = Narabikae::ActiveRecordExtension.new(self, option)
|
|
57
|
+
extension.set_between(prev_target, next_target, **args)
|
|
41
58
|
end
|
|
59
|
+
define_method :"#{field}_between=" do |value|
|
|
60
|
+
prev_target = nil
|
|
61
|
+
next_target = nil
|
|
62
|
+
|
|
63
|
+
case value
|
|
64
|
+
when Array
|
|
65
|
+
prev_target, next_target = value
|
|
66
|
+
when Hash
|
|
67
|
+
payload = value.with_indifferent_access
|
|
68
|
+
prev_target = payload[:prev_target] || payload[:prev]
|
|
69
|
+
next_target = payload[:next_target] || payload[:next]
|
|
70
|
+
else
|
|
71
|
+
prev_target = value
|
|
72
|
+
end
|
|
42
73
|
|
|
43
|
-
before_update do
|
|
44
74
|
extension = Narabikae::ActiveRecordExtension.new(self, option)
|
|
45
|
-
extension.
|
|
75
|
+
extension.set_between(prev_target, next_target)
|
|
46
76
|
end
|
|
47
77
|
|
|
48
78
|
define_method :"move_to_#{field}_after" do |target = nil, **args|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: narabikae
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- matazou
|
|
@@ -10,49 +10,49 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: activerecord
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: '7.1'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: '7.1'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: railties
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '7.1'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '7.1'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: fractional_indexer
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: 0.4.0
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: 0.4.0
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name:
|
|
55
|
+
name: appraisal
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - ">="
|
|
@@ -66,47 +66,103 @@ dependencies:
|
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '0'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
|
-
name:
|
|
69
|
+
name: debug
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 1.
|
|
74
|
+
version: '1.9'
|
|
75
75
|
type: :development
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 1.
|
|
81
|
+
version: '1.9'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
|
-
name:
|
|
83
|
+
name: minitest
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
86
|
- - "~>"
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version:
|
|
88
|
+
version: '5.0'
|
|
89
89
|
type: :development
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version:
|
|
95
|
+
version: '5.0'
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
|
-
name:
|
|
97
|
+
name: mocha
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: mysql2
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: pg
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - ">="
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0'
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: sqlite3
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0'
|
|
145
|
+
type: :development
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
152
|
+
- !ruby/object:Gem::Dependency
|
|
153
|
+
name: rubocop-rails-omakase
|
|
98
154
|
requirement: !ruby/object:Gem::Requirement
|
|
99
155
|
requirements:
|
|
100
|
-
- -
|
|
156
|
+
- - ">="
|
|
101
157
|
- !ruby/object:Gem::Version
|
|
102
|
-
version:
|
|
158
|
+
version: '0'
|
|
103
159
|
type: :development
|
|
104
160
|
prerelease: false
|
|
105
161
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
162
|
requirements:
|
|
107
|
-
- -
|
|
163
|
+
- - ">="
|
|
108
164
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
165
|
+
version: '0'
|
|
110
166
|
description: 'provides functionality similar to acts_as_list. However, by managing
|
|
111
167
|
position using a fractional indexing system, it allows database record updates during
|
|
112
168
|
reordering to be completed with only a single update (N = 1)!
|
|
@@ -122,7 +178,6 @@ files:
|
|
|
122
178
|
- README.md
|
|
123
179
|
- lib/narabikae.rb
|
|
124
180
|
- lib/narabikae/active_record_extension.rb
|
|
125
|
-
- lib/narabikae/active_record_handler.rb
|
|
126
181
|
- lib/narabikae/configuration.rb
|
|
127
182
|
- lib/narabikae/option.rb
|
|
128
183
|
- lib/narabikae/option_store.rb
|
|
@@ -145,7 +200,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
145
200
|
requirements:
|
|
146
201
|
- - ">="
|
|
147
202
|
- !ruby/object:Gem::Version
|
|
148
|
-
version: '3.
|
|
203
|
+
version: '3.1'
|
|
149
204
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
205
|
requirements:
|
|
151
206
|
- - ">="
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module Narabikae
|
|
2
|
-
class ActiveRecordHandler
|
|
3
|
-
# Initializes a new instance of the ActiveRecordHandler class.
|
|
4
|
-
#
|
|
5
|
-
# @param record [Object] The ActiveRecord object.
|
|
6
|
-
# @param column [Symbol] The column symbol.
|
|
7
|
-
def initialize(record, column)
|
|
8
|
-
@record = record
|
|
9
|
-
@column = column
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Generates a new key for the last position
|
|
13
|
-
#
|
|
14
|
-
# @return [String] The newly generated key for the last position.
|
|
15
|
-
def create_last_position
|
|
16
|
-
FractionalIndexer.generate_key(prev_key: current_last_position)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
attr_reader :record, :column
|
|
22
|
-
|
|
23
|
-
def current_last_position
|
|
24
|
-
model.maximum(column)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def model
|
|
28
|
-
record.class.base_class
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|