File tree Expand file tree Collapse file tree 10 files changed +101
-13
lines changed
lib/acts_as_recursive_tree
acts_as_recursive_tree/preloaders Expand file tree Collapse file tree 10 files changed +101
-13
lines changed Original file line number Diff line number Diff line change
1
+ ### Version 3.2.0
2
+ - Added #preload_tree method to preload the parent/child relations of a single node
3
+
1
4
### Version 3.1.0
2
5
- Rails 7 support
3
6
Original file line number Diff line number Diff line change @@ -134,7 +134,7 @@ __Additional methods:__
134
134
__ Utility methods:__
135
135
* ` root? ` - returns true if this node is a root node
136
136
* ` leaf? ` - returns true if this node is a leave node
137
-
137
+ * ` preload_tree ` - fetches all descendants of this node and assignes the proper parent/children associations. You are then able to traverse the tree through the children/parent association without querying the database again.
138
138
139
139
## Customizing the recursion
140
140
Original file line number Diff line number Diff line change @@ -8,7 +8,8 @@ module ActsMacro
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 ,
Original file line number Diff line number Diff line change @@ -91,6 +91,14 @@ def leaf?
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
+ def preload_tree
98
+ ActsAsRecursiveTree ::Preloaders ::Descendants . new ( self ) . preload!
99
+ true
100
+ end
101
+
94
102
def base_class
95
103
self . class . base_class
96
104
end
Original file line number Diff line number Diff line change
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 )
11
+ @node = node
12
+ @parent_key = node . _recursive_tree_config . parent_key
13
+ end
14
+
15
+ def preload!
16
+ apply_records ( @node )
17
+ end
18
+
19
+ private
20
+
21
+ def records
22
+ @records ||= @node . descendants . to_a
23
+ end
24
+
25
+ def apply_records ( parent_node )
26
+ children = records . select { |child | child . send ( @parent_key ) == parent_node . id }
27
+
28
+ parent_node . association ( :children ) . target = children
29
+
30
+ children . each do |child |
31
+ child . association ( :parent ) . target = parent_node
32
+ apply_records ( child )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
Original file line number Diff line number Diff line change 1
1
# frozen_string_literal: true
2
2
3
3
module ActsAsRecursiveTree
4
- VERSION = '3.1 .0'
4
+ VERSION = '3.2 .0'
5
5
end
Original file line number Diff line number Diff line change
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 ) }
9
+ let ( :root ) { create_tree ( 2 ) }
10
+ let ( :children ) { root . children }
11
+
12
+ describe '#preload! will set the associations target attribute' do
13
+ before do
14
+ preloader . preload!
15
+ end
16
+
17
+ it 'sets the children assoction' do
18
+ children . each do |child |
19
+ expect ( child . association ( :children ) . target ) . not_to be_nil
20
+ end
21
+ end
22
+
23
+ it 'sets the parent assoction' do
24
+ children . each do |child |
25
+ expect ( child . association ( :parent ) . target ) . not_to be_nil
26
+ end
27
+ end
28
+ end
29
+ end
Original file line number Diff line number Diff line change 3
3
require 'spec_helper'
4
4
5
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
6
+ include TreeMethods
16
7
17
8
before do
18
9
@root = create_tree ( 3 )
Original file line number Diff line number Diff line change 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
Original file line number Diff line number Diff line change
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 )
6
+ node = Node . create! ( name : 'root' ) if node . nil?
7
+
8
+ 1 . upto ( max_level - current_level ) do |index |
9
+ child = node . children . create! ( name : "child #{ index } - level #{ current_level } " )
10
+ create_tree ( max_level , current_level + 1 , child )
11
+ end
12
+
13
+ node
14
+ end
15
+ end
You can’t perform that action at this time.
0 commit comments