acts_as_recursive_tree 3.1.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3681366c263bd90e2479c6a4761042825547241a15e828678c260789f05ad54
4
- data.tar.gz: c526e787809100bcba957ddeae424ffa068afe7f10e8e2ece1aec354cce6c70b
3
+ metadata.gz: b60be808a8b0a3e93edfbb83a83cc8b270eebade9888ddc6038bb4af4429d7da
4
+ data.tar.gz: 96622ef4e488bbd73de8dd4d0ae51c51a2b11843219d2a34669367ace4f77d60
5
5
  SHA512:
6
- metadata.gz: 2b4e5b9a5a1462b39cc8c2cc0184d8db0653b1d02be8728d2bf46fa309d847224765c3e6b8ac885f064a42f64b34375d9e032e888d5588c9940d55225ceced4a
7
- data.tar.gz: 4e8a2077169e98b889671412c87c80d21f03541bbcdc62cefe520e47c626459c29ff1ee6a07bb54a9ebd94ca8418c4369d27ff9950a3c559b0bd7ef41970afc6
6
+ metadata.gz: fca6f5732cc344c395d1d74f3cc13ac5be4af9ea74eb545764b28ef1a7550fcb3300975cccce92a2772f5d3ca1e12fb1e4a6e124f07037f66099b86256918c57
7
+ data.tar.gz: 8b2cf07e6aa5ef4543f6d81200ac3421d918039140480314496c416f8ff3f6fce65a1ba43bdcae837f67df5e279e9281d8680ca76945d84a8115877eeb4cc7d2
@@ -19,19 +19,35 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['2.5', '2.6', '2.7', '3.0']
22
+ ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2']
23
23
  gemfile: [ar_52, ar_60, ar_61, ar_70]
24
24
  exclude:
25
+ - ruby-version: '3.2'
26
+ gemfile: ar_52
27
+ - ruby-version: '3.2'
28
+ gemfile: ar_60
29
+ - ruby-version: '3.2'
30
+ gemfile: ar_61
31
+ - ruby-version: '3.1'
32
+ gemfile: ar_52
33
+ - ruby-version: '3.1'
34
+ gemfile: ar_60
35
+ - ruby-version: '3.1'
36
+ gemfile: ar_61
25
37
  - ruby-version: '3.0'
26
38
  gemfile: ar_52
27
- - ruby-version: '2.5'
28
- gemfile: ar_70
39
+ # - ruby-version: '2.6'
40
+ # gemfile: ar_next
29
41
  - ruby-version: '2.6'
30
42
  gemfile: ar_70
43
+ # - ruby-version: '2.5'
44
+ # gemfile: ar_next
45
+ - ruby-version: '2.5'
46
+ gemfile: ar_70
31
47
  env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
32
48
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
33
49
  steps:
34
- - uses: actions/checkout@v2
50
+ - uses: actions/checkout@v3
35
51
  - name: Set up Ruby
36
52
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
37
53
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ db.log
17
17
  test.sqlite3
