narabikae 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64ba8405e3f4412d5c417fb13a499653d883308f93aa704350eedfc3e70dc1f9
4
- data.tar.gz: cc4217ce041d2db2e41af69182427fc6865dffed4329f23f5b70f0a1e8a26860
3
+ metadata.gz: 96e657bcc111a885f21934870ac5360490aa4999b6df0533af524f761ecd9244
4
+ data.tar.gz: d7eca3f77b6260eea610bb5f5dbcdd71099972f74cd88028d00c5a266bd32667
5
5
  SHA512:
6
- metadata.gz: bd0676a4413917e858801f4ec5a99c210ba82a92a77fb7153b5b20e2bff2a40f8ce1c13ebc34b067c863d2d5fe56a5806a521553850295a2a62a363b1e3543bf
7
- data.tar.gz: af54f6713a9f547a46c99393d6ede9198ced3ce47fdf3eb48e776482b3aa66d2b02f0bcdf218b9dbb8d478db4bee0332e9b56f6a5cb296372d0c70b203384e7f
6
+ metadata.gz: e0bcec0651250189dcdac6c724bccf994d98350d4668e543dab8e039cc3f5ecd2891961ba2ecaa3d9bcd198094968137d4d5fb8215f7da692a849496b310cec7
7
+ data.tar.gz: 90aa217d0d397c49452febff37a6153ffc10c2f309de27348482b1f28a3dac58440306001f2110eb70c3fa063e1d3e2789846b4c5fe0377750c84260b70a2e2b
data/README.md CHANGED
@@ -1,28 +1,223 @@
1
1
  # Narabikae
2
- Short description and motivation.
3
2
 
