closure_tree 9.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7da1033357e368c9a4d2a36b95a8e75899b61b63e872c25167dd5f8862afb454
4
- data.tar.gz: '08df010ac42f946d8171152ad5ef4a8e6c1b94263cf39e5d297e4cbf805dca81'
3
+ metadata.gz: 4a3ac565445f8b4e6054dd67fe023b025db79b17a2d16af5ba8804f190142e37
4
+ data.tar.gz: d7e7af95d8ef2b2f01c17248e31aaf3b1c7c51109670fc88fefb9343934ee8ff
5
5
  SHA512:
6
- metadata.gz: 34ef107ba1737f7e4cec6ff581149ce66b4daed0dee6b0e4bc266688a748c182e8c4e1082d38846de22671a23b2a640d80f753715f8e93fdc7889d134cd14c97
7
- data.tar.gz: 300a2a3b499a8428e23f89235e409dce6cbd50378ee6406f2ab03d03b97f63d20b5365ff9adabbfaff47ce9b50a54fd5a2e80576dd5839b7d23debce98528074
6
+ metadata.gz: 9ecedfd06bac1b6d234c05624730b74c93b34d8ba1cb81b09653f7af3bea42d17f0154d471a7f8c0bbc842981a2a1e1260b061a51fbec4cba49f9963f7aced9c
7
+ data.tar.gz: 8a329d9a4483a36411515789d2d596905a27501dada6ef77f25cb0b34c3154eb3c5aad0aef28f9e4978987d33587d352a4027dd2cc0d7acf422970729e195151
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
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
+
10
+ ## [9.1.0](https://github.com/ClosureTree/closure_tree/compare/closure_tree/v9.0.0...closure_tree/v9.1.0) (2025-07-23)
11
+
12
+
13
+ ### Features
14
+
15
+ * migrate from ActiveSupport::Autoload to Zeitwerk ([#457](https://github.com/ClosureTree/closure_tree/issues/457)) ([d18e80c](https://github.com/ClosureTree/closure_tree/commit/d18e80cdbd4f3510377363bc7b5166f0cc1b0a6f))
16
+
3
17
  ## [9.0.0](https://github.com/ClosureTree/closure_tree/compare/closure_tree-v8.0.0...closure_tree/v9.0.0) (2025-07-21)
4
18
 
5
19
 
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
- => ["Grandparent", "Parent", "First Child", "Second Child", "Third Child", "Fourth Child"]
161
+ #=> ["Grandparent", "Parent", "First Child", "Second Child", "Third Child", "Fourth Child"]
160
162
 
161
163
  child1.ancestry_path
162
- => ["Grandparent", "Parent", "First Child"]
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
data/closure_tree.gemspec CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |gem|
27
27
 
28
28
  gem.add_dependency 'activerecord', '>= 7.2.0'
29
29
  gem.add_dependency 'with_advisory_lock', '>= 7.0.0'
30
+ gem.add_dependency 'zeitwerk', '~> 2.7'
30
31
 
31
32
  gem.add_development_dependency 'database_cleaner'
32
33
  gem.add_development_dependency 'minitest'
@@ -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
@@ -1,11 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClosureTree
4
- class Configuration # :nodoc:
5
- attr_accessor :database_less
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
6
14
 
7
- def initialize
8
- @database_less = ENV['DATABASE_URL'].to_s.include?('//user:pass@127.0.0.1/')
15
+ def database_less?
16
+ false # Always return false since this option does nothing
9
17
  end
18
+
19
+ # Keep the old method name for backward compatibility
20
+ alias database_less database_less?
10
21
  end
11
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
- # tests fail if you include Model before HierarchyMaintenance wtf
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,11 +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
- rescue StandardError => e
40
- raise e unless ClosureTree.configuration.database_less
41
43
  end
42
44
 
43
- alias acts_as_tree has_closure_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)
44
47
  end
45
48
  end
@@ -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
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'closure_tree/support_flags'
4
- require 'closure_tree/support_attributes'
5
- require 'closure_tree/numeric_order_support'
6
- require 'closure_tree/active_record_support'
7
- require 'closure_tree/hash_tree_support'
8
- require 'closure_tree/arel_helpers'
9
-
10
3
  # This class and mixins are an effort to reduce the namespace pollution to models that act_as_tree.
11
4
  module ClosureTree
12
5
  class Support
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClosureTree
4
- VERSION = Gem::Version.new('9.0.0')
4
+ VERSION = Gem::Version.new('9.1.1')
5
5
  end
data/lib/closure_tree.rb CHANGED
@@ -1,50 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_record'
4
+ require 'zeitwerk'
4
5
 
5
- module ClosureTree
6
- extend ActiveSupport::Autoload
7
-
8
- autoload :HasClosureTree
9
- autoload :HasClosureTreeRoot
10
- autoload :Support
11
- autoload :HierarchyMaintenance
12
- autoload :Model
13
- autoload :Finders
14
- autoload :HashTree
15
- autoload :Digraphs
16
- autoload :DeterministicOrdering
17
- autoload :NumericDeterministicOrdering
18
- autoload :Configuration
19
- autoload :AdapterSupport
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.ignore("#{__dir__}/generators")
8
+ loader.setup
20
9
 
10
+ module ClosureTree
21
11
  def self.configure
22
- yield configuration
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
23
22
  end
24
-
25
- def self.configuration
26
- @configuration ||= Configuration.new
27
- end
28
- end
29
-
30
- ActiveSupport.on_load :active_record do
31
- ActiveRecord::Base.extend ClosureTree::HasClosureTree
32
- ActiveRecord::Base.extend ClosureTree::HasClosureTreeRoot
33
- end
34
-
35
- # Adapter injection for different database types
36
- ActiveSupport.on_load :active_record_postgresqladapter do
37
- prepend ClosureTree::AdapterSupport
38
- end
39
-
40
- ActiveSupport.on_load :active_record_mysql2adapter do
41
- prepend ClosureTree::AdapterSupport
42
- end
43
-
44
- ActiveSupport.on_load :active_record_trilogyadapter do
45
- prepend ClosureTree::AdapterSupport
46
23
  end
47
24
 
48
- ActiveSupport.on_load :active_record_sqlite3adapter do
49
- prepend ClosureTree::AdapterSupport
25
+ ActiveSupport.on_load(:active_record) do
26
+ extend ClosureTree::HasClosureTree, ClosureTree::HasClosureTreeRoot
50
27
  end
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.0.0
4
+ version: 9.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew McEachen
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 7.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: zeitwerk
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.7'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: database_cleaner
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -137,8 +151,8 @@ files:
137
151
  - closure_tree.gemspec
138
152
  - lib/closure_tree.rb
139
153
  - lib/closure_tree/active_record_support.rb
140
- - lib/closure_tree/adapter_support.rb
141
154
  - lib/closure_tree/arel_helpers.rb
155
+ - lib/closure_tree/association_setup.rb
142
156
  - lib/closure_tree/configuration.rb
143
157
  - lib/closure_tree/deterministic_ordering.rb
144
158
  - lib/closure_tree/digraphs.rb
@@ -156,9 +170,7 @@ files:
156
170
  - lib/closure_tree/support_flags.rb
157
171
  - lib/closure_tree/test/matcher.rb
158
172
  - lib/closure_tree/version.rb
159
- - lib/generators/closure_tree/config_generator.rb
160
173
  - lib/generators/closure_tree/migration_generator.rb
161
- - lib/generators/closure_tree/templates/config.rb
162
174
  - lib/generators/closure_tree/templates/create_hierarchies_table.rb.erb
163
175
  homepage: https://github.com/ClosureTree/closure_tree/
164
176
  licenses:
@@ -166,7 +178,7 @@ licenses:
166
178
  metadata:
167
179
  bug_tracker_uri: https://github.com/ClosureTree/closure_tree/issues
168
180
  changelog_uri: https://github.com/ClosureTree/closure_tree/blob/master/CHANGELOG.md
169
- documentation_uri: https://www.rubydoc.info/gems/closure_tree/9.0.0
181
+ documentation_uri: https://www.rubydoc.info/gems/closure_tree/9.1.1
170
182
  homepage_uri: https://closuretree.github.io/closure_tree/
171
183
  source_code_uri: https://github.com/ClosureTree/closure_tree
172
184
  rubygems_mfa_required: 'true'
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ClosureTree
4
- module AdapterSupport
5
- extend ActiveSupport::Concern
6
-
7
- # This module is now only used to ensure the adapter has been loaded
8
- # The actual advisory lock functionality is handled through the model's
9
- # with_advisory_lock method from the with_advisory_lock gem
10
- end
11
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ClosureTree
4
- module Generators # :nodoc:
5
- class ConfigGenerator < Rails::Generators::Base # :nodoc:
6
- source_root File.expand_path('templates', __dir__)
7
- desc 'Install closure tree config.'
8
-
9
- def config
10
- template 'config.rb', 'config/initializers/closure_tree_config.rb'
11
- end
12
- end
13
- end
14
- end
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ClosureTree.configure do |config|
4
- # Some PaaS like Heroku don't have available the db in some build steps like
5
- # assets:precompile, this is skipped when this value is true, default = false
6
- # config.database_less = true
7
- end