acts_as_list 1.0.4 → 1.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 +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/continuous_integration.yml +62 -0
- data/.gitignore +3 -2
- data/CHANGELOG.md +15 -0
- data/Gemfile +6 -21
- data/README.md +15 -7
- data/Rakefile +0 -8
- data/acts_as_list.gemspec +9 -4
- data/lib/acts_as_list/active_record/acts/callback_definer.rb +2 -4
- data/lib/acts_as_list/active_record/acts/list.rb +29 -33
- data/lib/acts_as_list/active_record/acts/scope_method_definer.rb +1 -2
- data/lib/acts_as_list/version.rb +1 -1
- data/test/helper.rb +17 -5
- data/test/shared_list.rb +131 -108
- data/test/shared_no_addition.rb +35 -0
- data/test/support/ci_database.yml +20 -0
- data/test/{database.yml → support/database.yml} +4 -2
- data/test/test_list.rb +172 -5
- metadata +103 -23
- data/.travis.yml +0 -55
- data/Appraisals +0 -40
- data/gemfiles/rails_4_2.gemfile +0 -32
- data/gemfiles/rails_5_0.gemfile +0 -31
- data/gemfiles/rails_5_1.gemfile +0 -31
- data/gemfiles/rails_5_2.gemfile +0 -31
- data/gemfiles/rails_6_0.gemfile +0 -31
- data/gemfiles/rails_6_1.gemfile +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97086f40e0296a92be25b988885cb618bd17323c31639c7b976e20e82005da54
|
4
|
+
data.tar.gz: 225e05bc8973871d0a7955abaf181180ff5b8de3d2b461278eeb260df3a49b2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d43449915c885f64e477bff8c4a9e0d581e632e4cfab61e0f313fc0b67c4507f6cda9e3aab5afb53357cc44fee664f873e7ca2b93b0f22a22b1e272ed1e39db
|
7
|
+
data.tar.gz: 24dfa6bf08290e1d18331ee5ad7c0eeb03ae1dce2191881503a889058136ccfb8007d0acbee9bc57e5363073740a8efec872225b4978922d0a1f1dcf49c3ca0a
|
@@ -0,0 +1,62 @@
|
|
1
|
+
name: Continuous Integration
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request:
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
tests:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
name: Ruby ${{ matrix.ruby }}, DB ${{ matrix.db }}, Rails ${{ matrix.rails }}
|
13
|
+
strategy:
|
14
|
+
fail-fast: false
|
15
|
+
matrix:
|
16
|
+
ruby:
|
17
|
+
- '3.0'
|
18
|
+
- '3.1'
|
19
|
+
- '3.2'
|
20
|
+
- '3.3'
|
21
|
+
rails:
|
22
|
+
- '6.1'
|
23
|
+
- '7.0'
|
24
|
+
- '7.1'
|
25
|
+
db:
|
26
|
+
- mysql
|
27
|
+
- postgresql
|
28
|
+
- sqlite
|
29
|
+
exclude:
|
30
|
+
- rails: '7.0'
|
31
|
+
ruby: '3.1'
|
32
|
+
- rails: '7.0'
|
33
|
+
ruby: '3.2'
|
34
|
+
- rails: '7.0'
|
35
|
+
ruby: '3.3'
|
36
|
+
env:
|
37
|
+
DB: ${{ matrix.db }}
|
38
|
+
RAILS_VERSION: ${{ matrix.rails }}
|
39
|
+
steps:
|
40
|
+
- uses: actions/checkout@v4
|
41
|
+
- name: Set up Ruby
|
42
|
+
uses: ruby/setup-ruby@v1
|
43
|
+
with:
|
44
|
+
ruby-version: ${{ matrix.ruby }}
|
45
|
+
bundler-cache: true
|
46
|
+
- name: Enable MySQL
|
47
|
+
if: ${{ matrix.db == 'mysql' }}
|
48
|
+
run: sudo systemctl start mysql.service
|
49
|
+
- name: Create MySQL Database
|
50
|
+
if: ${{ matrix.db == 'mysql' }}
|
51
|
+
run: mysql -u root -proot -e 'CREATE DATABASE runner;'
|
52
|
+
- name: Enable PostgreSQL
|
53
|
+
if: ${{ matrix.db == 'postgresql' }}
|
54
|
+
run: sudo systemctl start postgresql.service
|
55
|
+
- name: Create PostgreSQL User
|
56
|
+
if: ${{ matrix.db == 'postgresql' }}
|
57
|
+
run: sudo -u postgres -i createuser runner -s
|
58
|
+
- name: Create PostgreSQL Database
|
59
|
+
if: ${{ matrix.db == 'postgresql' }}
|
60
|
+
run: createdb runner
|
61
|
+
- name: Run the default task
|
62
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## Unreleased
|
8
8
|
|
9
|
+
## v1.2.0 - 2024-06-03
|
10
|
+
|
11
|
+
### Added
|
12
|
+
- Add support for composite primary keys. [\#430](https://github.com/brendon/acts_as_list/pull/430) ([divagueame])
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
- Refactored CI testing framework and removed the Appraisals gem. Use RAILS_VERSION to switch the Rails version you are currently running the tests against.
|
16
|
+
- We now only explicitly test against Rails 6.1+ and Ruby 3.0+.
|
17
|
+
|
18
|
+
## v1.1.0 - 2023-02-01
|
19
|
+
|
20
|
+
### Fixed (Possibly Breaking)
|
21
|
+
- Use `after_save` instead of `after_commit` for `clear_scope_changed` callback [\#407](https://github.com/brendon/acts_as_list/pull/407) ([@Flixt](https://github.com/Flixt))
|
22
|
+
- Rename `add_to_list_top` and `add_to_list_bottom` private methods to `avoid_collision` that handles both cases as well as the case where `:add_new_at` is `nil`. Setting an explicit position when `:add_new_at` is `nil` will now shuffle other items out of the way if necessary. *This may break existing workarounds you have in place to deal with this bug*. [\#411](https://github.com/brendon/acts_as_list/pull/411). ([brendon])
|
23
|
+
|
9
24
|
## v1.0.4 - 2021-04-20
|
10
25
|
|
11
26
|
### Fixed
|
data/Gemfile
CHANGED
@@ -1,28 +1,13 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
+
# Specify your gem's dependencies in positioning.gemspec
|
3
4
|
gemspec
|
4
5
|
|
5
|
-
gem "rake"
|
6
|
-
gem "appraisal"
|
6
|
+
gem "rake", "~> 13.0"
|
7
7
|
|
8
|
-
|
9
|
-
gem "github_changelog_generator", "1.9.0"
|
10
|
-
end
|
11
|
-
|
12
|
-
group :test do
|
13
|
-
gem "minitest", "~> 5.0"
|
14
|
-
gem "timecop"
|
15
|
-
gem "mocha"
|
16
|
-
end
|
17
|
-
|
18
|
-
group :sqlite do
|
19
|
-
gem "sqlite3", "~> 1.4"
|
20
|
-
end
|
21
|
-
|
22
|
-
group :postgresql do
|
23
|
-
gem "pg", "~> 1.2.0"
|
24
|
-
end
|
8
|
+
gem "minitest", "~> 5.0"
|
25
9
|
|
26
|
-
|
27
|
-
gem "
|
10
|
+
if ENV["RAILS_VERSION"]
|
11
|
+
gem "activerecord", ENV["RAILS_VERSION"]
|
12
|
+
gem "activesupport", ENV["RAILS_VERSION"]
|
28
13
|
end
|
data/README.md
CHANGED
@@ -4,6 +4,11 @@
|
|
4
4
|
[](https://travis-ci.org/brendon/acts_as_list)
|
5
5
|
[](https://badge.fury.io/rb/acts_as_list)
|
6
6
|
|
7
|
+
## ANNOUNCING: Positioning, the gem
|
8
|
+
As maintainer of both Acts As List and the Ranked Model gems, I've become intimately aquainted with the strengths and weaknesses of each. I ended up writing a small scale Rails Concern for positioning database rows for a recent project and it worked really well so I've decided to release it as a gem: [Positioning](https://github.com/brendon/positioning)
|
9
|
+
|
10
|
+
Positioning works similarly to Acts As List in that it maintains a sequential list of integer values as positions. It differs in that it encourages a unique constraints on the position column and supports multiple lists per database table. It borrows Ranked Model's concept of relative positioning. I encourage you to check it out and give it a whirl on your project!
|
11
|
+
|
7
12
|
## Description
|
8
13
|
|
9
14
|
This `acts_as` extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a `position` column defined as an integer on the mapped database table.
|
@@ -117,7 +122,7 @@ When using PostgreSQL, it is also possible to leave this migration up to the dat
|
|
117
122
|
ROW_NUMBER() OVER (
|
118
123
|
PARTITION BY todo_list_id
|
119
124
|
ORDER BY updated_at
|
120
|
-
)
|
125
|
+
) AS new_position
|
121
126
|
FROM todo_items
|
122
127
|
) AS mapping
|
123
128
|
WHERE todo_items.id = mapping.id;
|
@@ -133,26 +138,29 @@ The `position` column is set after validations are called, so you should not put
|
|
133
138
|
If you need a scope by a non-association field you should pass an array, containing field name, to a scope:
|
134
139
|
```ruby
|
135
140
|
class TodoItem < ActiveRecord::Base
|
136
|
-
# `
|
137
|
-
acts_as_list scope: [:
|
141
|
+
# `task_category` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association
|
142
|
+
acts_as_list scope: [:task_category]
|
138
143
|
end
|
139
144
|
```
|
140
145
|
|
141
146
|
You can also add multiple scopes in this fashion:
|
142
147
|
```ruby
|
143
148
|
class TodoItem < ActiveRecord::Base
|
144
|
-
|
149
|
+
belongs_to :todo_list
|
150
|
+
acts_as_list scope: [:task_category, :todo_list_id]
|
145
151
|
end
|
146
152
|
```
|
147
153
|
|
148
154
|
Furthermore, you can optionally include a hash of fixed parameters that will be included in all queries:
|
149
155
|
```ruby
|
150
156
|
class TodoItem < ActiveRecord::Base
|
151
|
-
|
157
|
+
belongs_to :todo_list
|
158
|
+
# or `discarded_at` if using discard
|
159
|
+
acts_as_list scope: [:task_category, :todo_list_id, deleted_at: nil]
|
152
160
|
end
|
153
161
|
```
|
154
162
|
|
155
|
-
This is useful when using this gem in conjunction with the popular [acts_as_paranoid](https://github.com/ActsAsParanoid/acts_as_paranoid)
|
163
|
+
This is useful when using this gem in conjunction with the popular [acts_as_paranoid](https://github.com/ActsAsParanoid/acts_as_paranoid) or [discard](https://github.com/jhawthorn/discard) gems.
|
156
164
|
|
157
165
|
## More Options
|
158
166
|
- `column`
|
@@ -192,7 +200,7 @@ class TodoItem < ActiveRecord::Base
|
|
192
200
|
end
|
193
201
|
|
194
202
|
class TodoAttachment < ActiveRecord::Base
|
195
|
-
belongs_to :
|
203
|
+
belongs_to :todo_item
|
196
204
|
acts_as_list scope: :todo_item
|
197
205
|
end
|
198
206
|
|
data/Rakefile
CHANGED
@@ -32,11 +32,3 @@ rescue LoadError
|
|
32
32
|
rescue StandardError
|
33
33
|
puts "RDocTask is not supported on this platform."
|
34
34
|
end
|
35
|
-
|
36
|
-
# See https://github.com/skywinder/github-changelog-generator#rake-task for details
|
37
|
-
# and github_changelog_generator --help for available options
|
38
|
-
require 'github_changelog_generator/task'
|
39
|
-
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
40
|
-
config.project = 'acts_as_list'
|
41
|
-
config.user = 'brendon'
|
42
|
-
end
|
data/acts_as_list.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
10
|
s.authors = ["Swanand Pagnis", "Brendon Muir"]
|
11
11
|
s.email = %w(swanand.pagnis@gmail.com brendon@spikeatschool.co.nz)
|
12
|
-
s.homepage = "
|
12
|
+
s.homepage = "https://github.com/brendon/acts_as_list"
|
13
13
|
s.summary = "A gem adding sorting, reordering capabilities to an active_record model, allowing it to act as a list"
|
14
14
|
s.description = 'This "acts_as" extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a "position" column defined as an integer on the mapped database table.'
|
15
15
|
s.license = "MIT"
|
@@ -27,8 +27,13 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.executables = `git ls-files -- bin/*`.split("\n").map {|f| File.basename(f)}
|
28
28
|
s.require_paths = ["lib"]
|
29
29
|
|
30
|
-
|
31
30
|
# Dependencies (installed via "bundle install")
|
32
|
-
s.add_dependency "activerecord", ">=
|
33
|
-
s.
|
31
|
+
s.add_dependency "activerecord", ">= 6.1"
|
32
|
+
s.add_dependency "activesupport", ">= 6.1"
|
33
|
+
s.add_development_dependency "minitest-hooks", "~> 1.5.1"
|
34
|
+
s.add_development_dependency "mocha", "~> 2.1.0"
|
35
|
+
s.add_development_dependency "timecop", "~> 0.9.8"
|
36
|
+
s.add_development_dependency "mysql2", "~> 0.5.6"
|
37
|
+
s.add_development_dependency "pg", "~> 1.5.5"
|
38
|
+
s.add_development_dependency "sqlite3", "~> 1.7.2"
|
34
39
|
end
|
@@ -11,11 +11,9 @@ module ActiveRecord::Acts::List::CallbackDefiner #:nodoc:
|
|
11
11
|
before_update :check_scope, unless: :act_as_list_no_update?
|
12
12
|
after_update :update_positions, unless: :act_as_list_no_update?
|
13
13
|
|
14
|
-
|
14
|
+
after_save :clear_scope_changed
|
15
15
|
|
16
|
-
|
17
|
-
before_create "add_to_list_#{add_new_at}".to_sym, unless: :act_as_list_no_update?
|
18
|
-
end
|
16
|
+
before_create :avoid_collision, unless: :act_as_list_no_update?
|
19
17
|
end
|
20
18
|
end
|
21
19
|
end
|
@@ -171,7 +171,7 @@ module ActiveRecord
|
|
171
171
|
limit ||= acts_as_list_list.count
|
172
172
|
acts_as_list_list.
|
173
173
|
where("#{quoted_position_column_with_table_name} <= ?", current_position).
|
174
|
-
where
|
174
|
+
where.not(primary_key_condition).
|
175
175
|
reorder(acts_as_list_order_argument(:desc)).
|
176
176
|
limit(limit)
|
177
177
|
end
|
@@ -188,7 +188,7 @@ module ActiveRecord
|
|
188
188
|
limit ||= acts_as_list_list.count
|
189
189
|
acts_as_list_list.
|
190
190
|
where("#{quoted_position_column_with_table_name} >= ?", current_position).
|
191
|
-
where
|
191
|
+
where.not(primary_key_condition).
|
192
192
|
reorder(acts_as_list_order_argument(:asc)).
|
193
193
|
limit(limit)
|
194
194
|
end
|
@@ -229,36 +229,27 @@ module ActiveRecord
|
|
229
229
|
acts_as_list_class.default_scoped.unscope(:select, :where).where(scope_condition)
|
230
230
|
end
|
231
231
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
true
|
248
|
-
end
|
249
|
-
|
250
|
-
def add_to_list_bottom
|
251
|
-
if assume_default_position?
|
252
|
-
self[position_column] = bottom_position_in_list.to_i + 1
|
232
|
+
def avoid_collision
|
233
|
+
case add_new_at
|
234
|
+
when :top
|
235
|
+
if assume_default_position?
|
236
|
+
increment_positions_on_all_items
|
237
|
+
self[position_column] = acts_as_list_top
|
238
|
+
else
|
239
|
+
increment_positions_on_lower_items(self[position_column], id)
|
240
|
+
end
|
241
|
+
when :bottom
|
242
|
+
if assume_default_position?
|
243
|
+
self[position_column] = bottom_position_in_list.to_i + 1
|
244
|
+
else
|
245
|
+
increment_positions_on_lower_items(self[position_column], id)
|
246
|
+
end
|
253
247
|
else
|
254
|
-
increment_positions_on_lower_items(self[position_column], id)
|
248
|
+
increment_positions_on_lower_items(self[position_column], id) if position_changed
|
255
249
|
end
|
256
250
|
|
257
|
-
# Make sure we know that we've processed this scope change already
|
258
|
-
|
259
|
-
|
260
|
-
# Don't halt the callback chain
|
261
|
-
true
|
251
|
+
@scope_changed = false # Make sure we know that we've processed this scope change already
|
252
|
+
return true # Don't halt the callback chain
|
262
253
|
end
|
263
254
|
|
264
255
|
def assume_default_position?
|
@@ -282,7 +273,7 @@ module ActiveRecord
|
|
282
273
|
scope = acts_as_list_list
|
283
274
|
|
284
275
|
if except
|
285
|
-
scope = scope.where(
|
276
|
+
scope = scope.where.not(primary_key_condition(except.id))
|
286
277
|
end
|
287
278
|
|
288
279
|
scope.in_list.reorder(acts_as_list_order_argument(:desc)).first
|
@@ -309,7 +300,7 @@ module ActiveRecord
|
|
309
300
|
scope = acts_as_list_list
|
310
301
|
|
311
302
|
if avoid_id
|
312
|
-
scope = scope.where(
|
303
|
+
scope = scope.where.not(primary_key_condition(avoid_id))
|
313
304
|
end
|
314
305
|
|
315
306
|
if sequential_updates?
|
@@ -350,7 +341,7 @@ module ActiveRecord
|
|
350
341
|
scope = acts_as_list_list
|
351
342
|
|
352
343
|
if avoid_id
|
353
|
-
scope = scope.where(
|
344
|
+
scope = scope.where.not(primary_key_condition(avoid_id))
|
354
345
|
end
|
355
346
|
|
356
347
|
if old_position < new_position
|
@@ -454,7 +445,7 @@ module ActiveRecord
|
|
454
445
|
send('decrement_positions_on_lower_items') if lower_item
|
455
446
|
cached_changes.each { |attribute, values| send("#{attribute}=", values[1]) }
|
456
447
|
|
457
|
-
|
448
|
+
avoid_collision
|
458
449
|
end
|
459
450
|
end
|
460
451
|
|
@@ -489,6 +480,11 @@ module ActiveRecord
|
|
489
480
|
version = Gem.loaded_specs['activerecord'].version
|
490
481
|
requirement.satisfied_by?(version)
|
491
482
|
end
|
483
|
+
|
484
|
+
def primary_key_condition(id = nil)
|
485
|
+
primary_keys = Array.wrap(self.class.primary_key)
|
486
|
+
id ? primary_keys.zip(Array.wrap(id)).to_h : slice(*primary_keys)
|
487
|
+
end
|
492
488
|
end
|
493
489
|
|
494
490
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
require "active_support/inflector"
|
3
3
|
|
4
4
|
module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
5
|
-
extend ActiveSupport::Inflector
|
6
5
|
|
7
6
|
def self.call(caller_class, scope)
|
8
7
|
scope = idify(caller_class, scope) if scope.is_a?(Symbol)
|
@@ -71,7 +70,7 @@ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
|
71
70
|
if caller_class.reflections.key?(name.to_s)
|
72
71
|
caller_class.reflections[name.to_s].foreign_key.to_sym
|
73
72
|
else
|
74
|
-
foreign_key(name).to_sym
|
73
|
+
ActiveSupport::Inflector.foreign_key(name).to_sym
|
75
74
|
end
|
76
75
|
end
|
77
76
|
end
|
data/lib/acts_as_list/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -16,6 +16,8 @@ require "minitest/autorun"
|
|
16
16
|
require "mocha/minitest"
|
17
17
|
require "#{File.dirname(__FILE__)}/../init"
|
18
18
|
|
19
|
+
ENV["DB"] = "mysql" unless ENV["DB"]
|
20
|
+
|
19
21
|
if defined?(ActiveRecord::VERSION) &&
|
20
22
|
ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2
|
21
23
|
|
@@ -23,8 +25,10 @@ if defined?(ActiveRecord::VERSION) &&
|
|
23
25
|
ActiveRecord::Base.raise_in_transactional_callbacks = true
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
database_configuration = ENV["CI"] ? "test/support/ci_database.yml" : "test/support/database.yml"
|
29
|
+
|
30
|
+
ActiveRecord::Base.configurations = YAML.safe_load(IO.read(database_configuration))
|
31
|
+
ActiveRecord::Base.establish_connection(ENV["DB"].to_sym)
|
28
32
|
ActiveRecord::Schema.verbose = false
|
29
33
|
|
30
34
|
def teardown_db
|
@@ -53,8 +57,8 @@ def assert_equal_or_nil(a, b)
|
|
53
57
|
end
|
54
58
|
|
55
59
|
def assert_no_deprecation_warning_raised_by(failure_message = 'ActiveRecord deprecation warning raised when we didn\'t expect it', pass_message = 'No ActiveRecord deprecation raised')
|
56
|
-
original_behavior =
|
57
|
-
|
60
|
+
original_behavior = active_record_deprecator.behavior
|
61
|
+
active_record_deprecator.behavior = :raise
|
58
62
|
begin
|
59
63
|
yield
|
60
64
|
rescue ActiveSupport::DeprecationException => e
|
@@ -65,5 +69,13 @@ def assert_no_deprecation_warning_raised_by(failure_message = 'ActiveRecord depr
|
|
65
69
|
pass pass_message
|
66
70
|
end
|
67
71
|
ensure
|
68
|
-
|
72
|
+
active_record_deprecator.behavior = original_behavior
|
73
|
+
end
|
74
|
+
|
75
|
+
def active_record_deprecator
|
76
|
+
if ActiveRecord::VERSION::MAJOR == 7 && ActiveRecord::VERSION::MINOR >= 1 || ActiveRecord::VERSION::MAJOR > 7
|
77
|
+
ActiveRecord.deprecator
|
78
|
+
else
|
79
|
+
ActiveSupport::Deprecation
|
80
|
+
end
|
69
81
|
end
|