acts_as_list 1.0.4 → 1.1.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: ece77b4a7a9c6c50d1d7a188c91c6dedd4c1dba4c8ece714c2922b339c3b5490
4
+ data.tar.gz: e7d925c76332e504d6a3db3706c4362b962d5bef2f9c882023bef99a0763e2d1
5
5
  SHA512:
6
- metadata.gz: 1bfced5d2dd4e50194eb8bdb92b244d40dd0a19df0ee663649510a965077a0d332715dc0100c1e9d006222e96e57745638c77812767b88ec7b9274dd350af4c1
7
- data.tar.gz: 485f6be53763f1f112095075b1c69d6e620f89018c2e3435f3500decf986e110c832f78ce5db6462d6233d13816516a2c1d59df1e3c172455439a11704b1c8b5
6
+ metadata.gz: 8ca617673d046ef18baab0a51d4e481ac10e7b044c569a9efb201c894be702aec66eb72cbc5364bb85b6374c144990fcc3c367b01cc62e0f431d87b6e41e0f4f
7
+ data.tar.gz: c1ce411f1aa503b28a837b714db52a4bcb42ae78542183f6641aa0d0f483c9c98b3521a4557554e9de539b12effeee6f901632636d61408e423c55d67287f47b
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: CI
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ tests:
7
+ name: Ruby ${{ matrix.ruby }}, ${{ matrix.gemfile }}, DB ${{ matrix.db }}
8
+ runs-on: ${{ matrix.os }}
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby:
13
+ - 2.4
14
+ - 2.5
15
+ - 2.6
16
+ - 2.7
17
+ - '3.0'
18
+ - 3.1
19
+ - 3.2
20
+ gemfile:
21
+ - gemfiles/rails_4_2.gemfile
22
+ - gemfiles/rails_5_0.gemfile
23
+ - gemfiles/rails_5_1.gemfile
24
+ - gemfiles/rails_5_2.gemfile
25
+ - gemfiles/rails_6_0.gemfile
26
+ - gemfiles/rails_6_1.gemfile
27
+ - gemfiles/rails_7_0.gemfile
28
+ db:
29
+ - sqlite
30
+ - mysql
31
+ - postgresql
32
+ exclude:
33
+ - ruby: 2.4
34
+ gemfile: gemfiles/rails_6_0.gemfile
35
+ - ruby: 2.4
36
+ gemfile: gemfiles/rails_6_1.gemfile
37
+ - ruby: 2.4
38
+ gemfile: gemfiles/rails_7_0.gemfile
39
+ - ruby: 2.5
40
+ gemfile: gemfiles/rails_7_0.gemfile
41
+ - ruby: 2.6
42
+ gemfile: gemfiles/rails_7_0.gemfile
43
+ - ruby: 2.7
44
+ gemfile: gemfiles/rails_4_2.gemfile
45
+ - ruby: 2.7
46
+ gemfile: gemfiles/rails_5_0.gemfile
47
+ db: sqlite
48
+ - ruby: '3.0'
49
+ gemfile: gemfiles/rails_4_2.gemfile
50
+ - ruby: '3.0'
51
+ gemfile: gemfiles/rails_5_0.gemfile
52
+ - ruby: '3.0'
53
+ gemfile: gemfiles/rails_5_1.gemfile
54
+ - ruby: '3.0'
55
+ gemfile: gemfiles/rails_5_2.gemfile
56
+ - ruby: 3.1
57
+ gemfile: gemfiles/rails_4_2.gemfile
58
+ - ruby: 3.1
59
+ gemfile: gemfiles/rails_5_0.gemfile
60
+ - ruby: 3.1
61
+ gemfile: gemfiles/rails_5_1.gemfile
62
+ - ruby: 3.1
63
+ gemfile: gemfiles/rails_5_2.gemfile
64
+ - ruby: 3.1
65
+ gemfile: gemfiles/rails_6_0.gemfile
66
+ - ruby: 3.2
67
+ gemfile: gemfiles/rails_4_2.gemfile
68
+ - ruby: 3.2
69
+ gemfile: gemfiles/rails_5_0.gemfile
70
+ - ruby: 3.2
71
+ gemfile: gemfiles/rails_5_1.gemfile
72
+ - ruby: 3.2
73
+ gemfile: gemfiles/rails_5_2.gemfile
74
+ - ruby: 3.2
75
+ gemfile: gemfiles/rails_6_0.gemfile
76
+ - ruby: 3.2
77
+ gemfile: gemfiles/rails_6_1.gemfile
78
+ os:
79
+ - ubuntu-latest
80
+ services:
81
+ mysql:
82
+ image: mysql:5.7
83
+ env:
84
+ MYSQL_ALLOW_EMPTY_PASSWORD: yes
85
+ ports:
86
+ - 3306:3306
87
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
88
+
89
+ postgres:
90
+ # Docker Hub image
91
+ image: postgres
92
+ # Provide the password for postgres
93
+ env:
94
+ POSTGRES_USER: postgres
95
+ POSTGRES_HOST_AUTH_METHOD: trust
96
+ ports:
97
+ - 5432:5432
98
+ # Set health checks to wait until postgres has started
99
+ options: >-
100
+ --health-cmd pg_isready
101
+ --health-interval 10s
102
+ --health-timeout 5s
103
+ --health-retries 5
104
+ env:
105
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
106
+ DB: ${{ matrix.db }}
107
+ steps:
108
+ - uses: actions/checkout@v3
109
+ - name: Set up Ruby
110
+ uses: ruby/setup-ruby@v1
111
+ with:
112
+ ruby-version: ${{ matrix.ruby }}
113
+ bundler-cache: true
114
+ - name: "Create MySQL database"
115
+ if: ${{ env.DB == 'mysql' }}
116
+ run: |
117
+ mysql -h 127.0.0.1 -u root -e 'create database acts_as_list;'
118
+ - name: "Create PostgreSQL database"
119
+ if: ${{ env.DB == 'postgresql' }}
120
+ run: |
121
+ psql -c 'create database acts_as_list;' -h localhost -U postgres
122
+ - name: Run tests
123
+ run: bundle exec rake
data/Appraisals CHANGED
@@ -36,5 +36,9 @@ appraise "rails-6-0" do
36
36
  end
