acts_as_recursive_tree 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  #