closure_tree 9.3.0 → 9.6.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 +4 -4
- data/CHANGELOG.md +31 -0
- data/README.md +2 -0
- data/closure_tree.gemspec +2 -2
- data/lib/closure_tree/arel_helpers.rb +8 -0
- data/lib/closure_tree/association_setup.rb +2 -2
- data/lib/closure_tree/has_closure_tree.rb +1 -0
- data/lib/closure_tree/hierarchy_maintenance.rb +31 -5
- data/lib/closure_tree/numeric_deterministic_ordering.rb +8 -13
- data/lib/closure_tree/support.rb +67 -6
- data/lib/closure_tree/support_attributes.rb +4 -0
- data/lib/closure_tree/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8e3f99ea82d4fd195ea034a9f6fcfdd0ce1ec69e74fa2b62241b68eb74070676
|
|
4
|
+
data.tar.gz: b59d11e3fe4e6236c457776b5b5e7cc7133c77759789fbe816b8185db44855e8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a1d3b69d8397b4f5d8b8ad7e81df74e9577c77dcbd10851b5fdc00a17f2b0b5caee0446fe71b3aaec28d0b79c7a4b8d97a89edcd3ac4599bbbeb4b0d6ddeb21
|
|
7
|
+
data.tar.gz: 88d92d332638b6c38e6575ae14ebfd8ec060c2ef0137f7335b72bda4f84537d7db43032623ac5138d31424753cbf83208e5f7f4de0ce1953fcd03a0d8314fbeb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [9.6.0](https://github.com/ClosureTree/closure_tree/compare/closure_tree/v9.5.0...closure_tree/v9.6.0) (2026-02-16)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* raise error when advisory lock cannot be acquired within configured timeout ([#480](https://github.com/ClosureTree/closure_tree/issues/480)) ([c030385](https://github.com/ClosureTree/closure_tree/commit/c030385297a9d3042d43354676a794b1c5757d2a))
|
|
9
|
+
* sibling reordering when node changes parent or scope ([#484](https://github.com/ClosureTree/closure_tree/issues/484)) ([254ba36](https://github.com/ClosureTree/closure_tree/commit/254ba360638bf21717d214b7fd328db8ffa167e0))
|
|
10
|
+
|
|
11
|
+
## [9.5.0](https://github.com/ClosureTree/closure_tree/compare/closure_tree-v9.3.0...closure_tree/v9.5.0) (2026-01-21)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* Add `dependent: :adopt` option for `has_closure_tree` ([#471](https://github.com/ClosureTree/closure_tree/issues/471)) ([d47d7c9](https://github.com/ClosureTree/closure_tree/commit/d47d7c93b59327112a68a7f8454a278877b6ba77))
|
|
17
|
+
* add PostgreSQL schema-qualified table name support ([#462](https://github.com/ClosureTree/closure_tree/issues/462)) ([5f9006c](https://github.com/ClosureTree/closure_tree/commit/5f9006cece95a76f665cb50c2615317e3fa48586))
|
|
18
|
+
* Add runtime advisory lock name customization and multi-database documentation ([#454](https://github.com/ClosureTree/closure_tree/issues/454)) ([d6ffd73](https://github.com/ClosureTree/closure_tree/commit/d6ffd7381e25a28f7a4742bfa2d9c893f0115395))
|
|
19
|
+
* migrate from ActiveSupport::Autoload to Zeitwerk ([#457](https://github.com/ClosureTree/closure_tree/issues/457)) ([d18e80c](https://github.com/ClosureTree/closure_tree/commit/d18e80cdbd4f3510377363bc7b5166f0cc1b0a6f))
|
|
20
|
+
* rewrite with clean api ([#451](https://github.com/ClosureTree/closure_tree/issues/451)) ([f56f2e1](https://github.com/ClosureTree/closure_tree/commit/f56f2e1a3490bb8a099cea8f80b676945fce1c2e))
|
|
21
|
+
* use with_advisory_lock that support rails 8.2 ([#479](https://github.com/ClosureTree/closure_tree/issues/479)) ([bca6231](https://github.com/ClosureTree/closure_tree/commit/bca623168d3255f57e186a3bdcd39e00d38e2a7c))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* add implicit_order_column for Rails 8.1+ compatibility ([#464](https://github.com/ClosureTree/closure_tree/issues/464)) ([f384303](https://github.com/ClosureTree/closure_tree/commit/f38430334a79ea15d236f9212118dcb5e4530746))
|
|
27
|
+
* configure release-please to recognize v8.0.0 release ([#455](https://github.com/ClosureTree/closure_tree/issues/455)) ([fc34f21](https://github.com/ClosureTree/closure_tree/commit/fc34f2148570afd83608b07a3f5282e5fd475783))
|
|
28
|
+
* eager loading of hierarchies by defining the primary key ([#465](https://github.com/ClosureTree/closure_tree/issues/465)) ([8c7e490](https://github.com/ClosureTree/closure_tree/commit/8c7e490ff89239e44aee71577288bff0e177a8d6))
|
|
29
|
+
* hierarchy class inheritance to avoid STI validations ([#392](https://github.com/ClosureTree/closure_tree/issues/392)) ([#472](https://github.com/ClosureTree/closure_tree/issues/472)) ([73b07a6](https://github.com/ClosureTree/closure_tree/commit/73b07a6cfeec226af16f51ce7c9c6c1d6d921b3d))
|
|
30
|
+
* improve api usage ([#475](https://github.com/ClosureTree/closure_tree/issues/475)) ([cde4d29](https://github.com/ClosureTree/closure_tree/commit/cde4d292236b4267ac4c0be2e2b21092a8b6298c))
|
|
31
|
+
* nil scope values being excluded from scope filtering ([#476](https://github.com/ClosureTree/closure_tree/issues/476)) ([f45880e](https://github.com/ClosureTree/closure_tree/commit/f45880e9765bfa3c1f171b7386bf5a5584208ad7))
|
|
32
|
+
* restore proper deprecation for database_less configuration ([#459](https://github.com/ClosureTree/closure_tree/issues/459)) ([de8b402](https://github.com/ClosureTree/closure_tree/commit/de8b40233d3de5243afce7ec9de9ad26c2eee181))
|
|
33
|
+
|
|
3
34
|
## [9.3.0](https://github.com/ClosureTree/closure_tree/compare/closure_tree/v9.2.0...closure_tree/v9.3.0) (2025-11-19)
|
|
4
35
|
|
|
5
36
|
|
data/README.md
CHANGED
|
@@ -314,6 +314,7 @@ When you include ```has_closure_tree``` in your model, you can provide a hash to
|
|
|
314
314
|
* ```:hierarchy_table_name``` to override the hierarchy table name. This defaults to the singular name of the model + "_hierarchies", like ```tag_hierarchies```.
|
|
315
315
|
* ```:dependent``` determines what happens when a node is destroyed. Defaults to ```nullify```.
|
|
316
316
|
* ```:nullify``` will simply set the parent column to null. Each child node will be considered a "root" node. This is the default.
|
|
317
|
+
* ```:adopt``` will move children to their grandparent (parent's parent). If there is no grandparent, children become root nodes. This is useful for maintaining tree structure when removing intermediate nodes.
|
|
317
318
|
* ```:delete_all``` will delete all descendant nodes (which circumvents the destroy hooks)
|
|
318
319
|
* ```:destroy``` will destroy all descendant nodes (which runs the destroy hooks on each child node)
|
|
319
320
|
* ```nil``` does nothing with descendant nodes
|
|
@@ -321,6 +322,7 @@ When you include ```has_closure_tree``` in your model, you can provide a hash to
|
|
|
321
322
|
* ```:order``` used to set up [deterministic ordering](#deterministic-ordering)
|
|
322
323
|
* ```:scope``` restricts root nodes and sibling ordering to specific columns. Can be a single symbol or an array of symbols. Example: ```scope: :user_id``` or ```scope: [:user_id, :group_id]```. This ensures that root nodes and siblings are scoped correctly when reordering. See [Ordering Roots](#ordering-roots) for more details.
|
|
323
324
|
* ```:touch``` delegates to the `belongs_to` annotation for the parent, so `touch`ing cascades to all children (the performance of this for deep trees isn't currently optimal).
|
|
325
|
+
* ```:advisory_lock_timeout_seconds``` When set, the advisory lock will raise ```WithAdvisoryLock::FailedToAcquireLock``` if the lock cannot be acquired within the timeout period. This helps callers handle timeout scenarios (e.g. retry or fail fast). If the option is not specified, the lock is waited for indefinitely until it is acquired. See [Lock wait timeouts](https://github.com/ClosureTree/with_advisory_lock?tab=readme-ov-file#lock-wait-timeouts) in the with_advisory_lock gem for details.
|
|
324
326
|
|
|
325
327
|
## Accessing Data
|
|
326
328
|
|
data/closure_tree.gemspec
CHANGED
|
@@ -26,11 +26,11 @@ Gem::Specification.new do |gem|
|
|
|
26
26
|
gem.required_ruby_version = '>= 3.3.0'
|
|
27
27
|
|
|
28
28
|
gem.add_dependency 'activerecord', '>= 7.2.0'
|
|
29
|
-
gem.add_dependency 'with_advisory_lock', '>= 7.
|
|
29
|
+
gem.add_dependency 'with_advisory_lock', '>= 7.5.0'
|
|
30
30
|
gem.add_dependency 'zeitwerk', '~> 2.7'
|
|
31
31
|
|
|
32
32
|
gem.add_development_dependency 'database_cleaner'
|
|
33
|
-
gem.add_development_dependency 'minitest'
|
|
33
|
+
gem.add_development_dependency 'minitest', '~> 5.0'
|
|
34
34
|
gem.add_development_dependency 'minitest-reporters'
|
|
35
35
|
gem.add_development_dependency 'parallel'
|
|
36
36
|
gem.add_development_dependency 'simplecov'
|
|
@@ -79,5 +79,13 @@ module ClosureTree
|
|
|
79
79
|
|
|
80
80
|
delete_manager
|
|
81
81
|
end
|
|
82
|
+
|
|
83
|
+
# Convert an Arel AST to SQL using the correct connection's visitor
|
|
84
|
+
# This ensures proper quoting for the specific database adapter (MySQL uses backticks, PostgreSQL uses double quotes)
|
|
85
|
+
def to_sql_with_connection(arel_manager)
|
|
86
|
+
collector = Arel::Collectors::SQLString.new
|
|
87
|
+
visitor = connection.visitor
|
|
88
|
+
visitor.accept(arel_manager.ast, collector).value
|
|
89
|
+
end
|
|
82
90
|
end
|
|
83
91
|
end
|
|
@@ -20,7 +20,7 @@ module ClosureTree
|
|
|
20
20
|
|
|
21
21
|
has_many :children, *_ct.has_many_order_with_option, class_name: _ct.model_class.to_s,
|
|
22
22
|
foreign_key: _ct.parent_column_name,
|
|
23
|
-
dependent: _ct.options[:dependent],
|
|
23
|
+
dependent: _ct.options[:dependent] == :adopt ? :nullify : _ct.options[:dependent],
|
|
24
24
|
inverse_of: :parent do
|
|
25
25
|
# We have to redefine hash_tree because the activerecord relation is already scoped to parent_id.
|
|
26
26
|
def hash_tree(options = {})
|
|
@@ -47,4 +47,4 @@ module ClosureTree
|
|
|
47
47
|
source: :descendant
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
|
-
end
|
|
50
|
+
end
|
|
@@ -18,7 +18,10 @@ module ClosureTree
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def _ct_skip_sort_order_maintenance!
|
|
21
|
-
|
|
21
|
+
ActiveSupport::Deprecation.new.warn(
|
|
22
|
+
'_ct_skip_sort_order_maintenance! is deprecated and will be removed in the next major version. ' \
|
|
23
|
+
'Sort order maintenance is now handled automatically.'
|
|
24
|
+
)
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def _ct_validate
|
|
@@ -38,7 +41,17 @@ module ClosureTree
|
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
def _ct_after_save
|
|
41
|
-
|
|
44
|
+
scope_changed = _ct.order_is_numeric? && _ct.scope_changed?(self)
|
|
45
|
+
|
|
46
|
+
if saved_changes[_ct.parent_column_name] || @was_new_record
|
|
47
|
+
rebuild!
|
|
48
|
+
elsif scope_changed
|
|
49
|
+
# Scope changed without parent change - reorder old scope's siblings
|
|
50
|
+
_ct_reorder_prior_siblings_if_parent_changed
|
|
51
|
+
_ct_reorder_siblings
|
|
52
|
+
elsif _ct.order_option? && saved_changes[_ct.order_column_sym]
|
|
53
|
+
_ct_reorder_siblings(saved_changes[_ct.order_column_sym].min)
|
|
54
|
+
end
|
|
42
55
|
if saved_changes[_ct.parent_column_name] && !@was_new_record
|
|
43
56
|
# Resetting the ancestral collections addresses
|
|
44
57
|
# https://github.com/mceachen/closure_tree/issues/68
|
|
@@ -46,18 +59,31 @@ module ClosureTree
|
|
|
46
59
|
self_and_ancestors.reload
|
|
47
60
|
end
|
|
48
61
|
@was_new_record = false # we aren't new anymore.
|
|
49
|
-
@_ct_skip_sort_order_maintenance = false # only skip once.
|
|
50
62
|
true # don't cancel anything.
|
|
51
63
|
end
|
|
52
64
|
|
|
53
65
|
def _ct_before_destroy
|
|
54
66
|
_ct.with_advisory_lock do
|
|
67
|
+
_ct_adopt_children_to_grandparent if _ct.options[:dependent] == :adopt
|
|
55
68
|
delete_hierarchy_references
|
|
56
69
|
self.class.find(id).children.find_each(&:rebuild!) if _ct.options[:dependent] == :nullify
|
|
57
70
|
end
|
|
58
71
|
true # don't prevent destruction
|
|
59
72
|
end
|
|
60
73
|
|
|
74
|
+
private def _ct_adopt_children_to_grandparent
|
|
75
|
+
grandparent_id = read_attribute(_ct.parent_column_name)
|
|
76
|
+
children_ids = self.class.where(_ct.parent_column_name => id).pluck(:id)
|
|
77
|
+
|
|
78
|
+
return if children_ids.empty?
|
|
79
|
+
|
|
80
|
+
# Update all children's parent_id in a single query
|
|
81
|
+
self.class.where(id: children_ids).update_all(_ct.parent_column_name => grandparent_id)
|
|
82
|
+
|
|
83
|
+
# Rebuild hierarchy for each child
|
|
84
|
+
self.class.where(id: children_ids).find_each(&:rebuild!)
|
|
85
|
+
end
|
|
86
|
+
|
|
61
87
|
def rebuild!(called_by_rebuild = false)
|
|
62
88
|
_ct.with_advisory_lock do
|
|
63
89
|
delete_hierarchy_references unless (defined? @was_new_record) && @was_new_record
|
|
@@ -72,7 +98,7 @@ module ClosureTree
|
|
|
72
98
|
SQL
|
|
73
99
|
end
|
|
74
100
|
|
|
75
|
-
if _ct.order_is_numeric?
|
|
101
|
+
if _ct.order_is_numeric?
|
|
76
102
|
_ct_reorder_prior_siblings_if_parent_changed
|
|
77
103
|
# Prevent double-reordering of siblings:
|
|
78
104
|
_ct_reorder_siblings unless called_by_rebuild
|
|
@@ -93,7 +119,7 @@ module ClosureTree
|
|
|
93
119
|
|
|
94
120
|
hierarchy_table = hierarchy_class.arel_table
|
|
95
121
|
delete_query = _ct.build_hierarchy_delete_query(hierarchy_table, id)
|
|
96
|
-
_ct.connection.execute(delete_query
|
|
122
|
+
_ct.connection.execute(_ct.to_sql_with_connection(delete_query))
|
|
97
123
|
end
|
|
98
124
|
end
|
|
99
125
|
|
|
@@ -12,11 +12,16 @@ module ClosureTree
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def _ct_reorder_prior_siblings_if_parent_changed
|
|
15
|
-
return
|
|
15
|
+
return if @was_new_record
|
|
16
|
+
|
|
17
|
+
parent_changed = saved_change_to_attribute?(_ct.parent_column_name)
|
|
18
|
+
scope_changed = _ct.scope_changed?(self)
|
|
19
|
+
|
|
20
|
+
return unless parent_changed || scope_changed
|
|
16
21
|
|
|
17
22
|
was_parent_id = attribute_before_last_save(_ct.parent_column_name)
|
|
18
|
-
|
|
19
|
-
_ct.reorder_with_parent_id(was_parent_id, nil,
|
|
23
|
+
previous_scope_conditions = _ct.previous_scope_values_from_instance(self)
|
|
24
|
+
_ct.reorder_with_parent_id(was_parent_id, nil, previous_scope_conditions)
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
def _ct_reorder_siblings(minimum_sort_order_value = nil)
|
|
@@ -132,9 +137,7 @@ module ClosureTree
|
|
|
132
137
|
def prepend_child(child_node)
|
|
133
138
|
child_node.order_value = -1
|
|
134
139
|
child_node.parent = self
|
|
135
|
-
child_node._ct_skip_sort_order_maintenance!
|
|
136
140
|
if child_node.save
|
|
137
|
-
_ct_reorder_children
|
|
138
141
|
child_node.reload
|
|
139
142
|
else
|
|
140
143
|
child_node
|
|
@@ -161,19 +164,11 @@ module ClosureTree
|
|
|
161
164
|
|
|
162
165
|
_ct.with_advisory_lock do
|
|
163
166
|
prior_sibling_parent = sibling.parent
|
|
164
|
-
reorder_from_value = if prior_sibling_parent == parent
|
|
165
|
-
[order_value, sibling.order_value].compact.min
|
|
166
|
-
else
|
|
167
|
-
order_value
|
|
168
|
-
end
|
|
169
167
|
|
|
170
168
|
sibling.order_value = order_value
|
|
171
169
|
sibling.parent = parent
|
|
172
|
-
sibling._ct_skip_sort_order_maintenance!
|
|
173
170
|
sibling.save # may be a no-op
|
|
174
171
|
|
|
175
|
-
_ct_reorder_siblings(reorder_from_value)
|
|
176
|
-
|
|
177
172
|
# The sort order should be correct now except for self and sibling, which may need to flip:
|
|
178
173
|
sibling_is_after = reload.order_value < sibling.reload.order_value
|
|
179
174
|
if add_after != sibling_is_after
|
data/lib/closure_tree/support.rb
CHANGED
|
@@ -16,9 +16,10 @@ module ClosureTree
|
|
|
16
16
|
|
|
17
17
|
@options = {
|
|
18
18
|
parent_column_name: 'parent_id',
|
|
19
|
-
dependent: :nullify, # or :destroy or :
|
|
19
|
+
dependent: :nullify, # or :destroy, :delete_all, or :adopt -- see the README
|
|
20
20
|
name_column: 'name',
|
|
21
21
|
with_advisory_lock: true, # This will be overridden by adapter support
|
|
22
|
+
advisory_lock_timeout_seconds: nil,
|
|
22
23
|
numeric_order: false
|
|
23
24
|
}.merge(options)
|
|
24
25
|
raise ArgumentError, "name_column can't be 'path'" if options[:name_column] == 'path'
|
|
@@ -30,14 +31,34 @@ module ClosureTree
|
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
if !@options[:with_advisory_lock] && @options[:advisory_lock_timeout_seconds].present?
|
|
35
|
+
raise ArgumentError, "advisory_lock_timeout_seconds can't be specified when advisory_lock is disabled"
|
|
36
|
+
end
|
|
37
|
+
|
|
33
38
|
return unless order_is_numeric?
|
|
34
39
|
|
|
35
40
|
extend NumericOrderSupport.adapter_for_connection(connection)
|
|
36
41
|
end
|
|
37
42
|
|
|
43
|
+
# Find the abstract base class for database connection
|
|
44
|
+
# This ensures hierarchy class uses the same database but doesn't inherit
|
|
45
|
+
# validations/callbacks from STI parent classes (issue #392)
|
|
46
|
+
def abstract_base_class
|
|
47
|
+
klass = model_class
|
|
48
|
+
while klass.superclass != ActiveRecord::Base
|
|
49
|
+
parent = klass.superclass
|
|
50
|
+
# Stop at abstract class (ApplicationRecord, SecondaryRecord, etc.)
|
|
51
|
+
return parent if parent.abstract_class?
|
|
52
|
+
# Stop at connection boundary (handles non-abstract parents with custom connections)
|
|
53
|
+
return parent if parent.connection_specification_name != parent.superclass.connection_specification_name
|
|
54
|
+
klass = parent
|
|
55
|
+
end
|
|
56
|
+
ActiveRecord::Base
|
|
57
|
+
end
|
|
58
|
+
|
|
38
59
|
def hierarchy_class_for_model
|
|
39
60
|
parent_class = model_class.module_parent
|
|
40
|
-
hierarchy_class = parent_class.const_set(short_hierarchy_class_name, Class.new(
|
|
61
|
+
hierarchy_class = parent_class.const_set(short_hierarchy_class_name, Class.new(abstract_base_class))
|
|
41
62
|
model_class_name = model_class.to_s
|
|
42
63
|
hierarchy_class.class_eval do
|
|
43
64
|
# Rails 8.1+ requires an implicit_order_column for models without a primary key
|
|
@@ -137,8 +158,9 @@ module ClosureTree
|
|
|
137
158
|
end
|
|
138
159
|
|
|
139
160
|
def with_advisory_lock(&block)
|
|
140
|
-
|
|
141
|
-
|
|
161
|
+
lock_method = options[:advisory_lock_timeout_seconds].present? ? :with_advisory_lock! : :with_advisory_lock
|
|
162
|
+
if options[:with_advisory_lock] && connection.supports_advisory_locks? && model_class.respond_to?(lock_method)
|
|
163
|
+
model_class.public_send(lock_method, advisory_lock_name, advisory_lock_options) do
|
|
142
164
|
transaction(&block)
|
|
143
165
|
end
|
|
144
166
|
else
|
|
@@ -222,12 +244,12 @@ module ClosureTree
|
|
|
222
244
|
case scope_option
|
|
223
245
|
when Symbol
|
|
224
246
|
value = instance.read_attribute(scope_option)
|
|
225
|
-
scope_hash[scope_option] = value
|
|
247
|
+
scope_hash[scope_option] = value
|
|
226
248
|
when Array
|
|
227
249
|
scope_option.each do |item|
|
|
228
250
|
if item.is_a?(Symbol)
|
|
229
251
|
value = instance.read_attribute(item)
|
|
230
|
-
scope_hash[item] = value
|
|
252
|
+
scope_hash[item] = value
|
|
231
253
|
end
|
|
232
254
|
end
|
|
233
255
|
end
|
|
@@ -235,6 +257,45 @@ module ClosureTree
|
|
|
235
257
|
scope_hash
|
|
236
258
|
end
|
|
237
259
|
|
|
260
|
+
def previous_scope_values_from_instance(instance)
|
|
261
|
+
return {} unless options[:scope] && instance
|
|
262
|
+
|
|
263
|
+
scope_option = options[:scope]
|
|
264
|
+
scope_hash = {}
|
|
265
|
+
|
|
266
|
+
case scope_option
|
|
267
|
+
when Symbol
|
|
268
|
+
value = instance.attribute_before_last_save(scope_option)
|
|
269
|
+
scope_hash[scope_option] = value
|
|
270
|
+
when Array
|
|
271
|
+
scope_option.each do |item|
|
|
272
|
+
if item.is_a?(Symbol)
|
|
273
|
+
value = instance.attribute_before_last_save(item)
|
|
274
|
+
scope_hash[item] = value
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
scope_hash
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def scope_changed?(instance)
|
|
283
|
+
return false unless options[:scope] && instance
|
|
284
|
+
|
|
285
|
+
scope_option = options[:scope]
|
|
286
|
+
|
|
287
|
+
case scope_option
|
|
288
|
+
when Symbol
|
|
289
|
+
instance.saved_change_to_attribute?(scope_option)
|
|
290
|
+
when Array
|
|
291
|
+
scope_option.any? do |item|
|
|
292
|
+
item.is_a?(Symbol) && instance.saved_change_to_attribute?(item)
|
|
293
|
+
end
|
|
294
|
+
else
|
|
295
|
+
false
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
238
299
|
def apply_scope_conditions(scope, instance = nil)
|
|
239
300
|
return scope unless options[:scope] && instance
|
|
240
301
|
|
data/lib/closure_tree/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: closure_tree
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.
|
|
4
|
+
version: 9.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthew McEachen
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 7.
|
|
33
|
+
version: 7.5.0
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 7.
|
|
40
|
+
version: 7.5.0
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: zeitwerk
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -70,16 +70,16 @@ dependencies:
|
|
|
70
70
|
name: minitest
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
|
-
- - "
|
|
73
|
+
- - "~>"
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
75
|
+
version: '5.0'
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
|
-
- - "
|
|
80
|
+
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0'
|
|
82
|
+
version: '5.0'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: minitest-reporters
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -178,7 +178,7 @@ licenses:
|
|
|
178
178
|
metadata:
|
|
179
179
|
bug_tracker_uri: https://github.com/ClosureTree/closure_tree/issues
|
|
180
180
|
changelog_uri: https://github.com/ClosureTree/closure_tree/blob/master/CHANGELOG.md
|
|
181
|
-
documentation_uri: https://www.rubydoc.info/gems/closure_tree/9.
|
|
181
|
+
documentation_uri: https://www.rubydoc.info/gems/closure_tree/9.6.0
|
|
182
182
|
homepage_uri: https://closuretree.github.io/closure_tree/
|
|
183
183
|
source_code_uri: https://github.com/ClosureTree/closure_tree
|
|
184
184
|
rubygems_mfa_required: 'true'
|