37
37
 
38
38
  appraise "rails-6-1" do
39
- gem "activerecord", "6.1.0.rc1"
39
+ gem "activerecord", "~> 6.1.0"
40
+ end
41
+
42
+ appraise "rails-7-0" do
43
+ gem "activerecord", "~> 7.0.0"
40
44
  end
data/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## v1.1.0 - 2023-02-01
10
+
11
+ ### Fixed (Possibly Breaking)
12
+ - 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))
13
+ - 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])
14
+
9
15
  ## v1.0.4 - 2021-04-20
10
16
 
11
17
  ### Fixed
data/README.md CHANGED
@@ -133,26 +133,29 @@ The `position` column is set after validations are called, so you should not put
133
133
  If you need a scope by a non-association field you should pass an array, containing field name, to a scope:
134
134
  ```ruby
135
135
  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]
136
+ # `task_category` is a plain text field (e.g. 'work', 'shopping', 'meeting'), not an association
137
+ acts_as_list scope: [:task_category]
138
138
  end
139
139
  ```
140
140
 
141
141
  You can also add multiple scopes in this fashion:
142
142
  ```ruby
143
143
  class TodoItem < ActiveRecord::Base
144
- acts_as_list scope: [:kind, :owner_id]
144
+ belongs_to :todo_list
145
+ acts_as_list scope: [:task_category, :todo_list_id]
145
146
  end
146
147
  ```
147
148
 
148
149
  Furthermore, you can optionally include a hash of fixed parameters that will be included in all queries:
149
150
  ```ruby
150
151
  class TodoItem < ActiveRecord::Base
151
- acts_as_list scope: [:kind, :owner_id, deleted_at: nil]
152
+ belongs_to :todo_list
153
+ # or `discarded_at` if using discard
154
+ acts_as_list scope: [:task_category, :todo_list_id, deleted_at: nil]
152
155
  end
153
156
  ```
154
157
 