18
18
  /gemfiles/*.lock
19
19
  /gemfiles/.bundle
20
+ /.ruby-version
data/Appraisals CHANGED
@@ -16,6 +16,13 @@ appraise 'ar-61' do
16
16
  end
17
17
 
18
18
  appraise 'ar-70' do
19
- gem 'activerecord', '~> 7.0.0'
20
- gem 'activesupport', '~> 7.0.0'
19
+ gem 'activerecord', '~> 7.0'
20
+ gem 'activesupport', '~> 7.0'
21
+ end
22
+
23
+ appraise 'ar-next' do
24
+ git 'https://github.com/rails/rails.git', branch: 'main' do
25
+ gem 'activerecord'
26
+ gem 'activesupport'
27
+ end
21
28
  end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### Version 3.3.0
2
+ - added __includes:__ option to __preload_tree__
3
+
4
+ ### Version 3.2.0
5
+ - Added #preload_tree method to preload the parent/child relations of a single node
6
+
1
7
  ### Version 3.1.0
2
8
  - Rails 7 support
3
9
 
data/README.md CHANGED
@@ -19,6 +19,17 @@ ActsAsRecursiveTree currently supports following ActiveRecord versions and is te
19
19
  * ActiveRecord 6.1.x
20
20
  * ActiveRecord 7.0.x
21
21
 
22
+ ## Supported Rubies
23
+ ActsAsRecursiveTree is tested with following rubies:
24
+ * MRuby 2.5
25
+ * MRuby 2.6
26
+ * MRuby 2.7
27
+ * MRuby 3.0
28
+ * MRuby 3.1
29
+ * MRuby 3.2
30
+
31
+ Other Ruby implementations are not tested, but should also work.
32
+
22
33
  ## Installation
23
34
 
24
35
  Add this line to your application's Gemfile:
@@ -123,7 +134,11 @@ __Additional methods:__
123
134
  __Utility methods:__
124
135
  * `root?` - returns true if this node is a root node
125
136
  * `leaf?` - returns true if this node is a leave node
137
+ * `preload_tree` - fetches all descendants of this node and assigns the proper parent/children associations. You are then able to traverse the tree through the children/parent association without querying the database again. You can also pass arguments to `includes` which will be forwarded when fetching records.
126
138
 
139
+ ```ruby
140
+ node.preload_tree(includes: [:association, :another_association])
141
+ ```
127
142
 
128
143
  ## Customizing the recursion
129
144
 
@@ -201,17 +216,6 @@ sub_node_instance.descendants # => returns Node and SubNode instances
201
216
  ```
202
217
 
203
218
 
204
- ## Known Issues
205
-
206
- When using PostgreSQL as underlying database system chances are good that you encounter following error message:
207
-
208
- `
209
- ActiveRecord::StatementInvalid: PG::ProtocolViolation: ERROR: bind message supplies 1 parameters, but prepared statement "" requires 2
210
- `
211
-
212
- This is a known ActiveRecord issue which should be fixed in Rails 5.2. Alternative
213
-
214
-
215
219
  ## Contributing
216
220
 
217
221
  1. Fork it ( https://github.com/1and1/acts_as_recursive_tree/fork )
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_runtime_dependency 'zeitwerk', '>= 2.4'
29
29
 
30
30
  spec.add_development_dependency 'appraisal', '~> 2.4'
31
- spec.add_development_dependency 'database_cleaner', '~> 1.5'
31
+ spec.add_development_dependency 'database_cleaner', '~> 2.0'
32
32
  spec.add_development_dependency 'rake'
33
33
  spec.add_development_dependency 'rspec-rails', '>= 3.5'
34
34
  spec.add_development_dependency 'rubocop', '~> 1.23.0'
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 7.0.0"
6
- gem "activesupport", "~> 7.0.0"
5
+ gem "activerecord", "~> 7.0"
6
+ gem "activesupport", "~> 7.0"
7
7
 
8
8
  gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git "https://github.com/rails/rails.git", branch: "main" do
6
+ gem "activerecord"
7
+ gem "activesupport"
8
+ end
9
+
10
+ gemspec path: "../"
@@ -8,7 +8,8 @@ module ActsAsRecursiveTree
8
8
  # * <tt>foreign_key</tt> - specifies the column name to use for tracking
9
9
  # of the tree (default: +parent_id+)
10
10
  def recursive_tree(parent_key: :parent_id, parent_type_column: nil)
11
- class_attribute :_recursive_tree_config
11
+ class_attribute(:_recursive_tree_config, instance_writer: false)
12
+
12
13
  self._recursive_tree_config = Config.new(
13
14
  model_class: self,
14
15
  parent_key: parent_key.to_sym,
@@ -91,6 +91,16 @@ module ActsAsRecursiveTree
91
91
  children.none?
92
92
  end
93
93
 
94
+ #
95
+ # Fetches all descendants of this node and assigns the parent/children associations
96
+ #
97
+ # @param includes [Array|Hash] pass the same arguments that should be passed to the #includes() method.
98
+ #
99
+ def preload_tree(includes: nil)
100
+ ActsAsRecursiveTree::Preloaders::Descendants.new(self, includes: includes).preload!
101
+ true
102
+ end
103
+
94
104
  def base_class
95
105
  self.class.base_class
96
106
  end
@@ -99,11 +109,11 @@ module ActsAsRecursiveTree
99
109
 
100
110
  module ClassMethods
101
111
  def self_and_ancestors_of(ids, &block)
102
- Builders::Ancestors.build(self, ids, &block)
112
+ ActsAsRecursiveTree::Builders::Ancestors.build(self, ids, &block)
103
113
  end
104
114
 
105
115
  def ancestors_of(ids, &block)
106
- Builders::Ancestors.build(self, ids, exclude_ids: true, &block)
116
+ ActsAsRecursiveTree::Builders::Ancestors.build(self, ids, exclude_ids: true, &block)
107
117
  end
108
118
 
109
119
  def roots_of(ids)
@@ -111,15 +121,15 @@ module ActsAsRecursiveTree
111
121
  end
112
122
 
113
123
  def self_and_descendants_of(ids, &block)
114
- Builders::Descendants.build(self, ids, &block)
124
+ ActsAsRecursiveTree::Builders::Descendants.build(self, ids, &block)
115
125
  end
116
126
 
117
127
  def descendants_of(ids, &block)
118
- Builders::Descendants.build(self, ids, exclude_ids: true, &block)
128
+ ActsAsRecursiveTree::Builders::Descendants.build(self, ids, exclude_ids: true, &block)
119
129
  end
120
130
 
121
131
  def leaves_of(ids, &block)
122
- Builders::Leaves.build(self, ids, &block)
132
+ ActsAsRecursiveTree::Builders::Leaves.build(self, ids, &block)
123
133
  end
124
134
  end
125
135
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActsAsRecursiveTree
4
+ module Preloaders
5
+ #
6
+ # Preloads all descendants records for a given node and sets the parent and child associations on each record
7
+ # based on the preloaded data. After this, calling #parent or #children will not trigger a database query.
8
+ #
9
+ class Descendants
10
+ def initialize(node, includes: nil)
11
+ @node = node
12
+ @parent_key = node._recursive_tree_config.parent_key
13
+ @includes = includes
14
+ end
15
+
16
+ def preload!
17
+ apply_records(@node)
18
+ end
19
+
20
+ private
21
+
22
+ def records
23
+ @records ||= begin
24
+ descendants = @node.descendants
25
+ descendants = descendants.includes(*@includes) if @includes
26
+ descendants.to_a
27
+ end
28
+ end
29
+
30
+ def apply_records(parent_node)
31
+ children = records.select { |child| child.send(@parent_key) == parent_node.id }
32
+
33
+ parent_node.association(:children).target = children
34
+
35
+ children.each do |child|
36
+ child.association(:parent).target = parent_node
37
+ apply_records(child)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActsAsRecursiveTree
4
- VERSION = '3.1.0'
4
+ VERSION = '3.3.0'
5
5
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe ActsAsRecursiveTree::Builders::Ancestors do
6
+ context 'basic' do
7
+ it_behaves_like 'build recursive query'
8
+ it_behaves_like 'ancestor query'
9
+ include_context 'context with ordering'
10
+ end
11
+
12
+ context 'with options' do
13
+ include_context 'setup with enforced ordering' do
14
+ it_behaves_like 'with ordering'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe ActsAsRecursiveTree::Builders::Descendants do
6
+ context 'basic' do
7
+ it_behaves_like 'build recursive query'
8
+ it_behaves_like 'descendant query'
9
+ include_context 'context without ordering'
10
+ end
11
+
12
+ context 'with options' do
13
+ include_context 'setup with enforced ordering' do
14
+ let(:ordering) { true }
15
+ it_behaves_like 'with ordering'
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe ActsAsRecursiveTree::Builders::Leaves do
6
+ context 'basic' do
7
+ it_behaves_like 'build recursive query'
8
+ it_behaves_like 'descendant query'
9
+ include_context 'context without ordering'
10
+ end
11
+
12
+ context 'with options' do
13
+ include_context 'setup with enforced ordering' do
14
+ let(:ordering) { true }
15
+ it_behaves_like 'without ordering'
16
+ end
17
+ end
18
+ end
@@ -2,21 +2,21 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- shared_examples 'single values' do
6
- subject(:value) { described_class.create(single_value) }
5
+ RSpec.describe ActsAsRecursiveTree::Options::Values do
6
+ shared_examples 'single values' do
7
+ subject(:value) { described_class.create(single_value) }
7
8
 
8
- it { is_expected.to be_a ActsAsRecursiveTree::Options::Values::SingleValue }
9
+ it { is_expected.to be_a described_class::SingleValue }
9
10
 
10
- it 'apply_toes' do
11
- expect(value.apply_to(attribute).to_sql).to end_with " = #{single_value}"
12
- end
11
+ it 'apply_toes' do
12
+ expect(value.apply_to(attribute).to_sql).to end_with " = #{single_value}"
13
+ end
13
14
 
14
- it 'apply_negated_toes' do
15
- expect(value.apply_negated_to(attribute).to_sql).to end_with " != #{single_value}"
15
+ it 'apply_negated_toes' do
16
+ expect(value.apply_negated_to(attribute).to_sql).to end_with " != #{single_value}"
17
+ end
16
18
  end
17
- end
18
19
 
19
- describe ActsAsRecursiveTree::Options::Values do
20
20
  let(:table) { Arel::Table.new('test_table') }
21
21
  let(:attribute) { table['test_attr'] }
22
22
 
@@ -44,7 +44,7 @@ describe ActsAsRecursiveTree::Options::Values do
44
44
 
45
45
  let(:array) { [1, 2, 3] }
46
46
 
47
- it { is_expected.to be_a ActsAsRecursiveTree::Options::Values::MultiValue }
47
+ it { is_expected.to be_a described_class::MultiValue }
48
48
 
49
49
  it 'apply_toes' do
50
50
  expect(value.apply_to(attribute).to_sql).to end_with " IN (#{array.join(', ')})"
@@ -60,7 +60,7 @@ describe ActsAsRecursiveTree::Options::Values do
60
60
 
61
61
  let(:range) { 1..3 }
62
62
 
63
- it { is_expected.to be_a ActsAsRecursiveTree::Options::Values::RangeValue }
63
+ it { is_expected.to be_a described_class::RangeValue }
64
64
 
65
65
  it 'apply_toes' do
66
66
  expect(value.apply_to(attribute).to_sql).to end_with "BETWEEN #{range.begin} AND #{range.end}"
@@ -83,7 +83,7 @@ describe ActsAsRecursiveTree::Options::Values do
83
83
  end
84
84
  end
85
85
 
86
- it { is_expected.to be_a ActsAsRecursiveTree::Options::Values::Relation }
86
+ it { is_expected.to be_a described_class::Relation }
87
87
 
88
88
  it 'apply_toes' do
89
89
  expect(value.apply_to(attribute).to_sql).to match(/IN \(SELECT.*\)/)
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe ActsAsRecursiveTree::Preloaders::Descendants do
6
+ include TreeMethods
7
+
8
+ let(:preloader) { described_class.new(root.reload, includes: included_associations) }
9
+ let(:included_associations) { nil }
10
+ let(:root) { create_tree(2, create_node_info: true) }
11
+ let(:children) { root.children }
12
+
13
+ describe '#preload! will set the associations target attribute' do
14
+ before do
15
+ preloader.preload!
16
+ end
17
+
18
+ it 'sets the children association' do
19
+ children.each do |child|
20
+ expect(child.association(:children).target).not_to be_nil
21
+ end
22
+ end
23
+
24
+ it 'sets the parent association' do
25
+ children.each do |child|
26
+ expect(child.association(:parent).target).not_to be_nil
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#preload! will include associations' do
32
+ let(:included_associations) { :node_info }
33
+
34
+ before do
35
+ preloader.preload!
36
+ end
37
+
38
+ it 'sets the children association' do
39
+ children.each do |child|
40
+ expect(child.association(included_associations).target).not_to be_nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Location do
5
+ RSpec.describe Location do
6
6
  before do
7
7
  @building = Building.create!(name: 'big house')
8
8
 
@@ -2,17 +2,8 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Node do
6
- def create_tree(max_level, current_level = 0, node = nil)
7
- node = Node.create!(name: 'root') if node.nil?
8
-
9
- 1.upto(max_level - current_level) do |index|
10
- child = node.children.create!(name: "child #{index} - level #{current_level}")
11
- create_tree(max_level, current_level + 1, child)
12
- end
13
-
14
- node
15
- end
5
+ RSpec.describe Node do
6
+ include TreeMethods
16
7
 
17
8
  before do
18
9
  @root = create_tree(3)
@@ -2,57 +2,68 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe 'Relation' do
6
- def create_tree(max_level, current_level: 0, node: nil, stop_at: nil)
7
- node = Node.create!(name: 'root') if node.nil?
8
-
9
- 1.upto(max_level - current_level) do |index|
10
- child = node.children.create!(name: "child #{index} - level #{current_level}", active: stop_at > current_level)
11
- child.create_node_info(status: stop_at > current_level ? 'foo' : 'bar')
12
- create_tree(max_level, current_level: current_level + 1, node: child, stop_at: stop_at)
13
- end
5
+ RSpec.describe 'Relation' do
6
+ include TreeMethods
14
7
 
15
- node
16
- end
8
+ let(:root) { create_tree(4, stop_at: 2) }
17
9
 
18
- before do
19
- @root = create_tree(4, stop_at: 2)
20
- @child = @root.children.first
21
- end
10
+ describe '#descendants' do
11
+ context 'with simple relation' do
12
+ let(:descendants) { root.descendants { |opts| opts.condition = Node.where(active: true) }.to_a }
22
13
 
23
- context 'descendants' do
24
- it 'works with simple relation' do
25
- desc = @root.descendants { |opts| opts.condition = Node.where(active: true) }
26
- desc.all.each do |node|
27
- expect(node.active).to be_truthy
14
+ it 'returns only active nodes' do
15
+ descendants.each do |node|
16
+ expect(node.active).to be_truthy
17
+ end
28
18
  end
29
19
  end
30
20
 
31
- it 'works with joins relation' do
32
- desc = @root.descendants { |opts| opts.condition = Node.joins(:node_info).where.not(node_infos: { status: 'bar' }) }
33
- desc.all.each do |node|
34
- expect(node.node_info.status).to eql('foo')
21
+ context 'with condition on joined association' do
22
+ let(:descendants) do
23
+ root.descendants do |opts|
24
+ opts.condition = Node.joins(:node_info).where.not(node_infos: { status: 'bar' })
25
+ end
26
+ end
27
+
28
+ it 'returns only node with condition fulfilled' do
29
+ descendants.each do |node|
30
+ expect(node.node_info.status).to eql('foo')
31
+ end
35
32
  end
36
33
  end
37
34
  end
38
35
 
39
- context 'ancestors' do
40
- it 'works with simple relation' do
41
- ancestors = @root.leaves.first.ancestors { |opts| opts.condition = Node.where(active: false) }.to_a
36
+ describe '#ancestors' do
37
+ context 'with simple_relation' do
38
+ let(:ancestors) { root.leaves.first.ancestors { |opts| opts.condition = Node.where(active: false) }.to_a }
42
39
 
43
- ancestors.each do |node|
44
- expect(node.active).to be_falsey
40
+ it 'return only active nodes' do
41
+ ancestors.each do |node|
42
+ expect(node.active).to be_falsey
43
+ end
45
44
  end
46
45
 
47
- expect(ancestors).not_to include(@root)
46
+ it 'does not return the root node' do
47
+ expect(ancestors).not_to include(root)
48
+ end
48
49
  end
49
50
 
50
- it 'works with joins relation' do
51
- ancestors = @root.leaves.first.ancestors { |opts| opts.condition = Node.joins(:node_info).where.not(node_infos: { status: 'foo' }) }
52
- ancestors.all.each do |node|
53
- expect(node.node_info.status).to eql('bar')
51
+ context 'with condition on joined association' do
52
+ let(:ancestors) do
53
+ root.leaves.first.ancestors do |opts|
54
+ opts.condition = Node.joins(:node_info).where.not(node_infos: { status: 'foo' })
55
+ end
56
+ end
57
+
58
+ it 'return only nodes for the matching condition' do
59
+ ancestors.each do |node|
60
+ expect(node.node_info.status).to eql('bar')
61
+ end
62
+ end
63
+
64
+ it 'does not return the root node' do
65
+ expect(ancestors).not_to include(root)
54
66
  end
55
- expect(ancestors).not_to include(@root)
56
67
  end
57
68
  end
58
69
  end
data/spec/spec_helper.rb CHANGED
@@ -11,6 +11,10 @@ require_relative 'db/database'
11
11
 
12
12
  require 'database_cleaner'
13
13
 
14
+ # Requires supporting ruby files with custom matchers and macros, etc,
15
+ # in spec/support/ and its subdirectories.
16
+ Dir[File.join(__dir__, 'support/**/*.rb')].sort.each { |f| require f }
17
+
14
18
  # This file was generated by the `rspec --init` command. Conventionally, all
