acts_as_list 1.0.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/brendon/acts_as_list.svg?branch=master)](https://travis-ci.org/brendon/acts_as_list)
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/acts_as_list.svg)](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
|