acts_as_list 1.0.4 → 1.2.6
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/CHANGELOG.md +47 -4
- data/README.md +17 -9
- data/Rakefile +0 -8
- data/lib/acts_as_list/active_record/acts/callback_definer.rb +2 -4
- data/lib/acts_as_list/active_record/acts/list.rb +38 -34
- data/lib/acts_as_list/active_record/acts/position_column_method_definer.rb +15 -6
- data/lib/acts_as_list/active_record/acts/scope_method_definer.rb +25 -5
- data/lib/acts_as_list/active_record/acts/sequential_updates_method_definer.rb +15 -15
- data/lib/acts_as_list/active_record/acts/with_connection.rb +25 -0
- data/lib/acts_as_list/version.rb +1 -1
- metadata +18 -61
- data/.github/FUNDING.yml +0 -3
- data/.gitignore +0 -12
- data/.travis.yml +0 -55
- data/Appraisals +0 -40
- data/Gemfile +0 -28
- data/acts_as_list.gemspec +0 -34
- 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
- data/test/database.yml +0 -16
- data/test/helper.rb +0 -69
- data/test/shared.rb +0 -12
- data/test/shared_array_scope_list.rb +0 -177
- data/test/shared_list.rb +0 -324
- data/test/shared_list_sub.rb +0 -188
- data/test/shared_no_addition.rb +0 -38
- data/test/shared_quoting.rb +0 -23
- data/test/shared_top_addition.rb +0 -110
- data/test/shared_zero_based.rb +0 -104
- data/test/test_default_scope_with_select.rb +0 -33
- data/test/test_joined_list.rb +0 -61
- data/test/test_list.rb +0 -1086
- data/test/test_no_update_for_extra_classes.rb +0 -131
- data/test/test_no_update_for_scope_destruction.rb +0 -69
- data/test/test_no_update_for_subclasses.rb +0 -56
- data/test/test_scope_with_user_defined_foreign_key.rb +0 -42
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 957cacf5cbca5ad0bbc4f543856f54a56cd55e9ec4513bc52467229a92690481
|
|
4
|
+
data.tar.gz: a9917febbae3f5f281ef4ede81bce9843e8224cfb444fd19a2ebc6fcc8a89aad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c37e7b4e1ffe907f923072f43d4b587f7a55cf6b34e637b53af8bb73062365aa7328e020b50a1d6c63734dd41bf7a6427fa47485ba5ea5dc2d17806743f383d5
|
|
7
|
+
data.tar.gz: 63c7a691d02b65619e1203fa4eb35316df1e46c4ab8e4e103dcd5e869a762449abac2e31a45671abc4b2291c442bcec8e95f51f56a3a918a22df94de47378e12
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## v1.2.6 - 2025-10-21
|
|
10
|
+
|
|
11
|
+
- Expose configuration via class-level method (acts_as_list_options) [\#447](https://github.com/brendon/acts_as_list/pull/447) ([anthony0030])
|
|
12
|
+
|
|
13
|
+
## v1.2.5 - 2025-10-21
|
|
14
|
+
|
|
15
|
+
- Fix crash when deleting an association with composite primary keys [\#450](https://github.com/brendon/acts_as_list/pull/450) ([smathieu])
|
|
16
|
+
|
|
17
|
+
## v1.2.4 - 2024-11-20
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- funding_uri to gemspec
|
|
21
|
+
|
|
22
|
+
## v1.2.3 - 2024-10-14
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- Use `.with_connection do |connection|` where possible instead of `.connection` [\#441](https://github.com/brendon/acts_as_list/pull/441) ([flood4life])
|
|
26
|
+
|
|
27
|
+
## v1.2.2 - 2024-07-16
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- Updated .gemspec to exclude unnecessary files from the gem package. [\#437](https://github.com/brendon/acts_as_list/pull/437) ([f440])
|
|
31
|
+
|
|
32
|
+
## v1.2.1 - 2024-06-06
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- We no longer delegate the class `connection` method to the instance. [\#436](https://github.com/brendon/acts_as_list/pull/436) ([davidcelis])
|
|
36
|
+
|
|
37
|
+
## v1.2.0 - 2024-06-03
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- Add support for composite primary keys. [\#430](https://github.com/brendon/acts_as_list/pull/430) ([divagueame])
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- Refactored CI testing framework and removed the Appraisals gem. Use RAILS_VERSION to switch the Rails version you are currently running the tests against.
|
|
44
|
+
- We now only explicitly test against Rails 6.1+ and Ruby 3.0+.
|
|
45
|
+
|
|
46
|
+
## v1.1.0 - 2023-02-01
|
|
47
|
+
|
|
48
|
+
### Fixed (Possibly Breaking)
|
|
49
|
+
- 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))
|
|
50
|
+
- 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])
|
|
51
|
+
|
|
9
52
|
## v1.0.4 - 2021-04-20
|
|
10
53
|
|
|
11
54
|
### Fixed
|
|
@@ -250,7 +293,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
250
293
|
**Closed issues:**
|
|
251
294
|
|
|
252
295
|
- DEPRECATION WARNING on rails 5.0 as of acts\_as\_list 0.9 [\#251](https://github.com/brendon/acts_as_list/issues/251)
|
|
253
|
-
-
|
|
296
|
+
- higher\_items returns items with the same position value [\#247](https://github.com/brendon/acts_as_list/issues/247)
|
|
254
297
|
- Broken with unique constraint on position [\#245](https://github.com/brendon/acts_as_list/issues/245)
|
|
255
298
|
|
|
256
299
|
**Merged pull requests:**
|
|
@@ -478,7 +521,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
478
521
|
- Fix sanitize\_sql\_hash\_for\_conditions deprecation warning in Rails 4.2 [\#140](https://github.com/brendon/acts_as_list/pull/140) ([eagletmt](https://github.com/eagletmt))
|
|
479
522
|
- Simpler method to find the subclass name [\#139](https://github.com/brendon/acts_as_list/pull/139) ([brendon](https://github.com/brendon))
|
|
480
523
|
- Rails4 enum column support [\#130](https://github.com/brendon/acts_as_list/pull/130) ([arunagw](https://github.com/arunagw))
|
|
481
|
-
- use eval for
|
|
524
|
+
- use eval for determining the self.class.name useful when this is used in an abstract class [\#123](https://github.com/brendon/acts_as_list/pull/123) ([flarik](https://github.com/flarik))
|
|
482
525
|
|
|
483
526
|
## [0.5.0](https://github.com/brendon/acts_as_list/tree/0.5.0) (2014-10-31)
|
|
484
527
|
[Full Changelog](https://github.com/brendon/acts_as_list/compare/0.4.0...0.5.0)
|
|
@@ -618,7 +661,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
618
661
|
- acts\_as\_list :scope =\> "doesnt\_seem\_to\_work" [\#12](https://github.com/brendon/acts_as_list/issues/12)
|
|
619
662
|
- don't work perfectly with default\_scope [\#11](https://github.com/brendon/acts_as_list/issues/11)
|
|
620
663
|
- MySQL: Position column MUST NOT have default [\#10](https://github.com/brendon/acts_as_list/issues/10)
|
|
621
|
-
- insert\_at fails on postgresql w/ non-null constraint on
|
|
664
|
+
- insert\_at fails on postgresql w/ non-null constraint on position\_column [\#8](https://github.com/brendon/acts_as_list/issues/8)
|
|
622
665
|
|
|
623
666
|
**Merged pull requests:**
|
|
624
667
|
|
|
@@ -627,7 +670,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
627
670
|
- Massive test refactorings. [\#24](https://github.com/brendon/acts_as_list/pull/24) ([splattael](https://github.com/splattael))
|
|
628
671
|
- Silent migrations to reduce test noise. [\#22](https://github.com/brendon/acts_as_list/pull/22) ([splattael](https://github.com/splattael))
|
|
629
672
|
- Should decrement lower items after the item has been destroyed to avoid unique key conflicts. [\#18](https://github.com/brendon/acts_as_list/pull/18) ([aepstein](https://github.com/aepstein))
|
|
630
|
-
- Fix spelling and
|
|
673
|
+
- Fix spelling and grammar [\#15](https://github.com/brendon/acts_as_list/pull/15) ([tmiller](https://github.com/tmiller))
|
|
631
674
|
- store\_at\_0 should yank item from the list then decrement items to avoid r [\#14](https://github.com/brendon/acts_as_list/pull/14) ([aepstein](https://github.com/aepstein))
|
|
632
675
|
- Support default\_scope ordering by calling .unscoped [\#13](https://github.com/brendon/acts_as_list/pull/13) ([tanordheim](https://github.com/tanordheim))
|
|
633
676
|
|
data/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# Acts As List
|
|
2
2
|
|
|
3
3
|
## Build Status
|
|
4
|
-
[](https://github.com/brendon/acts_as_list/actions/workflows/continuous_integration.yml)
|
|
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 acquainted 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
|
|
|
@@ -282,7 +290,7 @@ All versions `0.1.5` onwards require Rails 3.0.x and higher.
|
|
|
282
290
|
|
|
283
291
|
We often hear complaints that `position` values are repeated, incorrect etc. For example, #254. To ensure data integrity, you should rely on your database. There are two things you can do:
|
|
284
292
|
|
|
285
|
-
1. Use constraints. If you model `Item` that `belongs_to` an `Order`, and it has a `position` column, then add a unique constraint on `items` with `[:order_id, :position]`. Think of it as a list invariant. What are the properties of your list that don't change no matter how many items you have in it? One such
|
|
293
|
+
1. Use constraints. If you model `Item` that `belongs_to` an `Order`, and it has a `position` column, then add a unique constraint on `items` with `[:order_id, :position]`. Think of it as a list invariant. What are the properties of your list that don't change no matter how many items you have in it? One such property is that each item has a distinct position. Another _could be_ that position is always greater than 0. It is strongly recommended that you rely on your database to enforce these invariants or constraints. Here are the docs for [PostgreSQL](https://www.postgresql.org/docs/9.5/static/ddl-constraints.html) and [MySQL](https://dev.mysql.com/doc/refman/8.0/en/alter-table.html).
|
|
286
294
|
2. Use mutexes or row level locks. At its heart the duplicate problem is that of handling concurrency. Adding a contention resolution mechanism like locks will solve it to some extent. But it is not a solution or replacement for constraints. Locks are also prone to deadlocks.
|
|
287
295
|
|
|
288
296
|
As a library, `acts_as_list` may not always have all the context needed to apply these tools. They are much better suited at the application level.
|
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
|
|
@@ -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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative './with_connection'
|
|
4
|
+
|
|
3
5
|
module ActiveRecord
|
|
4
6
|
module Acts #:nodoc:
|
|
5
7
|
module List #:nodoc:
|
|
@@ -25,6 +27,11 @@ module ActiveRecord
|
|
|
25
27
|
configuration = { column: "position", scope: "1 = 1", top_of_list: 1, add_new_at: :bottom, touch_on_update: true }
|
|
26
28
|
configuration.update(options) if options.is_a?(Hash)
|
|
27
29
|
|
|
30
|
+
# * Expose configuration via class-level method
|
|
31
|
+
define_singleton_method(:acts_as_list_options) do
|
|
32
|
+
configuration.dup.freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
28
35
|
caller_class = self
|
|
29
36
|
|
|
30
37
|
ActiveRecord::Acts::List::PositionColumnMethodDefiner.call(caller_class, configuration[:column], configuration[:touch_on_update])
|
|
@@ -171,7 +178,7 @@ module ActiveRecord
|
|
|
171
178
|
limit ||= acts_as_list_list.count
|
|
172
179
|
acts_as_list_list.
|
|
173
180
|
where("#{quoted_position_column_with_table_name} <= ?", current_position).
|
|
174
|
-
where
|
|
181
|
+
where.not(primary_key_condition).
|
|
175
182
|
reorder(acts_as_list_order_argument(:desc)).
|
|
176
183
|
limit(limit)
|
|
177
184
|
end
|
|
@@ -188,7 +195,7 @@ module ActiveRecord
|
|
|
188
195
|
limit ||= acts_as_list_list.count
|
|
189
196
|
acts_as_list_list.
|
|
190
197
|
where("#{quoted_position_column_with_table_name} >= ?", current_position).
|
|
191
|
-
where
|
|
198
|
+
where.not(primary_key_condition).
|
|
192
199
|
reorder(acts_as_list_order_argument(:asc)).
|
|
193
200
|
limit(limit)
|
|
194
201
|
end
|
|
@@ -229,36 +236,27 @@ module ActiveRecord
|
|
|
229
236
|
acts_as_list_class.default_scoped.unscope(:select, :where).where(scope_condition)
|
|
230
237
|
end
|
|
231
238
|
|
|
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
|
|
239
|
+
def avoid_collision
|
|
240
|
+
case add_new_at
|
|
241
|
+
when :top
|
|
242
|
+
if assume_default_position?
|
|
243
|
+
increment_positions_on_all_items
|
|
244
|
+
self[position_column] = acts_as_list_top
|
|
245
|
+
else
|
|
246
|
+
increment_positions_on_lower_items(self[position_column], id)
|
|
247
|
+
end
|
|
248
|
+
when :bottom
|
|
249
|
+
if assume_default_position?
|
|
250
|
+
self[position_column] = bottom_position_in_list.to_i + 1
|
|
251
|
+
else
|
|
252
|
+
increment_positions_on_lower_items(self[position_column], id)
|
|
253
|
+
end
|
|
253
254
|
else
|
|
254
|
-
increment_positions_on_lower_items(self[position_column], id)
|
|
255
|
+
increment_positions_on_lower_items(self[position_column], id) if position_changed
|
|
255
256
|
end
|
|
256
257
|
|
|
257
|
-
# Make sure we know that we've processed this scope change already
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
# Don't halt the callback chain
|
|
261
|
-
true
|
|
258
|
+
@scope_changed = false # Make sure we know that we've processed this scope change already
|
|
259
|
+
return true # Don't halt the callback chain
|
|
262
260
|
end
|
|
263
261
|
|
|
264
262
|
def assume_default_position?
|
|
@@ -282,7 +280,7 @@ module ActiveRecord
|
|
|
282
280
|
scope = acts_as_list_list
|
|
283
281
|
|
|
284
282
|
if except
|
|
285
|
-
scope = scope.where(
|
|
283
|
+
scope = scope.where.not(primary_key_condition(except.id))
|
|
286
284
|
end
|
|
287
285
|
|
|
288
286
|
scope.in_list.reorder(acts_as_list_order_argument(:desc)).first
|
|
@@ -309,7 +307,7 @@ module ActiveRecord
|
|
|
309
307
|
scope = acts_as_list_list
|
|
310
308
|
|
|
311
309
|
if avoid_id
|
|
312
|
-
scope = scope.where(
|
|
310
|
+
scope = scope.where.not(primary_key_condition(avoid_id))
|
|
313
311
|
end
|
|
314
312
|
|
|
315
313
|
if sequential_updates?
|
|
@@ -350,7 +348,7 @@ module ActiveRecord
|
|
|
350
348
|
scope = acts_as_list_list
|
|
351
349
|
|
|
352
350
|
if avoid_id
|
|
353
|
-
scope = scope.where(
|
|
351
|
+
scope = scope.where.not(primary_key_condition(avoid_id))
|
|
354
352
|
end
|
|
355
353
|
|
|
356
354
|
if old_position < new_position
|
|
@@ -454,7 +452,7 @@ module ActiveRecord
|
|
|
454
452
|
send('decrement_positions_on_lower_items') if lower_item
|
|
455
453
|
cached_changes.each { |attribute, values| send("#{attribute}=", values[1]) }
|
|
456
454
|
|
|
457
|
-
|
|
455
|
+
avoid_collision
|
|
458
456
|
end
|
|
459
457
|
end
|
|
460
458
|
|
|
@@ -468,7 +466,9 @@ module ActiveRecord
|
|
|
468
466
|
|
|
469
467
|
# When using raw column name it must be quoted otherwise it can raise syntax errors with SQL keywords (e.g. order)
|
|
470
468
|
def quoted_position_column
|
|
471
|
-
@_quoted_position_column ||= self.class.connection
|
|
469
|
+
@_quoted_position_column ||= ActiveRecord::Acts::List::WithConnection.new(self.class).call do |connection|
|
|
470
|
+
connection.quote_column_name(position_column)
|
|
471
|
+
end
|
|
472
472
|
end
|
|
473
473
|
|
|
474
474
|
# Used in order clauses
|
|
@@ -489,6 +489,10 @@ module ActiveRecord
|
|
|
489
489
|
version = Gem.loaded_specs['activerecord'].version
|
|
490
490
|
requirement.satisfied_by?(version)
|
|
491
491
|
end
|
|
492
|
+
|
|
493
|
+
def primary_key_condition(id = self.id)
|
|
494
|
+
{ self.class.primary_key => [id] }
|
|
495
|
+
end
|
|
492
496
|
end
|
|
493
497
|
|
|
494
498
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative './with_connection'
|
|
4
|
+
|
|
3
5
|
module ActiveRecord::Acts::List::PositionColumnMethodDefiner #:nodoc:
|
|
4
6
|
def self.call(caller_class, position_column, touch_on_update)
|
|
5
7
|
define_class_methods(caller_class, position_column, touch_on_update)
|
|
@@ -15,7 +17,9 @@ module ActiveRecord::Acts::List::PositionColumnMethodDefiner #:nodoc:
|
|
|
15
17
|
def self.define_class_methods(caller_class, position_column, touch_on_update)
|
|
16
18
|
caller_class.class_eval do
|
|
17
19
|
define_singleton_method :quoted_position_column do
|
|
18
|
-
@_quoted_position_column ||=
|
|
20
|
+
@_quoted_position_column ||= ActiveRecord::Acts::List::WithConnection.new(self).call do |connection|
|
|
21
|
+
connection.quote_column_name(position_column)
|
|
22
|
+
end
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
define_singleton_method :quoted_position_column_with_table_name do
|
|
@@ -72,17 +76,22 @@ module ActiveRecord::Acts::List::PositionColumnMethodDefiner #:nodoc:
|
|
|
72
76
|
cached_quoted_now = quoted_current_time_from_proper_timezone
|
|
73
77
|
|
|
74
78
|
timestamp_attributes_for_update_in_model.map do |attr|
|
|
75
|
-
|
|
79
|
+
ActiveRecord::Acts::List::WithConnection.new(self.class).call do |connection|
|
|
80
|
+
", #{connection.quote_column_name(attr)} = #{cached_quoted_now}"
|
|
81
|
+
end
|
|
76
82
|
end.join
|
|
77
83
|
end
|
|
78
84
|
|
|
79
85
|
private
|
|
80
86
|
|
|
81
|
-
delegate :connection, to: self
|
|
82
|
-
|
|
83
87
|
def quoted_current_time_from_proper_timezone
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
ActiveRecord::Acts::List::WithConnection.new(self.class).call do |connection|
|
|
89
|
+
connection.quote(
|
|
90
|
+
connection.quoted_date(
|
|
91
|
+
current_time_from_proper_timezone
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
end
|
|
86
95
|
end
|
|
87
96
|
end
|
|
88
97
|
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)
|
|
@@ -22,7 +21,16 @@ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
define_method :destroyed_via_scope? do
|
|
25
|
-
|
|
24
|
+
return false unless destroyed_by_association
|
|
25
|
+
|
|
26
|
+
foreign_key = destroyed_by_association.foreign_key
|
|
27
|
+
if foreign_key.is_a?(Array)
|
|
28
|
+
# Composite foreign key - check if scope is one of the keys
|
|
29
|
+
foreign_key.map(&:to_sym).include?(scope)
|
|
30
|
+
else
|
|
31
|
+
# Single foreign key
|
|
32
|
+
scope == foreign_key.to_sym
|
|
33
|
+
end
|
|
26
34
|
end
|
|
27
35
|
elsif scope.is_a?(Array)
|
|
28
36
|
define_method :scope_condition do
|
|
@@ -45,7 +53,16 @@ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
|
|
45
53
|
end
|
|
46
54
|
|
|
47
55
|
define_method :destroyed_via_scope? do
|
|
48
|
-
|
|
56
|
+
return false unless destroyed_by_association
|
|
57
|
+
|
|
58
|
+
foreign_key = destroyed_by_association.foreign_key
|
|
59
|
+
if foreign_key.is_a?(Array)
|
|
60
|
+
# Composite foreign key - check if any keys overlap with scope
|
|
61
|
+
(scope_condition.keys & foreign_key.map(&:to_sym)).any?
|
|
62
|
+
else
|
|
63
|
+
# Single foreign key
|
|
64
|
+
scope_condition.keys.include?(foreign_key.to_sym)
|
|
65
|
+
end
|
|
49
66
|
end
|
|
50
67
|
else
|
|
51
68
|
define_method :scope_condition do
|
|
@@ -69,9 +86,12 @@ module ActiveRecord::Acts::List::ScopeMethodDefiner #:nodoc:
|
|
|
69
86
|
return name if name.to_s =~ /_id$/
|
|
70
87
|
|
|
71
88
|
if caller_class.reflections.key?(name.to_s)
|
|
72
|
-
caller_class.reflections[name.to_s].foreign_key
|
|
89
|
+
foreign_key = caller_class.reflections[name.to_s].foreign_key
|
|
90
|
+
# Handle composite foreign keys (Arrays) by returning as-is
|
|
91
|
+
# Single foreign keys should be converted to symbols
|
|
92
|
+
foreign_key.is_a?(Array) ? foreign_key.map(&:to_sym) : foreign_key.to_sym
|
|
73
93
|
else
|
|
74
|
-
foreign_key(name).to_sym
|
|
94
|
+
ActiveSupport::Inflector.foreign_key(name).to_sym
|
|
75
95
|
end
|
|
76
96
|
end
|
|
77
97
|
end
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative './with_connection'
|
|
4
|
+
|
|
3
5
|
module ActiveRecord::Acts::List::SequentialUpdatesMethodDefiner #:nodoc:
|
|
4
6
|
def self.call(caller_class, column, sequential_updates_option)
|
|
5
7
|
caller_class.class_eval do
|
|
6
8
|
define_method :sequential_updates? do
|
|
7
|
-
if
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
else
|
|
21
|
-
@sequential_updates
|
|
9
|
+
return @sequential_updates if defined?(@sequential_updates)
|
|
10
|
+
|
|
11
|
+
return @sequential_updates = sequential_updates_option unless sequential_updates_option.nil?
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Acts::List::WithConnection.new(caller_class).call do |connection|
|
|
14
|
+
table_exists =
|
|
15
|
+
if active_record_version_is?('>= 5')
|
|
16
|
+
connection.data_source_exists?(caller_class.table_name)
|
|
17
|
+
else
|
|
18
|
+
connection.table_exists?(caller_class.table_name)
|
|
19
|
+
end
|
|
20
|
+
index_exists = connection.index_exists?(caller_class.table_name, column, unique: true)
|
|
21
|
+
@sequential_updates = table_exists && index_exists
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Acts
|
|
5
|
+
module List
|
|
6
|
+
class WithConnection
|
|
7
|
+
def initialize(recipient)
|
|
8
|
+
@recipient = recipient
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :recipient
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
if recipient.respond_to?(:with_connection)
|
|
15
|
+
recipient.with_connection do |connection|
|
|
16
|
+
yield connection
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
yield recipient.connection
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|