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 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