acts_as_recursive_tree 2.2.1 → 3.0.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +37 -0
  3. data/.github/workflows/lint.yml +31 -0
  4. data/.github/workflows/rubygem.yml +37 -0
  5. data/.gitignore +2 -1
  6. data/.rubocop.yml +30 -1
  7. data/.rubocop_todo.yml +28 -281
  8. data/Appraisals +10 -20
  9. data/CHANGELOG.md +10 -1
  10. data/Gemfile +2 -0
  11. data/README.md +3 -0
  12. data/Rakefile +8 -8
  13. data/acts_as_recursive_tree.gemspec +27 -18
  14. data/gemfiles/ar_52.gemfile +8 -0
  15. data/gemfiles/ar_60.gemfile +8 -0
  16. data/gemfiles/ar_61.gemfile +8 -0
  17. data/lib/acts_as_recursive_tree.rb +7 -11
  18. data/lib/acts_as_recursive_tree/acts_macro.rb +6 -6
  19. data/lib/acts_as_recursive_tree/associations.rb +10 -8
  20. data/lib/acts_as_recursive_tree/builders/ancestors.rb +3 -2
  21. data/lib/acts_as_recursive_tree/builders/descendants.rb +3 -1
  22. data/lib/acts_as_recursive_tree/builders/leaves.rb +7 -8
  23. data/lib/acts_as_recursive_tree/builders/relation_builder.rb +14 -10
  24. data/lib/acts_as_recursive_tree/builders/{strategy.rb → strategies.rb} +3 -9
  25. data/lib/acts_as_recursive_tree/builders/{strategy → strategies}/ancestor.rb +3 -1
  26. data/lib/acts_as_recursive_tree/builders/{strategy → strategies}/descendant.rb +3 -1
  27. data/lib/acts_as_recursive_tree/builders/{strategy → strategies}/join.rb +5 -3
  28. data/lib/acts_as_recursive_tree/builders/{strategy → strategies}/subselect.rb +3 -1
  29. data/lib/acts_as_recursive_tree/config.rb +2 -0
  30. data/lib/acts_as_recursive_tree/model.rb +9 -8
  31. data/lib/acts_as_recursive_tree/options/depth_condition.rb +3 -2
  32. data/lib/acts_as_recursive_tree/options/query_options.rb +4 -2
  33. data/lib/acts_as_recursive_tree/options/values.rb +17 -19
  34. data/lib/acts_as_recursive_tree/railtie.rb +2 -0
  35. data/lib/acts_as_recursive_tree/scopes.rb +8 -4
  36. data/lib/acts_as_recursive_tree/version.rb +3 -1
  37. data/spec/builders_spec.rb +17 -11
  38. data/spec/db/database.rb +5 -4
  39. data/spec/db/database.yml +2 -5
  40. data/spec/db/models.rb +12 -11
  41. data/spec/db/schema.rb +3 -4
  42. data/spec/model/location_spec.rb +7 -11
  43. data/spec/model/node_spec.rb +35 -49
  44. data/spec/model/relation_spec.rb +6 -11
  45. data/spec/spec_helper.rb +54 -55
  46. data/spec/values_spec.rb +21 -17
  47. metadata +115 -33
  48. data/lib/acts_as_recursive_tree/builders.rb +0 -14
  49. data/lib/acts_as_recursive_tree/options.rb +0 -9
data/Appraisals CHANGED
@@ -1,26 +1,16 @@
1
- appraise "ar-50" do
2
- ruby '~> 2'
1
+ # frozen_string_literal: true
3
2
 
4
- gem 'activerecord', '~> 5.0.0'
5
- gem 'sqlite3', '~> 1.3.6'
3
+ appraise 'ar-52' do
4
+ gem 'activerecord', '~> 5.2.0'
5
+ gem 'activesupport', '~> 5.2.0'
6
6
  end
7
7
 
8
- appraise "ar-51" do
9
- ruby '~> 2'
10
-
11
- gem 'activerecord', '~> 5.1.0'
12
- end
13
-
14
- appraise "ar-52" do
15
- ruby '~> 2'
16
-
17
- gem 'activerecord', '~> 5.2.0'
18
- end
19
-
20
- appraise "ar-60" do
21
- gem 'activerecord', '~> 6.0.0'
8
+ appraise 'ar-60' do
9
+ gem 'activerecord', '~> 6.0.0'
10
+ gem 'activesupport', '~> 6.0.0'
22
11
  end
23
12
 
24
- appraise "ar-61" do
25
- gem 'activerecord', '~> 6.1.0'
13
+ appraise 'ar-61' do
14
+ gem 'activerecord', '~> 6.1.0'
15
+ gem 'activesupport', '~> 6.1.0'
26
16
  end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ### Version 3.0.0