15
19
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
16
20
  # The generated `.rspec` file contains `--require spec_helper` which will cause
@@ -72,7 +76,7 @@ RSpec.configure do |config|
72
76
  # # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
73
77
  # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
74
78
  # # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
75
- # config.disable_monkey_patching!
79
+ config.disable_monkey_patching!
76
80
  #
77
81
  # # This setting enables warnings. It's recommended, but in some cases may
78
82
  # # be too noisy due to issues in dependencies.
@@ -1,15 +1,12 @@
1
- # frozen_string_literal: true
2
1
 
3
- require 'spec_helper'
4
-
5
- shared_context 'setup with enforced ordering' do
2
+ RSpec.shared_context 'setup with enforced ordering' do
6
3
  let(:ordering) { false }
7
4
  include_context 'base_setup' do
8
5
  let(:proc) { ->(config) { config.ensure_ordering! } }
9
6
  end
10
7
  end
11
8
 
12
- shared_context 'base_setup' do
9
+ RSpec.shared_context 'base_setup' do
13
10
  subject(:query) { builder.build.to_sql }
14
11
 
15
12
  let(:model_id) { 1 }
@@ -21,7 +18,7 @@ shared_context 'base_setup' do
21
18
  end
22
19
  end
23
20
 
24
- shared_examples 'basic recursive examples' do
21
+ RSpec.shared_examples 'basic recursive examples' do
25
22
  it { is_expected.to start_with "SELECT \"#{model_class.table_name}\".* FROM \"#{model_class.table_name}\"" }