4
- ## Usage
5
- How to use my plugin.
3
+ Narabikae(Japanese: 並び替え) means "reorder". Like [acts_as_list](https://github.com/brendon/acts_as_list), this gem provides automatic order management and reordering functionality for your records.
4
+
5
+ One of the key advantages of this gem is its use of the [fractional indexing algorithm](https://www.figma.com/blog/realtime-editing-of-ordered-sequences/#fractional-indexing), which greatly enhances the efficiency of reordering operations. With Narabikae, regardless of the amount of data, "only a single record" is updated during the reordering process 🎉.
6
6
 
7
7
  ## Installation
8
- Add this line to your application's Gemfile:
8
+
9
+ In your Gemfile
9
10
 
10
11
  ```ruby
11
12
  gem "narabikae"
12
13
  ```
13
14
 
14
- And then execute:
15
- ```bash
16
- $ bundle
15
+ ## Getting started
16
+
17
+ ### Adding a column to manage order
18
+
19
+ To manage the order of records, you'll need to create a column in your database. A key feature of this gem is that it generates the order as a string!
20
+
21
+ ```rb
22
+ # example
23
+
24
+ create_table :tasks do |t|
25
+ t.string :name
26
+
27
+ # for MySQL
28
+ t.string :position, null: false, limit: 200, charset: 'ascii', collation: 'ascii_bin'
29
+
30
+ # for PostgreSQL
31
+ t.string :position, null: false, limit: 200, collation: 'C'
32
+
33
+ # for SQLite3
34
+ t.string :position, null: false, collation: 'binary'
35
+ end
36
+ add_index :tasks, :position, unique: true
37
+
17
38
  ```
18
39
 
19
- Or install it yourself as:
20
- ```bash
21
- $ gem install narabikae
40
+ #### Key points to consider when creating the column:
41
+
42
+ - Set the collation to distinguish between uppercase and lowercase letters.
43
+
44
+ For example, if using MySQL 8.0’s default collation (utf8mb4_0900_ai_ci), which does not distinguish between uppercase and lowercase, the sort results may not behave as expected.
45
+
46
+ - It is recommended to apply both NOT NULL and UNIQUE constraints.
47
+
48
+ This ensures data integrity and efficient ordering.
49
+
50
+ - Explicitly set a character limit for the column.
51
+
52
+ Since this column will typically be indexed, it is important to set an appropriate length. This gem uses a base-62 numbering system to represent the order. In the example above, with a length limit of 200 characters, you can represent up to "62^200 unique order values", providing a huge range for ordered sequences.
53
+
54
+ ### Adding configuration to your model
55
+
56
+ You only need to add one line as shown below!
57
+
58
+ ```rb
59
+ class Task < ApplicationRecord
60
+ narabikae :position, size: 200
61
+
62
+ # arg1: optional
63
+ # . Specify the field you want to use for ordering.
64
+ # The default is :position.
65
+ #
66
+ # size: required
67
+ # Used for validation of the internally generated order value.
68
+ # This value should be equivalent to
69
+ # the limit set in the DB column.
70
+ end
22
71
  ```
23
72
 
73
+ Once this is done, the position will be automatically set each time a Task model instance is saved!
74
+
75
+ ```rb
76
+ Task.create([
77
+ { name: 'task-1' },
78
+ { name: 'task-2' },
79
+ { name: 'task-3' }
80
+ ])
81
+ Task.order(:position).pluck(:name, :position)
82
+ # => [["task-1", "a0"], ["task-3", "a1"], ["task-1", "a2"]]
83
+
84
+ ```
85
+
86
+ > [!NOTE]
87
+ > The position is set using the before_create callback. Therefore, do not define validations such as presence on the attributes managed by this gem!
88
+
89
+ ## Usage Details
90
+
91
+ ### Reorder
92
+
93
+ To insert an element after any specified item, use the `move_to_<field>_after` method.
94
+
95
+ ```ruby
96
+ target = Task.create(name: 'target') # pos: 'a0'
97
+ tasks = Task.create([
98
+ { name: 'task-1' }, # pos: 'a1'
99
+ { name: 'task-2' } # pos: 'a2'
100
+ ])
101
+
102
+ target.move_to_position_after(tasks.last)
103
+ # => true
104
+ target.position
105
+ # => 'a3'
106
+
107
+ # If no argument is passed, it will be inserted at the end of the list
108
+ tasks.first.move_to_position_after
109
+ # => true
110
+
111
+ Task.order(:position).pluck(:name, :position)
112
+ # => [["task-2", "a2"], ["target", "a3"], ["task-1", "a4"]]
113
+ ```
114
+
115
+ To insert an element before any specified item, use the `move_to_<field>_before` method.
116
+
117
+ ```ruby
118
+ tasks = Task.create([
119
+ { name: 'task-1' }, # pos: 'a0'
120
+ { name: 'task-2' } # pos: 'a1'
121
+ ])
122
+ target = Task.create(name: 'target') # pos: 'a2'
123
+
124
+ target.move_to_position_before(tasks.first)
125
+ target.position
126
+ # => 'Zz'
127
+
128
+ # If no argument is passed, it will be inserted at the start of the list
129
+ tasks.last.move_to_position_before
130
+ # => true
131
+
132
+ Task.order(:position).pluck(:name, :position)
133
+ # => [["task-2", "Zy"], ["target", "Zz"], ["task-1", "a0"]]
134
+ ```
135
+
136
+ The method you will likely use most often is `move_to_<field>_between`, which moves an element between two others!
137
+
138
+ ```ruby
139
+ tasks = Task.create([
140
+ { name: 'task-1' }, # pos: 'a0'
141
+ { name: 'task-2' } # pos: 'a1'
142
+ ])
143
+ target = Task.create(name: 'target') # pos: 'a2'
144
+
145
+ target.move_to_position_between(tasks.first, tasks.last)
146
+ # => true
147
+
148
+ target.position
149
+ # => 'a0V'
150
+
151
+ # If the first argument is nil, it behaves the same as `move_to_<field>_before`
152
+ # ex: target.move_to_position_between(nil, tasks.last)
153
+
154
+ # If the second argument is nil, it behaves the same as `move_to_<field>_after`
155
+ # ex: target.move_to_position_between(tasks.first, nil)
156
+ ```
157
+
158
+ ### Scope
159
+
160
+ You can use this when you want to manage independent positions within specific scopes, such as foreign keys.
161
+
162
+ ```ruby
163
+ # example
164
+ class Course < ApplicationRecord
165
+ has_many :chapters, dependent: :destroy
166
+ end
167
+
168
+ class Chapter < ApplicationRecord
169
+ belongs_to :course
170
+
171
+ narabikae :position, size: 100, scope: %i[course_id]
172
+ end
173
+
174
+ course = Course.create
175
+ other_course = Course.create
176
+
177
+ course.chapters.create
178
+ other_course.chapters.create
179
+
180
+ Chapter.pluck(:course_id, :position)
181
+ # => [[1, "a0"], [2, "a0"]]
182
+ ```
183
+
184
+ When the attribute declared in the scope is changed during an update, and there is no change in the value of the position field, the position will be automatically recalculated, and the record will move to the end of the list.
185
+
186
+ ```ruby
187
+ course = Course.create
188
+ other_course = Course.create
189
+
190
+ course.chapters.create
191
+ chapter = course.chapters.create # pos: 'a1'
192
+
193
+ chapter.course = other_course
194
+ chapter.save
195
+
196
+ chapter.position
197
+ # => 'a0'
198
+ ```
199
+
200
+ ### Retry generating position
201
+
202
+ Imagine the drag-and-drop functionality found in GitHub Projects or Zenhub, where items can be reordered. If two users try to insert different tickets between the same two tickets at the same time, their positions may overlap. In this case, the system will internally retry generating a unique position (up to 10 times by default).
203
+
204
+ If you want to increase the number of retries, you can use the "challenge" option to control the retry attempts, as shown below:
205
+
206
+ ```ruby
207
+ ticket.move_to_position_between(t1, t2, challenge: 15)
208
+ ```
209
+
210
+ > [!NOTE]
211
+ > Currently, if two users write to the database at exactly the same time, the system may not detect the duplicate positions, and the process will continue without error. This issue will be revisited if there is demand for a more robust solution in the future.
212
+
213
+ ## Questions, Feedback
214
+
215
+ Feel free to message me on Github (kazu-2020)
216
+
24
217
  ## Contributing
25
- Contribution directions go here.
218
+
219
+ Please wait a moment... 🙏
26
220
 
27
221
  ## License
222
+
28
223
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,51 @@
1
+ module Narabikae
2
+ class ActiveRecordExtension
3
+ def initialize(record, option)
4
+ @record = record
5
+ @option = option
6
+
7
+ @position_generator = Narabikae::Position.new(record, option)
8
+ end
9
+
10
+ def auto_set_position?
11
+ # check valid key for fractional_indexer
12
+ # when invalid key, raise FractionalIndexer::Error
13
+ FractionalIndexer.generate_key(prev_key: record.send(option.field))
14
+ option.scope.any? { |s| record.will_save_change_to_attribute?(s) } && !record.will_save_change_to_attribute?(option.field)
15
+ rescue FractionalIndexer::Error
16
+ true
17
+ end
18
+
19
+ def set_position
20
+ record.send("#{option.field}=", position_generator.create_last_position)
21
+ end
22
+
23
+ def move_to_after(target, **args)
24
+ new_position = position_generator.find_position_after(target, **args)
25
+ return false if new_position.blank?
26
+
27
+ record.send("#{option.field}=", new_position)
28
+ record.save
29
+ end
30
+
31
+ def move_to_before(target, **args)
32
+ new_position = position_generator.find_position_before(target, **args)
33
+ return false if new_position.blank?
34
+
35
+ record.send("#{option.field}=", new_position)
36
+ record.save
37
+ end
38
+
39
+ def move_to_between(prev_target, next_target, **args)
40
+ new_position = position_generator.find_position_between(prev_target, next_target, **args)
41
+ return false if new_position.blank?
42
+
43
+ record.send("#{option.field}=", new_position)
44
+ record.save
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :record, :option, :position_generator
50
+ end
51
+ end
@@ -0,0 +1,31 @@
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
@@ -0,0 +1,19 @@
1
+ module Narabikae
2
+ class Configuration
3
+ # Sets the base value for FractionalIndexer configuration.
4
+ #
5
+ # @param int [Integer] The base value can be 10, 62, 94, with the default being 94.
6
+ # @return [void]
7
+ def base=(int)
8
+ FractionalIndexer.configure do |config|
9
+ config.base = "base_#{int}".to_sym
10
+ end
11
+ end
12
+
13
+ # @return [Array] The string of digits configured for the FractionalIndexer.
14
+ # @see https://github.com/kazu-2020/fractional_indexer?tab=readme-ov-file#configure
15
+ def digits
16
+ FractionalIndexer.configuration.digits
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module Narabikae
2
+ class Option
3
+ attr_reader :field, :key_max_size, :scope
4
+
5
+ # Initializes a new instance of the Option class.
6
+ #
7
+ # @param field [Symbol]
8
+ # @param key_max_size [Integer] The maximum size of the key.
9
+ # @param scope [Array<Symbol>] The scope of the option.
10
+ def initialize(field:, key_max_size:, scope: [])
11
+ @field = field.to_sym
12
+ @key_max_size = key_max_size.to_i
13
+ @scope = scope.is_a?(Array) ? scope.map(&:to_sym) : []
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module Narabikae
2
+ class OptionStore
3
+ attr_reader :store
4
+
5
+ def initialize
6
+ @store = {}
7
+ end
8
+
9
+ def register!(field, option)
10
+ if store.key?(field)
11
+ raise Narabikae::Error, "the field `#{field}` is already registered"
12
+ end
13
+ if option.scope.include?(field)
14
+ raise Narabikae::Error, "dependency loop detected: #{option.scope}"
15
+ end
16
+ if option.scope.any? { |s| store.key?(s) }
17
+ raise Narabikae::Error, "the scope `#{option.scope}` is already registered as other field"
18
+ end
19
+
20
+ store[field] = option
21
+
22
+ option
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,151 @@
1
+ module Narabikae
2
+ class Position
3
+ # Initializes a new instance of the Position class.
4
+ #
5
+ # @param record [Object] Active Record object.
6
+ # @param option [Option]
7
+ def initialize(record, option)
8
+ @record = record
9
+ @option = option
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
+ # Finds the position after the specified target.
20
+ # If generated key is invalid(ex: it already exists),
21
+ # a new key is generated until the challenge count reaches the limit.
22
+ # challenge count is 10 by default.
23
+ #
24
+ # @param target [#send(field)]
25
+ # @param args [Hash] Additional arguments.
26
+ # @option args [Integer] :challenge The number of times to attempt finding a valid position.
27
+ # @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, **args)
29
+ merged_args = { challenge: 10 }.merge(args)
30
+ # when target is nil, try to generate key from the last position
31
+ key = FractionalIndexer.generate_key(
32
+ prev_key: target&.send(option.field) || current_last_position
33
+ )
34
+ return key if valid?(key)
35
+
36
+ (merged_args[:challenge] || 0).times do
37
+ key += random_fractional
38
+ return key if valid?(key)
39
+ end
40
+
41
+ nil
42
+ rescue FractionalIndexer::Error
43
+ nil
44
+ end
45
+
46
+ #
47
+ # Finds the position before the target position.
48
+ # If generated key is invalid(ex: it already exists),
49
+ # a new key is generated until the challenge count reaches the limit.
50
+ # challenge count is 10 by default.
51
+ #
52
+ # @example
53
+ # position = Position.new
54
+ # position.find_position_before(target, challenge: 5)
55
+ #
56
+ # @param target [#send(field)]
57
+ # @param args [Hash] Additional arguments.
58
+ # @option args [Integer] :challenge The number of times to attempt finding a valid position.
59
+ # @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, **args)
61
+ merged_args = { challenge: 10 }.merge(args)
62
+ # when target is nil, try to generate key from the first position
63
+ key = FractionalIndexer.generate_key(
64
+ next_key: target&.send(option.field) || current_first_position
65
+ )
66
+ return key if valid?(key)
67
+
68
+ (merged_args[:challenge] || 0).times do
69
+ key += random_fractional
70
+ return key if valid?(key)
71
+ end
72
+
73
+ nil
74
+ rescue FractionalIndexer::Error
75
+ nil
76
+ end
77
+
78
+ # Finds the position between two targets.
79
+ #
80
+ # @param prev_target [#send(field)] The previous target.
81
+ # @param next_target [#send(field)] The next target.
82
+ # @param args [Hash] Additional arguments.
83
+ # @option args [Integer] :challenge The number of times to attempt finding a valid position.
84
+ # @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, **args)
86
+ return find_position_before(next_target, **args) if prev_target.blank?
87
+ return find_position_after(prev_target, **args) if next_target.blank?
88
+
89
+ merged_args = { challenge: 10 }.merge(args)
90
+
91
+ prev_key, next_key = [ prev_target.send(option.field), next_target.send(option.field) ].minmax
92
+ key = FractionalIndexer.generate_key(
93
+ prev_key: prev_key,
94
+ next_key: next_key,
95
+ )
96
+ return key if valid?(key)
97
+
98
+ (merged_args[:challenge] || 0).times do
99
+ key += random_fractional
100
+ return key if valid?(key)
101
+ end
102
+
103
+ nil
104
+ rescue FractionalIndexer::Error
105
+ nil
106
+ end
107
+
108
+ private
109
+
110
+ attr_reader :record, :option
111
+
112
+ def capable?(key)
113
+ option.key_max_size >= key.size
114
+ end
115
+
116
+ def current_first_position
117
+ model.merge(model_scope).minimum(option.field)
118
+ end
119
+
120
+ def current_last_position
121
+ model.merge(model_scope).maximum(option.field)
122
+ end
123
+
124
+ def model
125
+ record.class.base_class
126
+ end
127
+
128
+ # generate a random fractional part
129
+ #
130
+ # @return [String] The random fractional part.
131
+ # @see https://github.com/kazu-2020/fractional_indexer?tab=readme-ov-file#fractional-part
132
+ def random_fractional
133
+ # `fractional` represents the fractional part, but to ensure that the last digit is not zero value (ex: base_62 => '0'), the range is set to [1..].
134
+ FractionalIndexer.configuration.digits[1..].sample
135
+ end
136
+
137
+ def model_scope
138
+ model.where(record.slice(*option.scope))
139
+ end
140
+
141
+ def uniq?(key)
142
+ model.where(option.field => key).merge(model_scope).empty?
143
+ end
144
+
145
+ def valid?(key)
146
+ return false if key.blank?
147
+
148
+ capable?(key) && uniq?(key)
149
+ end
150
+ end
151
+ end
@@ -1,3 +1,3 @@
1
1
  module Narabikae
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/narabikae.rb CHANGED
@@ -1,6 +1,75 @@
1
1
  require "narabikae/version"
2
- require "narabikae/railtie"
2
+
3
+ require "narabikae/active_record_extension"
4
+ require "narabikae/configuration"
5
+ require "narabikae/option"
6
+ require "narabikae/option_store"
7
+ require "narabikae/position"
8
+
9
+ require "fractional_indexer"
10
+ require "active_support"
11
+ require "active_record"
3
12
 
4
13
  module Narabikae
5
- # Your code goes here...
14
+ class Error < StandardError; end
15
+
16
+ @configuration = Narabikae::Configuration.new
17
+
18
+ def self.configure
19
+ yield configuration if block_given?
20
+
21
+ configuration
22
+ end
23
+
24
+ def self.configuration
25
+ @configuration
26
+ end
27
+
28
+ module Extension
29
+ extend ActiveSupport::Concern
30
+
31
+ class_methods do
32
+ def narabikae(field = :position, size:, scope: [])
33
+ option = narabikae_option_store.register!(
34
+ field.to_sym,
35
+ Narabikae::Option.new(field: field, key_max_size: size, scope: scope)
36
+ )
37
+
38
+ before_create do
39
+ extension = Narabikae::ActiveRecordExtension.new(self, option)
40
+ extension.set_position
41
+ end
42
+
43
+ before_update do
44
+ extension = Narabikae::ActiveRecordExtension.new(self, option)
45
+ extension.set_position if extension.auto_set_position?
46
+ end
47
+
48
+ define_method :"move_to_#{field}_after" do |target = nil, **args|
49
+ extension = Narabikae::ActiveRecordExtension.new(self, option)
50
+ extension.move_to_after(target, **args)
51
+ end
52
+
53
+ define_method :"move_to_#{field}_before" do |target = nil, **args|
54
+ extension = Narabikae::ActiveRecordExtension.new(self, option)
55
+ extension.move_to_before(target, **args)
56
+ end
57
+
58
+ define_method :"move_to_#{field}_between" do |prev_target = nil, next_target = nil, **args|
59
+ extension = Narabikae::ActiveRecordExtension.new(self, option)
60
+ extension.move_to_between(prev_target, next_target, **args)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def narabikae_option_store
67
+ @_narabikae_option_store ||= Narabikae::OptionStore.new
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ ActiveSupport.on_load :active_record do |base|
74
+ base.include Narabikae::Extension
6
75
  end
metadata CHANGED
@@ -1,35 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: narabikae
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - matazou
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-01 00:00:00.000000000 Z
11
+ date: 2024-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: fractional_indexer
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '7.1'
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
20
24
  - - ">="
21
25
  - !ruby/object:Gem::Version
22
- version: 7.1.3.2
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
23
34
  type: :runtime
24
35
  prerelease: false
25
36
  version_requirements: !ruby/object:Gem::Requirement
26
37
  requirements:
27
- - - "~>"
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 5.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
28
67
  - !ruby/object:Gem::Version
29
- version: '7.1'
68
+ version: 5.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rails-omakase
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
30
94
  - - ">="
31
95
  - !ruby/object:Gem::Version
32
- version: 7.1.3.2
96
+ version: '0'
33
97
  description: 'provides functionality similar to acts_as_list. However, by managing
34
98
  position using a fractional indexing system, it allows database record updates during
35
99
  reordering to be completed with only a single update (N = 1)!
@@ -44,9 +108,13 @@ files:
44
108
  - MIT-LICENSE
45
109
  - README.md
46
110
  - lib/narabikae.rb
47
- - lib/narabikae/railtie.rb
111
+ - lib/narabikae/active_record_extension.rb
112
+ - lib/narabikae/active_record_handler.rb
113
+ - lib/narabikae/configuration.rb
114
+ - lib/narabikae/option.rb
115
+ - lib/narabikae/option_store.rb
116
+ - lib/narabikae/position.rb
48
117
  - lib/narabikae/version.rb
49
- - lib/tasks/narabikae_tasks.rake
50
118
  homepage: https://github.com/kazu-2020/narabikae
51
119
  licenses:
52
120
  - MIT
@@ -72,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
140
  - !ruby/object:Gem::Version
73
141
  version: '0'
74
142
  requirements: []
75
- rubygems_version: 3.5.3
143
+ rubygems_version: 3.5.9
76
144
  signing_key:
77
145
  specification_version: 4
78
146
  summary: provides simple position management and sorting functionality for Active
@@ -1,4 +0,0 @@
1
- module Narabikae
2
- class Railtie < ::Rails::Railtie
3
- end
4
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :narabikae do
3
- # # Task goes here
4
- # end