155
- This is useful when using this gem in conjunction with the popular [acts_as_paranoid](https://github.com/ActsAsParanoid/acts_as_paranoid) gem.
158
+ 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
159
 
157
160
  ## More Options
158
161
  - `column`
@@ -4,7 +4,7 @@ source "http://rubygems.org"
4
4
 
5
5
  gem "rake"
6
6
  gem "appraisal"
7
- gem "activerecord", "6.1.0.rc1"
7
+ gem "activerecord", "~> 6.1.0"
8
8
 
9
9
  group :development do
10
10
  gem "github_changelog_generator", "1.9.0"
@@ -0,0 +1,31 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rake"
6
+ gem "appraisal"
7
+ gem "activerecord", "~> 7.0.0"
8
+
9
+ group :development do
10
+ gem "github_changelog_generator", "~> 1.16.0"
11
+ end
12
+
13
+ group :test do
14
+ gem "minitest", "~> 5.0"
15
+ gem "timecop"
16
+ gem "mocha"
17
+ end
18
+
19
+ group :sqlite do
20
+ gem "sqlite3", "~> 1.4"
21
+ end
22
+
23
+ group :postgresql do
24
+ gem "pg", "~> 1.3.0"
25
+ end
26
+
27
+ group :mysql do
28
+ gem "mysql2", "~> 0.5.0"
29
+ end
30
+
31
+ gemspec path: "../"
@@ -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
@@ -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?
@@ -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
 
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module Acts
5
5
  module List
6
- VERSION = '1.0.4'
6
+ VERSION = '1.1.0'
7
7
  end
8
8
  end
9
9
  end
data/test/database.yml CHANGED
@@ -4,13 +4,15 @@ sqlite:
4
4
 
5
5
  mysql:
6
6
  adapter: mysql2
7
+ host: 127.0.0.1
7
8
  username: root
8
9
  password:
9
10
  database: acts_as_list
10
11
 
11
12
  postgresql:
12
13
  adapter: postgresql
14
+ host: localhost
13
15
  username: postgres
14
- password:
16
+ password: postgres
15
17
  database: acts_as_list
16
18
  min_messages: ERROR
@@ -34,5 +34,40 @@ module Shared
34
34
  new.reload
35
35
  assert !new.in_list?
36
36
  end
37
+
38
+ def test_collision_avoidance_with_explicit_position
39
+ first = NoAdditionMixin.create(parent_id: 20, pos: 1)
40
+ second = NoAdditionMixin.create(parent_id: 20, pos: 1)
41
+ third = NoAdditionMixin.create(parent_id: 30, pos: 1)
42
+
43
+ first.reload
44
+ second.reload
45
+ third.reload
46
+
47
+ assert_equal 2, first.pos
48
+ assert_equal 1, second.pos
49
+ assert_equal 1, third.pos
50
+
51
+ first.update(pos: 1)
52
+
53
+ first.reload
54
+ second.reload
55
+
56
+ assert_equal 1, first.pos
57
+ assert_equal 2, second.pos
58
+
59
+ first.update(parent_id: 30)
60
+
61
+ first.reload
62
+ second.reload
63
+ third.reload
64
+
65
+ assert_equal 1, first.pos
66
+ assert_equal 30, first.parent_id
67
+ assert_equal 1, second.pos
68
+ assert_equal 20, second.parent_id
69
+ assert_equal 2, third.pos
70
+ assert_equal 30, third.parent_id
71
+ end
37
72
  end
38
73
  end
data/test/test_list.rb CHANGED
@@ -604,6 +604,42 @@ class MultiDestroyTest < ActsAsListTestCase
604
604
  end
605
605
  end
606
606
 
607
+ class MultiUpdateTest < ActsAsListTestCase
608
+
609
+ def setup
610
+ setup_db
611
+ end
612
+
613
+ def test_multiple_updates_within_transaction
614
+ @page = ListMixin.create! id: 100, parent_id: nil, pos: 1
615
+ @row = ListMixin.create! parent_id: @page.id, pos: 1
616
+ @column1 = ListMixin.create! parent_id: @row.id, pos: 1
617
+ @column2 = ListMixin.create! parent_id: @row.id, pos: 2
618
+ @rich_text1 = ListMixin.create! parent_id: @column1.id, pos: 1
619
+ @rich_text2 = ListMixin.create! parent_id: @column2.id, pos: 1
620
+
621
+ ActiveRecord::Base.transaction do
622
+ @rich_text1.update!(parent_id: @column2.id, pos: 1)
623
+
624
+ assert_equal [@rich_text1.id, @rich_text2.id], ListMixin.where(parent_id: @column2.id).order('pos').map(&:id)
625
+ assert_equal [1, 2], ListMixin.where(parent_id: @column2.id).order('pos').map(&:pos)
626
+
627
+ @column1.destroy!
628
+ assert_equal [@column2.id], ListMixin.where(parent_id: @row.id).order('pos').map(&:id)
629
+ assert_equal [1], ListMixin.where(parent_id: @row.id).order('pos').map(&:pos)
630
+
631
+ @rich_text1.update!(parent_id: @page.id, pos: 1)
632
+ @rich_text2.update!(parent_id: @page.id, pos: 2)
633
+ @row.destroy!
634
+ @column2.destroy!
635
+ end
636
+
637
+ assert_equal(1, @page.reload.pos)
638
+ assert_equal [@rich_text1.id, @rich_text2.id], ListMixin.where(parent_id: @page.id).order('pos').map(&:id)
639
+ assert_equal [1, 2], ListMixin.where(parent_id: @page.id).order('pos').map(&:pos)
640
+ end
641
+ end
642
+
607
643
  #class TopAdditionMixin < Mixin
608
644
 
609
645
  class TopAdditionTest < ActsAsListTestCase
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_list
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Swanand Pagnis
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-04-19 00:00:00.000000000 Z
12
+ date: 2023-01-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -51,6 +51,8 @@ extra_rdoc_files: []
51
51
  files:
52
52
  - ".gemtest"
53
53
  - ".github/FUNDING.yml"
54
+ - ".github/dependabot.yml"
55
+ - ".github/workflows/ci.yml"
54
56
  - ".gitignore"
55
57
  - ".travis.yml"
56
58
  - Appraisals
@@ -66,6 +68,7 @@ files:
66
68
  - gemfiles/rails_5_2.gemfile
67
69
  - gemfiles/rails_6_0.gemfile
68
70
  - gemfiles/rails_6_1.gemfile
71
+ - gemfiles/rails_7_0.gemfile
69
72
  - init.rb
70
73
  - lib/acts_as_list.rb
71
74
  - lib/acts_as_list/active_record/acts/active_record.rb
@@ -118,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
121
  - !ruby/object:Gem::Version
119
122
  version: '0'
120
123
  requirements: []
121
- rubygems_version: 3.0.3
124
+ rubygems_version: 3.0.3.1
122
125
  signing_key:
123
126
  specification_version: 4
124
127
  summary: A gem adding sorting, reordering capabilities to an active_record model,