2
+ - BREAKING: Dropped support for Rails < 5.2
3
+ - BREAKING: Increased minimum Ruby version to 2.5
4
+ - ADD: initial support for Rails < 7
5
+ - CHANGE: Using zeitwerk for auto loading
6
+
7
+ ### Version 2.2.1
8
+ - Rails 6.1 support
9
+
1
10
  ### Version 2.2.0
2
11
  - Rails 6.0 support
3
12
 
@@ -32,4 +41,4 @@
32
41
  - BUGFIX: ordering result when querying ancestors
33
42
 
34
43
  ### Version 1.0.0
35
- - inital release using AREL
44
+ - initial release using AREL
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in acts_as_recursive_tree.gemspec
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # ActsAsRecursiveTree
2
2
 
3
+ [![CI Status](https://github.com/1and1/acts_as_recursive_tree/workflows/CI/badge.svg?branch=main)](https://github.com/1and1/acts_as_recursive_tree/actions?query=workflow%3ACI+branch%3Amaster)
4
+ [![Gem Version](https://badge.fury.io/rb/acts_as_recursive_tree.svg)](https://badge.fury.io/rb/acts_as_recursive_tree)
5
+
3
6
  Use the power of recursive SQL statements in your Rails application.
4
7
 
5
8
  When you have tree based data in your application, you always to struggle with retrieving data. There are solutions, but the always come at a price:
data/Rakefile CHANGED
@@ -1,15 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
- begin
3
- require 'rspec/core/rake_task'
4
- RSpec::Core::RakeTask.new(:spec)
4
+ require 'rspec/core/rake_task'
5
5
 
6
- task default: [:spec]
7
- rescue LoadError
8
- end
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: [:spec]
9
9
 
10
10
  desc 'Deletes temporary files'
11
11
  task :clean_tmp_files do
12
- %w( db.log test.sqlite3 ).each do |file|
13
- File.delete(file) if File.exists?(file)
12
+ %w[db.log test.sqlite3].each do |file|
13
+ File.delete(file) if File.exist?(file)
14
14
  end
15
15
  end
@@ -1,30 +1,39 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'acts_as_recursive_tree/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = 'acts_as_recursive_tree'
8
- spec.version = ActsAsRecursiveTree::VERSION
9
- spec.authors = ['Wolfgang Wedelich-John', 'Willem Mulder']
10
- spec.email = ['wolfgang.wedelich@1und1.de', '14mRh4X0r@gmail.com']
11
- spec.summary = %q{Drop in replacement for acts_as_tree but using recursive queries}
12
- spec.description = %q{
13
- This is a ruby gem that provides drop in replacement for acts_as_tree but makes use of SQL recursive statement. Be sure to have a DBMS that supports recursive queries when using this gem (e.g. PostgreSQL or SQLite). }
14
- spec.homepage = 'https://github.com/1and1/acts_as_recursive_tree'
15
- spec.license = 'MIT'
16
-
17
- spec.required_ruby_version = '>= 2.0.0'
8
+ spec.name = 'acts_as_recursive_tree'
9
+ spec.version = ActsAsRecursiveTree::VERSION
10
+ spec.authors = ['Wolfgang Wedelich-John', 'Willem Mulder']
11
+ spec.email = %w[wolfgang.wedelich@ionos.com 14mRh4X0r@gmail.com]
12
+ spec.summary = 'Drop in replacement for acts_as_tree but using recursive queries'
13
+ spec.description = '
14
+ This is a ruby gem that provides drop in replacement for acts_as_tree but makes use of SQL recursive statement. Be sure to have a DBMS that supports recursive queries when using this gem (e.g. PostgreSQL or SQLite). '
15
+ spec.homepage = 'https://github.com/1and1/acts_as_recursive_tree'
16
+ spec.license = 'MIT'
17
+ spec.metadata = {
18
+ 'bug_tracker_uri' => 'https://github.com/1and1/acts_as_recursive_tree/issues',
19
+ 'changelog_uri' => 'https://github.com/1and1/acts_as_recursive_tree/CHANGELOG.md'
20
+ }
21
+ spec.required_ruby_version = '>= 2.5.0'
18
22
  spec.files = `git ls-files -z`.split("\x0")
19
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
23
  spec.test_files = spec.files.grep(%r{^spec/})
21
24
  spec.require_paths = ['lib']
22
25
 
23
- spec.add_runtime_dependency 'activerecord', '>= 5.0.0', '< 6.2.0'
26
+ spec.add_runtime_dependency 'activerecord', '>= 5.2.0', '< 7.0'
27
+ spec.add_runtime_dependency 'activesupport', '>= 5.2.0', '< 7.0'
28
+ spec.add_runtime_dependency 'zeitwerk', '>= 2.4'
24
29
 
30
+ spec.add_development_dependency 'appraisal', '~> 2.4'
25
31
  spec.add_development_dependency 'database_cleaner', '~> 1.5'
26
- spec.add_development_dependency 'rake', '~> 10.0'
27
- spec.add_development_dependency 'rspec-rails', '~> 3.5'
32
+ spec.add_development_dependency 'rake'
33
+ spec.add_development_dependency 'rspec-rails', '>= 3.5'
34
+ spec.add_development_dependency 'rubocop', '>= 1.8.0'
35
+ spec.add_development_dependency 'rubocop-rails', '>= 2.9.0'
36
+ spec.add_development_dependency 'rubocop-rspec', '>= 2.1.0'
37
+
28
38
  spec.add_development_dependency 'sqlite3', '~> 1.3'
29
- spec.add_development_dependency 'appraisal', '~> 2.4'
30
39
  end
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.2.0"
6
+ gem "activesupport", "~> 5.2.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.0.0"
6
+ gem "activesupport", "~> 6.0.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.1.0"
6
+ gem "activesupport", "~> 6.1.0"
7
+
8
+ gemspec path: "../"
@@ -1,15 +1,11 @@
1
- require 'active_support/all'
1
+ # frozen_string_literal: true
2
+
2
3
  require_relative 'acts_as_recursive_tree/railtie' if defined?(Rails)
4
+ require 'zeitwerk'
3
5
 
4
- module ActsAsRecursiveTree
5
- extend ActiveSupport::Autoload
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.setup
6
8
 
7
- autoload :Config
8
- autoload :ActsMacro
9
- autoload :Model
10
- autoload :Associations
11
- autoload :Scopes
12
- autoload :Version
13
- autoload :Options
14
- autoload :Builders
9
+ module ActsAsRecursiveTree
10
+ # nothing special here
15
11
  end
@@ -1,17 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActsAsRecursiveTree
2
4
  module ActsMacro
3
-
4
5
  ##
5
6
  # Configuration options are:
6
7
  #
7
8
  # * <tt>foreign_key</tt> - specifies the column name to use for tracking
8
9
  # of the tree (default: +parent_id+)
9
10
  def recursive_tree(parent_key: :parent_id, parent_type_column: nil)
10
-
11
11
  class_attribute :_recursive_tree_config
12
12
  self._recursive_tree_config = Config.new(
13
- model_class: self,
14
- parent_key: parent_key.to_sym,
13
+ model_class: self,
14
+ parent_key: parent_key.to_sym,
15
15
  parent_type_column: parent_type_column.try(:to_sym)
16
16
  )
17
17
 
@@ -20,6 +20,6 @@ module ActsAsRecursiveTree
20
20
  include ActsAsRecursiveTree::Scopes
21
21
  end
22
22
 
23
- alias_method :acts_as_tree, :recursive_tree
23
+ alias acts_as_tree recursive_tree
24
24
  end
25
- end
25
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module ActsAsRecursiveTree
@@ -6,20 +8,20 @@ module ActsAsRecursiveTree
6
8
 
7
9
  included do
8
10
  belongs_to :parent,
9
- class_name: self.base_class.to_s,
10
- foreign_key: self._recursive_tree_config.parent_key,
11
- inverse_of: :children,
12
- optional: true
11
+ class_name: base_class.to_s,
12
+ foreign_key: _recursive_tree_config.parent_key,
13
+ inverse_of: :children,
14
+ optional: true
13
15
 
14
16
  has_many :children,
15
- class_name: self.base_class.to_s,
16
- foreign_key: self._recursive_tree_config.parent_key,
17
- inverse_of: :parent
17
+ class_name: base_class.to_s,
18
+ foreign_key: _recursive_tree_config.parent_key,
19
+ inverse_of: :parent
18
20
 
19
21
  has_many :self_and_siblings,
20
22
  through: :parent,
21
23
  source: :children,
22
- class_name: self.base_class.to_s
24
+ class_name: base_class.to_s
23
25
  end
24
26
  end
25
27
  end
@@ -1,14 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActsAsRecursiveTree
2
4
  module Builders
3
5
  class Ancestors < RelationBuilder
4
- self.traversal_strategy = ActsAsRecursiveTree::Builders::Strategy::Ancestor
6
+ self.traversal_strategy = ActsAsRecursiveTree::Builders::Strategies::Ancestor
5
7
 
6
8
  def get_query_options(_)
7
9
  opts = super
8
10
  opts.ensure_ordering!
9
11
  opts
10
12
  end
11
-
12
13
  end
13
14
  end
14
15
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActsAsRecursiveTree
2
4
  module Builders
3
5
  class Descendants < RelationBuilder
4
- self.traversal_strategy = ActsAsRecursiveTree::Builders::Strategy::Descendant
6
+ self.traversal_strategy = ActsAsRecursiveTree::Builders::Strategies::Descendant
5
7
  end
6
8
  end
7
9
  end
@@ -1,26 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActsAsRecursiveTree
2
4
  module Builders
3
5
  class Leaves < Descendants
4
-
5
6
  def create_select_manger(column = nil)
6
7
  select_manager = super
7
8
 
8
9
  select_manager.where(
9
- travers_loc_table[primary_key].not_in(
10
- travers_loc_table.where(
11
- travers_loc_table[parent_key].not_eq(nil)
12
- ).project(travers_loc_table[parent_key])
13
- )
10
+ travers_loc_table[primary_key].not_in(
11
+ travers_loc_table.where(
12
+ travers_loc_table[parent_key].not_eq(nil)
13
+ ).project(travers_loc_table[parent_key])
14
+ )
14
15
  )
15
16
  select_manager
16
-
17
17
  end
18
18
 
19
19
  def get_query_options(_)
20
20
  # do not allow any custom options
21
21
  ActsAsRecursiveTree::Options::QueryOptions.new
22
22
  end
23
-
24
23
  end
25
24
  end
26
25
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActsAsRecursiveTree
2
4
  module Builders
3
5
  #
4
6
  # Constructs the Arel necessary for recursion.
5
7
  #
6
8
  class RelationBuilder
7
-
8
9
  def self.build(klass, ids, exclude_ids: false, &block)
9
10
  new(klass, ids, exclude_ids: exclude_ids, &block).build
10
11
  end
@@ -12,6 +13,7 @@ module ActsAsRecursiveTree
12
13
  class_attribute :traversal_strategy, instance_writer: false
13
14
 
14
15
  attr_reader :klass, :ids, :recursive_temp_table, :travers_loc_table, :without_ids
16
+
15
17
  mattr_reader(:random) { Random.new }
16
18
 
17
19
  # Delegators for easier accessing config and query options
@@ -41,7 +43,7 @@ module ActsAsRecursiveTree
41
43
  def get_query_options(proc)
42
44
  opts = ActsAsRecursiveTree::Options::QueryOptions.new
43
45
 
44
- proc.call(opts) if proc
46
+ proc&.call(opts)
45
47
 
46
48
  opts
47
49
  end
@@ -51,14 +53,14 @@ module ActsAsRecursiveTree
51
53
  end
52
54
 
53
55
  def build
54
- relation = Strategy.for_query_options(@query_opts).build(self)
56
+ relation = Strategies.for_query_options(@query_opts).build(self)
55
57
 
56
- relation = apply_except_id(relation)
57
- relation
58
+ apply_except_id(relation)
58
59
  end
59
60
 
60
61
  def apply_except_id(relation)
61
62
  return relation unless without_ids
63
+
62
64
  relation.where(ids.apply_negated_to(base_table[primary_key]))
63
65
  end
64
66
 
@@ -70,10 +72,10 @@ module ActsAsRecursiveTree
70
72
 
71
73
  def create_select_manger(column = nil)
72
74
  projections = if column
73
- travers_loc_table[column]
74
- else
75
- Arel.star
76
- end
75
+ travers_loc_table[column]
76
+ else
77
+ Arel.star
78
+ end
77
79
 
78
80
  select_mgr = travers_loc_table.project(projections).with(:recursive, build_cte_table)
79
81
 
@@ -114,7 +116,8 @@ module ActsAsRecursiveTree
114
116
  end
115
117
 
116
118
  def apply_parent_type_column(arel_condition)
117
- return arel_condition unless parent_type_column.present?
119
+ return arel_condition if parent_type_column.blank?
120
+
118
121
  arel_condition.and(base_table[parent_type_column].eq(klass.base_class))
119
122
  end
120
123
 
@@ -131,6 +134,7 @@ module ActsAsRecursiveTree
131
134
  def apply_query_opts_condition(relation)
132
135
  # check with nil? and not #present?/#blank? which will execute the query
133
136
  return relation if condition.nil?
137
+
134
138
  relation.merge(condition)
135
139
  end
136
140
  end
@@ -1,17 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActsAsRecursiveTree
2
4
  module Builders
3
5
  #
4
6
  # Strategy module for different strategies of how to build the resulting query.
5
7
  #
6
- module Strategy
7
- extend ActiveSupport::Autoload
8
-
9
- autoload :Join
10
- autoload :Subselect
11
-
12
- autoload :Descendant
13
- autoload :Ancestor
14
-
8
+ module Strategies
15
9
  #
16
10
  # Returns a Strategy appropriate for query_opts
17
11
  #