26
23
 
27
24
  it { is_expected.to match(/WHERE "#{model_class.table_name}"."#{model_class.primary_key}" = #{model_id}/) }
@@ -35,7 +32,7 @@ shared_examples 'basic recursive examples' do
35
32
  }
36
33
  end
37
34
 
38
- shared_examples 'build recursive query' do
35
+ RSpec.shared_examples 'build recursive query' do
39
36
  context 'simple id' do
40
37
  context 'with simple class' do
41
38
  include_context 'base_setup' do
@@ -67,79 +64,35 @@ shared_examples 'build recursive query' do
67
64
  end
68
65
  end
69
66
 
70
- shared_examples 'ancestor query' do
67
+ RSpec.shared_examples 'ancestor query' do
71
68
  include_context 'base_setup'
72
69
 
73
70
  it { is_expected.to match(/"#{builder.travers_loc_table.name}"."#{model_class._recursive_tree_config.parent_key}" = "#{model_class.table_name}"."#{model_class.primary_key}"/) }
74
71
  end
75
72
 
76
- shared_examples 'descendant query' do
73
+ RSpec.shared_examples 'descendant query' do
77
74
  include_context 'base_setup'
78
75
 
79
76
  it { is_expected.to match(/"#{model_class.table_name}"."#{model_class._recursive_tree_config.parent_key}" = "#{builder.travers_loc_table.name}"."#{model_class.primary_key}"/) }
80
77
  it { is_expected.to match(/#{Regexp.escape(builder.travers_loc_table.project(builder.travers_loc_table[model_class.primary_key]).to_sql)}/) }
81
78
  end
82
79
 
83
- shared_context 'context with ordering' do
80
+ RSpec.shared_context 'context with ordering' do
84
81
  include_context 'base_setup' do
85
82
  it_behaves_like 'with ordering'
86
83
  end
87
84
  end
88
85
 
89
- shared_context 'context without ordering' do
86
+ RSpec.shared_context 'context without ordering' do
90
87
  include_context 'base_setup' do
91
88
  it_behaves_like 'without ordering'
92
89
  end
93
90
  end
94
91
 
95
- shared_examples 'with ordering' do
92
+ RSpec.shared_examples 'with ordering' do
96
93
  it { is_expected.to match(/ORDER BY #{Regexp.escape(builder.recursive_temp_table[model_class._recursive_tree_config.depth_column].asc.to_sql)}/) }
97
94
  end
98
95
 
99
- shared_examples 'without ordering' do
96
+ RSpec.shared_examples 'without ordering' do
100
97
  it { is_expected.not_to match(/ORDER BY/) }
101
98
  end
102
-
103
- describe ActsAsRecursiveTree::Builders::Descendants do
104
- context 'basic' do
105
- it_behaves_like 'build recursive query'
106
- it_behaves_like 'descendant query'
107
- include_context 'context without ordering'
108
- end
109
-
110
- context 'with options' do
111
- include_context 'setup with enforced ordering' do
112
- let(:ordering) { true }
113
- it_behaves_like 'with ordering'
114
- end
115
- end
116
- end
117
-
118
- describe ActsAsRecursiveTree::Builders::Ancestors do
119
- context 'basic' do
120
- it_behaves_like 'build recursive query'
121
- it_behaves_like 'ancestor query'
122
- include_context 'context with ordering'
123
- end
124
-
125
- context 'with options' do
126
- include_context 'setup with enforced ordering' do
127
- it_behaves_like 'with ordering'
128
- end
129
- end
130
- end
131
-
132
- describe ActsAsRecursiveTree::Builders::Leaves do
133
- context 'basic' do
134
- it_behaves_like 'build recursive query'
135
- it_behaves_like 'descendant query'
136
- include_context 'context without ordering'
137
- end
138
-
139
- context 'with options' do
140
- include_context 'setup with enforced ordering' do
141
- let(:ordering) { true }
142
- it_behaves_like 'without ordering'
143
- end
144
- end
145
- end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helper methods for simple tree creation
4
+ module TreeMethods
5
+ def create_tree(max_level, current_level: 0, node: nil, create_node_info: false, stop_at: -1)
6
+ node = Node.create!(name: 'root') if node.nil?
7
+
8
+ 1.upto(max_level - current_level) do |index|
9
+ child = node.children.create!(
10
+ name: "child #{index} - level #{current_level}",
11
+ active: stop_at > current_level
12
+ )
13
+
14
+ child.create_node_info(status: stop_at > current_level ? 'foo' : 'bar') if create_node_info
15
+
16
+ create_tree(
17
+ max_level,
18
+ current_level: current_level + 1,
19
+ node: child,
20
+ create_node_info: create_node_info,
21
+ stop_at: stop_at
22
+ )
23
+ end
24
+
25
+ node
26
+ end
27
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_recursive_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wolfgang Wedelich-John
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-12-20 00:00:00.000000000 Z
12
+ date: 2023-01-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -85,14 +85,14 @@ dependencies:
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '1.5'
88
+ version: '2.0'
89
89
  type: :development
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '1.5'
95
+ version: '2.0'
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: rake
98
98
  requirement: !ruby/object:Gem::Requirement
@@ -205,6 +205,7 @@ files:
205
205
  - gemfiles/ar_60.gemfile
206
206
  - gemfiles/ar_61.gemfile
207
207
  - gemfiles/ar_70.gemfile
208
+ - gemfiles/ar_next.gemfile
208
209
  - lib/acts_as_recursive_tree.rb
209
210
  - lib/acts_as_recursive_tree/acts_macro.rb
210
211
  - lib/acts_as_recursive_tree/associations.rb
@@ -222,10 +223,15 @@ files:
222
223
  - lib/acts_as_recursive_tree/options/depth_condition.rb
223
224
  - lib/acts_as_recursive_tree/options/query_options.rb
224
225
  - lib/acts_as_recursive_tree/options/values.rb
226
+ - lib/acts_as_recursive_tree/preloaders/descendants.rb
225
227
  - lib/acts_as_recursive_tree/railtie.rb
226
228
  - lib/acts_as_recursive_tree/scopes.rb
227
229
  - lib/acts_as_recursive_tree/version.rb
228
- - spec/builders_spec.rb
230
+ - spec/acts_as_recursive_tree/builders/ancestors_spec.rb
231
+ - spec/acts_as_recursive_tree/builders/descendants_spec.rb
232
+ - spec/acts_as_recursive_tree/builders/leaves_spec.rb
233
+ - spec/acts_as_recursive_tree/options/values_spec.rb
234
+ - spec/acts_as_recursive_tree/preloaders/descendants_spec.rb
229
235
  - spec/db/database.rb
230
236
  - spec/db/database.yml
231
237
  - spec/db/models.rb
@@ -234,7 +240,8 @@ files:
234
240
  - spec/model/node_spec.rb
235
241
  - spec/model/relation_spec.rb
236
242
  - spec/spec_helper.rb
237
- - spec/values_spec.rb
243
+ - spec/support/shared_examples/builders.rb
244
+ - spec/support/tree_methods.rb
238
245
  homepage: https://github.com/1and1/acts_as_recursive_tree
239
246
  licenses:
240
247
  - MIT
@@ -261,7 +268,11 @@ signing_key:
261
268
  specification_version: 4
262
269
  summary: Drop in replacement for acts_as_tree but using recursive queries
263
270
  test_files:
264
- - spec/builders_spec.rb
271
+ - spec/acts_as_recursive_tree/builders/ancestors_spec.rb
272
+ - spec/acts_as_recursive_tree/builders/descendants_spec.rb
273
+ - spec/acts_as_recursive_tree/builders/leaves_spec.rb
274
+ - spec/acts_as_recursive_tree/options/values_spec.rb
275
+ - spec/acts_as_recursive_tree/preloaders/descendants_spec.rb
265
276
  - spec/db/database.rb
266
277
  - spec/db/database.yml
267
278
  - spec/db/models.rb
@@ -270,4 +281,5 @@ test_files:
270
281
  - spec/model/node_spec.rb
271
282
  - spec/model/relation_spec.rb
272
283
  - spec/spec_helper.rb
273
- - spec/values_spec.rb
284
+ - spec/support/shared_examples/builders.rb
285
+ - spec/support/tree_methods.rb