Skip to content

Commit 919daa7

Browse files
committed
Add option to set :dependent option
1 parent 38896c4 commit 919daa7

File tree

11 files changed

+61
-49
lines changed

11 files changed

+61
-49
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### NEXT
2+
- Added :dependent option for setting explicit
3+
14
### Version 3.4.0
25
- Rails 7.1 compatibility
36
- Added ar_next to test matrix

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,26 @@ class Node < ActiveRecord::Base
5555
recursive_tree
5656
end
5757
```
58-
That's it. This will assume that your model has a column named `parent_id` which will be used for traversal. If your column is something different, then you can specifiy it in the call to `recursive_tree`:
58+
That's it. This will assume that your model has a column named `parent_id` which will be used for traversal. If your column is something different, then you can specify it in the call to `recursive_tree`:
5959

6060
```ruby
6161
recursive_tree parent_key: :some_other_column
6262
```
6363

64-
Some extra special stuff - if your parent relation is also polymorphic, the specify the polymorphic column:
64+
Some extra special stuff - if your parent relation is also polymorphic, then specify the polymorphic column:
6565

6666
```ruby
6767
recursive_tree parent_type_column: :some_other_type_column
6868
```
6969

70+
Controlling deletion behaviour:
71+
72+
By default, it is up to the user code to delete all child nodes in a tree when a parent node gets deleted. This can be controlled by the `:dependent` option, which will be set on the `children` association (see [#has_many](https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many) in the Rails doc).
73+
74+
```ruby
75+
recursive_tree dependent: :nullify # or :destroy, etc.
76+
```
77+
7078
## Usage
7179

7280
After you set up a model for usage, there are now several methods you can use.

lib/acts_as_recursive_tree/acts_macro.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ module ActsMacro
77
#
88
# * <tt>foreign_key</tt> - specifies the column name to use for tracking
99
# of the tree (default: +parent_id+)
10-
def recursive_tree(parent_key: :parent_id, parent_type_column: nil)
10+
def recursive_tree(parent_key: :parent_id, parent_type_column: nil, dependent: nil)
1111
class_attribute(:_recursive_tree_config, instance_writer: false)
1212

1313
self._recursive_tree_config = Config.new(
1414
model_class: self,
1515
parent_key: parent_key.to_sym,
16-
parent_type_column: parent_type_column.try(:to_sym)
16+
parent_type_column: parent_type_column.try(:to_sym),
17+
dependent: dependent
1718
)
1819

1920
include ActsAsRecursiveTree::Model

lib/acts_as_recursive_tree/associations.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ module Associations
1616
has_many :children,
1717
class_name: base_class.to_s,
1818
foreign_key: _recursive_tree_config.parent_key,
19-
inverse_of: :parent
19+
inverse_of: :parent,
20+
dependent: _recursive_tree_config.dependent
2021

2122
has_many :self_and_siblings,
2223
through: :parent,

lib/acts_as_recursive_tree/config.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ module ActsAsRecursiveTree
55
# Stores the configuration of one Model class
66
#
77
class Config
8-
attr_reader :parent_key, :parent_type_column, :depth_column
8+
attr_reader :parent_key, :parent_type_column, :depth_column, :dependent
99

10-
def initialize(model_class:, parent_key:, parent_type_column:, depth_column: :recursive_depth)
10+
def initialize(model_class:, parent_key:, parent_type_column:, depth_column: :recursive_depth, dependent: nil)
1111
@model_class = model_class
1212
@parent_key = parent_key
1313
@parent_type_column = parent_type_column
1414
@depth_column = depth_column
15+
@dependent = dependent
1516
end
1617

1718
#

spec/acts_as_recursive_tree/builders/ancestors_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
require 'spec_helper'
44

55
RSpec.describe ActsAsRecursiveTree::Builders::Ancestors do
6-
context 'basic' do
6+
context 'without additional setup' do
77
it_behaves_like 'build recursive query'
88
it_behaves_like 'ancestor query'
9-
include_context 'context with ordering'
9+
include_context 'with ordering'
1010
end
1111

1212
context 'with options' do
13-
include_context 'setup with enforced ordering' do
14-
it_behaves_like 'with ordering'
13+
include_context 'with enforced ordering setup' do
14+
it_behaves_like 'is adding ordering'
1515
end
1616
end
1717
end

spec/acts_as_recursive_tree/builders/descendants_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
require 'spec_helper'
44

55
RSpec.describe ActsAsRecursiveTree::Builders::Descendants do
6-
context 'basic' do
6+
context 'without additional setup' do
77
it_behaves_like 'build recursive query'
88
it_behaves_like 'descendant query'
9-
include_context 'context without ordering'
9+
include_context 'without ordering'
1010
end
1111

1212
context 'with options' do
13-
include_context 'setup with enforced ordering' do
13+
include_context 'with enforced ordering setup' do
1414
let(:ordering) { true }
15-
it_behaves_like 'with ordering'
15+
it_behaves_like 'is adding ordering'
1616
end
1717
end
1818
end

spec/acts_as_recursive_tree/builders/leaves_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
require 'spec_helper'
44

55
RSpec.describe ActsAsRecursiveTree::Builders::Leaves do
6-
context 'basic' do
6+
context 'without additional setup' do
77
it_behaves_like 'build recursive query'
88
it_behaves_like 'descendant query'
9-
include_context 'context without ordering'
9+
include_context 'without ordering'
1010
end
1111

1212
context 'with options' do
13-
include_context 'setup with enforced ordering' do
13+
include_context 'with enforced ordering setup' do
1414
let(:ordering) { true }
15-
it_behaves_like 'without ordering'
15+
it_behaves_like 'not adding ordering'
1616
end
1717
end
1818
end

spec/acts_as_recursive_tree/options/values_spec.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
let(:table) { Arel::Table.new('test_table') }
2121
let(:attribute) { table['test_attr'] }
2222

23-
context 'invalid agurment' do
23+
context 'with invalid agurment' do
2424
it 'raises exception' do
2525
expect { described_class.create(nil) }.to raise_exception(/is not supported/)
2626
end
2727
end
2828

29-
context 'single value' do
29+
context 'with single value' do
3030
let(:single_value) { 3 }
3131

3232
it_behaves_like 'single values' do
@@ -38,8 +38,8 @@
3838
end
3939
end
4040

41-
context 'multi value' do
42-
context 'Array' do
41+
context 'with multi value' do
42+
context 'with Array' do
4343
subject(:value) { described_class.create(array) }
4444

4545
let(:array) { [1, 2, 3] }
@@ -55,7 +55,7 @@
5555
end
5656
end
5757

58-
context 'Range' do
58+
context 'with Range' do
5959
subject(:value) { described_class.create(range) }
6060

6161
let(:range) { 1..3 }
@@ -71,7 +71,7 @@
7171
end
7272
end
7373

74-
context 'Relation' do
74+
context 'with Relation' do
7575
subject(:value) { described_class.create(relation, double) }
7676

7777
let(:relation) { Node.where(name: 'test') }

spec/support/shared_examples/builders.rb

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
RSpec.shared_context 'setup with enforced ordering' do
1+
# frozen_string_literal: true
2+
3+
RSpec.shared_context 'with enforced ordering setup' do
24
let(:ordering) { false }
3-
include_context 'base_setup' do
5+
include_context 'with base_setup' do
46
let(:proc) { ->(config) { config.ensure_ordering! } }
57
end
68
end
79

8-
RSpec.shared_context 'base_setup' do
10+
RSpec.shared_context 'with base_setup' do
911
subject(:query) { builder.build.to_sql }
1012

1113
let(:model_id) { 1 }
@@ -32,30 +34,30 @@
3234
end
3335

3436
RSpec.shared_examples 'build recursive query' do
35-
context 'simple id' do
37+
context 'with simple id' do
3638
context 'with simple class' do
37-
include_context 'base_setup' do
39+
include_context 'with base_setup' do
3840
let(:model_class) { Node }
3941
it_behaves_like 'basic recursive examples'
4042
end
4143
end
4244

4345
context 'with class with different parent key' do
44-
include_context 'base_setup' do
46+
include_context 'with base_setup' do
4547
let(:model_class) { NodeWithOtherParentKey }
4648
it_behaves_like 'basic recursive examples'
4749
end
4850
end
4951

5052
context 'with Subclass' do
51-
include_context 'base_setup' do
53+
include_context 'with base_setup' do
5254
let(:model_class) { Floor }
5355
it_behaves_like 'basic recursive examples'
5456
end
5557
end
5658

5759
context 'with polymorphic parent relation' do
58-
include_context 'base_setup' do
60+
include_context 'with base_setup' do
5961
let(:model_class) { NodeWithPolymorphicParent }
6062
it_behaves_like 'basic recursive examples'
6163
end
@@ -64,34 +66,34 @@
6466
end
6567

6668
RSpec.shared_examples 'ancestor query' do
67-
include_context 'base_setup'
69+
include_context 'with base_setup'
6870

6971
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}"/) }
7072
end
7173

7274
RSpec.shared_examples 'descendant query' do
73-
include_context 'base_setup'
75+
include_context 'with base_setup'
7476

7577
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}"/) }
7678
it { is_expected.to match(/#{Regexp.escape(builder.travers_loc_table.project(builder.travers_loc_table[model_class.primary_key]).to_sql)}/) }
7779
end
7880

79-
RSpec.shared_context 'context with ordering' do
80-
include_context 'base_setup' do
81-
it_behaves_like 'with ordering'
81+
RSpec.shared_context 'with ordering' do
82+
include_context 'with base_setup' do
83+
it_behaves_like 'is adding ordering'
8284
end
8385
end
8486

85-
RSpec.shared_context 'context without ordering' do
86-
include_context 'base_setup' do
87-
it_behaves_like 'without ordering'
87+
RSpec.shared_context 'without ordering' do
88+
include_context 'with base_setup' do
89+
it_behaves_like 'not adding ordering'
8890
end
8991
end
9092

91-
RSpec.shared_examples 'with ordering' do
93+
RSpec.shared_examples 'is adding ordering' do
9294
it { is_expected.to match(/ORDER BY #{Regexp.escape(builder.recursive_temp_table[model_class._recursive_tree_config.depth_column].asc.to_sql)}/) }
9395
end
9496

95-
RSpec.shared_examples 'without ordering' do
97+
RSpec.shared_examples 'not adding ordering' do
9698
it { is_expected.not_to match(/ORDER BY/) }
9799
end

spec/support/tree_methods.rb

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,10 @@
33
# Helper methods for simple tree creation
44
module TreeMethods
55
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?
6+
node ||= Node.create!(name: 'root')
77

88
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-
)
9+
child = node.children.create!(name: "child #{index} - level #{current_level}", active: stop_at > current_level)
1310

1411
child.create_node_info(status: stop_at > current_level ? 'foo' : 'bar') if create_node_info
1512

@@ -21,7 +18,6 @@ def create_tree(max_level, current_level: 0, node: nil, create_node_info: false,
2118
stop_at: stop_at
2219
)
2320
end
24-
2521
node
2622
end
2723
end

0 commit comments

Comments
 (0)