closure_tree 9.1.0 → 9.1.1
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 +7 -0
- data/README.md +15 -24
- data/lib/closure_tree/association_setup.rb +50 -0
- data/lib/closure_tree/configuration.rb +22 -0
- data/lib/closure_tree/has_closure_tree.rb +7 -2
- data/lib/closure_tree/model.rb +0 -39
- data/lib/closure_tree/version.rb +1 -1
- data/lib/closure_tree.rb +10 -5
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a3ac565445f8b4e6054dd67fe023b025db79b17a2d16af5ba8804f190142e37
|
4
|
+
data.tar.gz: d7e7af95d8ef2b2f01c17248e31aaf3b1c7c51109670fc88fefb9343934ee8ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ecedfd06bac1b6d234c05624730b74c93b34d8ba1cb81b09653f7af3bea42d17f0154d471a7f8c0bbc842981a2a1e1260b061a51fbec4cba49f9963f7aced9c
|
7
|
+
data.tar.gz: 8a329d9a4483a36411515789d2d596905a27501dada6ef77f25cb0b34c3154eb3c5aad0aef28f9e4978987d33587d352a4027dd2cc0d7acf422970729e195151
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [9.1.1](https://github.com/ClosureTree/closure_tree/compare/closure_tree/v9.1.0...closure_tree/v9.1.1) (2025-07-24)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* 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))
|
9
|
+
|
3
10
|
## [9.1.0](https://github.com/ClosureTree/closure_tree/compare/closure_tree/v9.0.0...closure_tree/v9.1.0) (2025-07-23)
|
4
11
|
|
5
12
|
|
data/README.md
CHANGED
@@ -73,6 +73,8 @@ Note that closure_tree only supports ActiveRecord 7.2 and later, and has test co
|
|
73
73
|
|
74
74
|
Make sure you check out the [large number of options](#available-options) that `has_closure_tree` accepts.
|
75
75
|
|
76
|
+
**Note:** The `acts_as_tree` alias is only created if your model doesn't already have a method with that name. This prevents conflicts with other gems like the original `acts_as_tree` gem.
|
77
|
+
|
76
78
|
**IMPORTANT: Make sure you add `has_closure_tree` _after_ `attr_accessible` and
|
77
79
|
`self.table_name =` lines in your model.**
|
78
80
|
|
@@ -156,10 +158,10 @@ Then:
|
|
156
158
|
|
157
159
|
```ruby
|
158
160
|
grandparent.self_and_descendants.collect(&:name)
|
159
|
-
|
161
|
+
#=> ["Grandparent", "Parent", "First Child", "Second Child", "Third Child", "Fourth Child"]
|
160
162
|
|
161
163
|
child1.ancestry_path
|
162
|
-
|
164
|
+
#=> ["Grandparent", "Parent", "First Child"]
|
163
165
|
```
|
164
166
|
|
165
167
|
### find_or_create_by_path
|
@@ -204,19 +206,16 @@ d = Tag.find_or_create_by_path %w[a b c d]
|
|
204
206
|
h = Tag.find_or_create_by_path %w[e f g h]
|
205
207
|
e = h.root
|
206
208
|
d.add_child(e) # "d.children << e" would work too, of course
|
207
|
-
h.ancestry_path
|
208
|
-
=> ["a", "b", "c", "d", "e", "f", "g", "h"]
|
209
|
+
h.ancestry_path #=> ["a", "b", "c", "d", "e", "f", "g", "h"]
|
209
210
|
```
|
210
211
|
|
211
212
|
When it is more convenient to simply change the `parent_id` of a node directly (for example, when dealing with a form `<select>`), closure_tree will handle the necessary changes automatically when the record is saved:
|
212
213
|
|
213
214
|
```ruby
|
214
215
|
j = Tag.find 102
|
215
|
-
j.self_and_ancestor_ids
|
216
|
-
=> [102, 87, 77]
|
216
|
+
j.self_and_ancestor_ids #=> [102, 87, 77]
|
217
217
|
j.update parent_id: 96
|
218
|
-
j.self_and_ancestor_ids
|
219
|
-
=> [102, 96, 95, 78]
|
218
|
+
j.self_and_ancestor_ids #=> [102, 96, 95, 78]
|
220
219
|
```
|
221
220
|
|
222
221
|
### Nested hashes
|
@@ -233,17 +232,13 @@ c1 = d1.parent
|
|
233
232
|
d2 = b.find_or_create_by_path %w(c2 d2)
|
234
233
|
c2 = d2.parent
|
235
234
|
|
236
|
-
Tag.hash_tree
|
237
|
-
=> {a => {b => {c1 => {d1 => {}}, c2 => {d2 => {}}}, b2 => {}}}
|
235
|
+
Tag.hash_tree #=> {a => {b => {c1 => {d1 => {}}, c2 => {d2 => {}}}, b2 => {}}}
|
238
236
|
|
239
|
-
Tag.hash_tree(:limit_depth => 2)
|
240
|
-
=> {a => {b => {}, b2 => {}}}
|
237
|
+
Tag.hash_tree(:limit_depth => 2) #=> {a => {b => {}, b2 => {}}}
|
241
238
|
|
242
|
-
b.hash_tree
|
243
|
-
=> {b => {c1 => {d1 => {}}, c2 => {d2 => {}}}}
|
239
|
+
b.hash_tree #=> {b => {c1 => {d1 => {}}, c2 => {d2 => {}}}}
|
244
240
|
|
245
|
-
b.hash_tree(:limit_depth => 2)
|
246
|
-
=> {b => {c1 => {}, c2 => {}}}
|
241
|
+
b.hash_tree(:limit_depth => 2) #=> {b => {c1 => {}, c2 => {}}}
|
247
242
|
```
|
248
243
|
|
249
244
|
**If your tree is large (or might become so), use :limit_depth.**
|
@@ -477,20 +472,16 @@ c = OrderedTag.create(name: 'c')
|
|
477
472
|
# We have to call 'root.reload.children' because root won't be in sync with the database otherwise:
|
478
473
|
|
479
474
|
a.append_sibling(b)
|
480
|
-
root.reload.children.pluck(:name)
|
481
|
-
=> ["a", "b"]
|
475
|
+
root.reload.children.pluck(:name) #=> ["a", "b"]
|
482
476
|
|
483
477
|
a.prepend_sibling(b)
|
484
|
-
root.reload.children.pluck(:name)
|
485
|
-
=> ["b", "a"]
|
478
|
+
root.reload.children.pluck(:name) #=> ["b", "a"]
|
486
479
|
|
487
480
|
a.append_sibling(c)
|
488
|
-
root.reload.children.pluck(:name)
|
489
|
-
=> ["b", "a", "c"]
|
481
|
+
root.reload.children.pluck(:name) #=> ["b", "a", "c"]
|
490
482
|
|
491
483
|
b.append_sibling(c)
|
492
|
-
root.reload.children.pluck(:name)
|
493
|
-
=> ["b", "c", "a"]
|
484
|
+
root.reload.children.pluck(:name) #=> ["b", "c", "a"]
|
494
485
|
```
|
495
486
|
|
496
487
|
### Ordering Roots
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module ClosureTree
|
6
|
+
# This concern sets up the ActiveRecord associations after all other modules are included.
|
7
|
+
# It must be included last to ensure that HierarchyMaintenance callbacks are already set up.
|
8
|
+
module AssociationSetup
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
belongs_to :parent, nil,
|
13
|
+
class_name: _ct.model_class.to_s,
|
14
|
+
foreign_key: _ct.parent_column_name,
|
15
|
+
inverse_of: :children,
|
16
|
+
touch: _ct.options[:touch],
|
17
|
+
optional: true
|
18
|
+
|
19
|
+
order_by_generations = -> { Arel.sql("#{_ct.quoted_hierarchy_table_name}.generations ASC") }
|
20
|
+
|
21
|
+
has_many :children, *_ct.has_many_order_with_option, class_name: _ct.model_class.to_s,
|
22
|
+
foreign_key: _ct.parent_column_name,
|
23
|
+
dependent: _ct.options[:dependent],
|
24
|
+
inverse_of: :parent do
|
25
|
+
# We have to redefine hash_tree because the activerecord relation is already scoped to parent_id.
|
26
|
+
def hash_tree(options = {})
|
27
|
+
# we want limit_depth + 1 because we don't do self_and_descendants.
|
28
|
+
limit_depth = options[:limit_depth]
|
29
|
+
_ct.hash_tree(@association.owner.descendants, limit_depth ? limit_depth + 1 : nil)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
has_many :ancestor_hierarchies, *_ct.has_many_order_without_option(order_by_generations),
|
34
|
+
class_name: _ct.hierarchy_class_name,
|
35
|
+
foreign_key: 'descendant_id'
|
36
|
+
|
37
|
+
has_many :self_and_ancestors, *_ct.has_many_order_without_option(order_by_generations),
|
38
|
+
through: :ancestor_hierarchies,
|
39
|
+
source: :ancestor
|
40
|
+
|
41
|
+
has_many :descendant_hierarchies, *_ct.has_many_order_without_option(order_by_generations),
|
42
|
+
class_name: _ct.hierarchy_class_name,
|
43
|
+
foreign_key: 'ancestor_id'
|
44
|
+
|
45
|
+
has_many :self_and_descendants, *_ct.has_many_order_with_option(order_by_generations),
|
46
|
+
through: :descendant_hierarchies,
|
47
|
+
source: :descendant
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ClosureTree
|
4
|
+
# Minimal configuration class to handle deprecated options
|
5
|
+
class Configuration
|
6
|
+
def database_less=(_value)
|
7
|
+
ActiveSupport::Deprecation.new.warn(
|
8
|
+
'ClosureTree.configure { |config| config.database_less = true } is deprecated ' \
|
9
|
+
'and will be removed in v10.0.0. The database_less option is no longer needed ' \
|
10
|
+
'for modern deployment practices. Remove this configuration from your initializer.'
|
11
|
+
)
|
12
|
+
# Ignore the value - this is a no-op for backward compatibility
|
13
|
+
end
|
14
|
+
|
15
|
+
def database_less?
|
16
|
+
false # Always return false since this option does nothing
|
17
|
+
end
|
18
|
+
|
19
|
+
# Keep the old method name for backward compatibility
|
20
|
+
alias database_less database_less?
|
21
|
+
end
|
22
|
+
end
|
@@ -25,7 +25,8 @@ module ClosureTree
|
|
25
25
|
class_attribute :hierarchy_class
|
26
26
|
self.hierarchy_class = _ct.hierarchy_class_for_model
|
27
27
|
|
28
|
-
#
|
28
|
+
# Include modules - HierarchyMaintenance provides callbacks that Model associations depend on
|
29
|
+
# The order is maintained for consistency, but associations are now set up after all includes
|
29
30
|
include ClosureTree::HierarchyMaintenance
|
30
31
|
include ClosureTree::Model
|
31
32
|
include ClosureTree::Finders
|
@@ -35,9 +36,13 @@ module ClosureTree
|
|
35
36
|
include ClosureTree::DeterministicOrdering if _ct.order_option?
|
36
37
|
include ClosureTree::NumericDeterministicOrdering if _ct.order_is_numeric?
|
37
38
|
|
39
|
+
# Include AssociationSetup last to ensure all dependencies are ready
|
40
|
+
include ClosureTree::AssociationSetup
|
41
|
+
|
38
42
|
connection_pool.release_connection
|
39
43
|
end
|
40
44
|
|
41
|
-
alias acts_as_tree
|
45
|
+
# Only alias acts_as_tree if it's not already defined (to avoid conflicts with other gems)
|
46
|
+
alias acts_as_tree has_closure_tree unless method_defined?(:acts_as_tree)
|
42
47
|
end
|
43
48
|
end
|
data/lib/closure_tree/model.rb
CHANGED
@@ -6,45 +6,6 @@ module ClosureTree
|
|
6
6
|
module Model
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
-
included do
|
10
|
-
belongs_to :parent, nil,
|
11
|
-
class_name: _ct.model_class.to_s,
|
12
|
-
foreign_key: _ct.parent_column_name,
|
13
|
-
inverse_of: :children,
|
14
|
-
touch: _ct.options[:touch],
|
15
|
-
optional: true
|
16
|
-
|
17
|
-
order_by_generations = -> { Arel.sql("#{_ct.quoted_hierarchy_table_name}.generations ASC") }
|
18
|
-
|
19
|
-
has_many :children, *_ct.has_many_order_with_option, class_name: _ct.model_class.to_s,
|
20
|
-
foreign_key: _ct.parent_column_name,
|
21
|
-
dependent: _ct.options[:dependent],
|
22
|
-
inverse_of: :parent do
|
23
|
-
# We have to redefine hash_tree because the activerecord relation is already scoped to parent_id.
|
24
|
-
def hash_tree(options = {})
|
25
|
-
# we want limit_depth + 1 because we don't do self_and_descendants.
|
26
|
-
limit_depth = options[:limit_depth]
|
27
|
-
_ct.hash_tree(@association.owner.descendants, limit_depth ? limit_depth + 1 : nil)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
has_many :ancestor_hierarchies, *_ct.has_many_order_without_option(order_by_generations),
|
32
|
-
class_name: _ct.hierarchy_class_name,
|
33
|
-
foreign_key: 'descendant_id'
|
34
|
-
|
35
|
-
has_many :self_and_ancestors, *_ct.has_many_order_without_option(order_by_generations),
|
36
|
-
through: :ancestor_hierarchies,
|
37
|
-
source: :ancestor
|
38
|
-
|
39
|
-
has_many :descendant_hierarchies, *_ct.has_many_order_without_option(order_by_generations),
|
40
|
-
class_name: _ct.hierarchy_class_name,
|
41
|
-
foreign_key: 'ancestor_id'
|
42
|
-
|
43
|
-
has_many :self_and_descendants, *_ct.has_many_order_with_option(order_by_generations),
|
44
|
-
through: :descendant_hierarchies,
|
45
|
-
source: :descendant
|
46
|
-
end
|
47
|
-
|
48
9
|
# Delegate to the Support instance on the class:
|
49
10
|
def _ct
|
50
11
|
self.class._ct
|
data/lib/closure_tree/version.rb
CHANGED
data/lib/closure_tree.rb
CHANGED
@@ -9,11 +9,16 @@ loader.setup
|
|
9
9
|
|
10
10
|
module ClosureTree
|
11
11
|
def self.configure
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
if block_given?
|
13
|
+
# Create a temporary configuration object to capture deprecated settings
|
14
|
+
config = Configuration.new
|
15
|
+
yield config
|
16
|
+
else
|
17
|
+
ActiveSupport::Deprecation.new.warn(
|
18
|
+
'ClosureTree.configure is deprecated and will be removed in a future version. ' \
|
19
|
+
'Configuration is no longer needed.'
|
20
|
+
)
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
19
24
|
|
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.1.
|
4
|
+
version: 9.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew McEachen
|
@@ -152,6 +152,8 @@ files:
|
|
152
152
|
- lib/closure_tree.rb
|
153
153
|
- lib/closure_tree/active_record_support.rb
|
154
154
|
- lib/closure_tree/arel_helpers.rb
|
155
|
+
- lib/closure_tree/association_setup.rb
|
156
|
+
- lib/closure_tree/configuration.rb
|
155
157
|
- lib/closure_tree/deterministic_ordering.rb
|
156
158
|
- lib/closure_tree/digraphs.rb
|
157
159
|
- lib/closure_tree/finders.rb
|
@@ -176,7 +178,7 @@ licenses:
|
|
176
178
|
metadata:
|
177
179
|
bug_tracker_uri: https://github.com/ClosureTree/closure_tree/issues
|
178
180
|
changelog_uri: https://github.com/ClosureTree/closure_tree/blob/master/CHANGELOG.md
|
179
|
-
documentation_uri: https://www.rubydoc.info/gems/closure_tree/9.1.
|
181
|
+
documentation_uri: https://www.rubydoc.info/gems/closure_tree/9.1.1
|
180
182
|
homepage_uri: https://closuretree.github.io/closure_tree/
|
181
183
|
source_code_uri: https://github.com/ClosureTree/closure_tree
|
182
184
|
rubygems_mfa_required: 'true'
|