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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a36b9ed8db57946d5ac96c829c68ce4f4107068628827840140e9dadb3c682fa
4
- data.tar.gz: 60b6890922d26b913aed5a212eae7d228629c1ce3721dd0818ec2e5069697d25
3
+ metadata.gz: 97086f40e0296a92be25b988885cb618bd17323c31639c7b976e20e82005da54
4
+ data.tar.gz: 225e05bc8973871d0a7955abaf181180ff5b8de3d2b461278eeb260df3a49b2c
5
5
  SHA512:
6
- metadata.gz: 1bfced5d2dd4e50194eb8bdb92b244d40dd0a19df0ee663649510a965077a0d332715dc0100c1e9d006222e96e57745638c77812767b88ec7b9274dd350af4c1
7
- data.tar.gz: 485f6be53763f1f112095075b1c69d6e620f89018c2e3435f3500decf986e110c832f78ce5db6462d6233d13816516a2c1d59df1e3c172455439a11704b1c8b5
6
+ metadata.gz: 6d43449915c885f64e477bff8c4a9e0d581e632e4cfab61e0f313fc0b67c4507f6cda9e3aab5afb53357cc44fee664f873e7ca2b93b0f22a22b1e272ed1e39db
7
+ data.tar.gz: 24dfa6bf08290e1d18331ee5ad7c0eeb03ae1dce2191881503a889058136ccfb8007d0acbee9bc57e5363073740a8efec872225b4978922d0a1f1dcf49c3ca0a
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -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
@@ -7,6 +7,7 @@ pkg/*
7
7
  .rbenv-version
8
8
  .ruby-gemset
9
9
  .ruby-version
10
- # Appraisal generated lockfiles
11
- *.gemfile.lock
12
10
  .DS_Store
11
+ /tmp/
12
+ /db/
13
+ file::memory*
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
- group :development do
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
- group :mysql do
27
- gem "mysql2", "~> 0.5.0"
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
- ) as new_position
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
- # `kind` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association
137
- acts_as_list scope: [:kind]
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
- acts_as_list scope: [:kind, :owner_id]
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
- acts_as_list scope: [:kind, :owner_id, deleted_at: nil]
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) gem.
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 :todo_list
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 = "http://github.com/brendon/acts_as_list"
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", ">= 4.2"
33
- s.add_development_dependency "bundler", ">= 1.0.0"
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
- after_commit :clear_scope_changed
14
+ after_save :clear_scope_changed
15
15
 
16
- if add_new_at.present?
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("#{quoted_table_name}.#{self.class.primary_key} != ?", self.send(self.class.primary_key)).
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("#{quoted_table_name}.#{self.class.primary_key} != ?", self.send(self.class.primary_key)).
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
- # Poorly named methods. They will insert the item at the desired position if the position
233
- # has been set manually using position=, not necessarily the top or bottom of the list:
234
-
235
- def add_to_list_top
236
- if assume_default_position?
237
- increment_positions_on_all_items
238
- self[position_column] = acts_as_list_top
239
- else
240
- increment_positions_on_lower_items(self[position_column], id)
241
- end
242
-
243
- # Make sure we know that we've processed this scope change already
244
- @scope_changed = false
245
-
246
- # Don't halt the callback chain
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
- @scope_changed = false
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("#{quoted_table_name}.#{self.class.primary_key} != ?", except.id)
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("#{quoted_table_name}.#{self.class.primary_key} != ?", avoid_id)
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("#{quoted_table_name}.#{self.class.primary_key} != ?", avoid_id)
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
- send("add_to_list_#{add_new_at}") if add_new_at.present?
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
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module Acts
5
5
  module List
6
- VERSION = '1.0.4'
6
+ VERSION = '1.2.0'
7
7
  end
8
8
  end
9
9
  end
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
- db_config = YAML.load_file(File.expand_path("../database.yml", __FILE__)).fetch(ENV["DB"] || "sqlite")
27
- ActiveRecord::Base.establish_connection(db_config)
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 = ActiveSupport::Deprecation.behavior
57
- ActiveSupport::Deprecation.behavior = :raise
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
- ActiveSupport::Deprecation.behavior = original_behavior
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