fast_tree 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef95899f36c494fb5bc3cea8317205591f61dae7
4
- data.tar.gz: f84331649e1fbf73db8853f7629ef8b7144f69a4
3
+ metadata.gz: 1a2408d73a49d03de109154c5bc8897bfcbeadaf
4
+ data.tar.gz: 48bda26709f050ccc1b56ac535220aaf89169696
5
5
  SHA512:
6
- metadata.gz: 7a5137010f0f12bf4e6c03543814b542d218c61fecc8aee0e886e7fc7c66a6c39d2f51b1bcd9e843ef50ed99f07333e48002859ea5412dcb8d2b2e6d6ef94f9c
7
- data.tar.gz: 8a6291deac4b760f876f50f24a2fec8c65262a1bf20b60c3a67041d60d4e6cdd4e13f3b789f71782c33a2e9c16f55782aacfde2534d60a5e8e5508082d9b66fd
6
+ metadata.gz: 59bd83c3ee0bb38e65dfe8ce9152619a18c65b06eddedfb65b765581e7afd354fc4687f82d25d86058c4d12894e20ad278c8eaa48802eafd4d2e4f30c78ee74f
7
+ data.tar.gz: c89f948f54b279fc8a857764995353abac0bf56e0bbb38e1ba6ef5afcb6e952757df66db150a1ce5c92211a813f2cd77feecb115d7036100c4baf7e71a210796
data/README.md CHANGED
@@ -24,18 +24,30 @@ $ gem install fast_tree
24
24
  ## Usage
25
25
 
26
26
  `fast_tree` provides a generator which adds left and right pointers used in nested sets model to your model class.
27
- Even if you have created a class or not, execute following commands in the terminal:
27
+ Even if you have created a target class or not, execute following commands in your terminal:
28
28
 
29
29
  ```bash
30
30
  $ bin/rails g fast_tree YOUR_MODEL_NAME
31
31
  ```
32
32
 
33
- After executing the command, add following sentence into your model:
33
+ and, execute migration by `bundle exec rake db:migrate`.
34
+
35
+ After that, add the following line into your model:
34
36
 
35
37
  ```ruby
36
38
  include FastTree::Model
37
39
  ```
38
40
 
41
+ It seems like:
42
+
43
+ ```ruby
44
+ class YOUR_MODEL_NAME < ApplicationRecord
45
+ include FastTree::Model
46
+
47
+ ...
48
+ end
49
+ ```
50
+
39
51
  Finally, you can use several methods as class methods and instance methods.
40
52
 
41
53
  If you are interested in how it works, see the section "How It Works" below.
@@ -137,6 +149,63 @@ target = YOUR_MODEL_NAME.second
137
149
  root_of_subtree.move_to(targe)
138
150
  ```
139
151
 
152
+ ### Find root
153
+
154
+ To get the root node from the tree,
155
+
156
+ ```
157
+ root = YOUR_MODEL_NAME.find_root
158
+ ```
159
+
160
+ ### Deal with subtrees
161
+
162
+ To get subtree from a root node,
163
+
164
+ ```ruby
165
+ # root can be any node in the tree
166
+ root = YOUR_MODEL_NAME.take
167
+ root.subtree.each do |node|
168
+ # do something
169
+ end
170
+ ```
171
+
172
+ NOTE: `subtree` method returns ActiveRelation, so that you can use `each`, `map` or anything you want!
173
+
174
+ ### Tree traverse algorithms
175
+
176
+ In `fast_tree`, several tree-traverse algorithms, such as DFS and BFS, are provided.
177
+
178
+ #### DFS (Depth First Search)
179
+
180
+ To get nodes by DFS,
181
+
182
+ ```ruby
183
+ root = YOUR_MODEL_NAME.take
184
+ root.subtree.dfs.each do |node|
185
+ # do something
186
+ end
187
+ ```
188
+
189
+ #### BFS (Breadth First Search)
190
+
191
+ To get nodes by BFS,
192
+
193
+ ```ruby
194
+ root = YOUR_MODEL_NAME.take
195
+ root.subtree.bfs.each do |node|
196
+ # do something
197
+ end
198
+ ```
199
+
200
+ ### Boolean methods
201
+
202
+ `fast_tree` provides several boolean methods, such as:
203
+
204
+ - root?
205
+ - leaf?
206
+ - has_children?
207
+
208
+ , as instance methods.
140
209
 
141
210
  ## How It Works
142
211
  The migration file will create a migration file, such as:
@@ -148,11 +217,13 @@ class AddFastTreeToTestTrees < ActiveRecord::Migration[5.0]
148
217
  ## Pointers
149
218
  t.integer :l_ptr
150
219
  t.integer :r_ptr
220
+ t.integer :depth
151
221
 
152
222
  end
153
223
 
154
224
  add_index :test_trees, :l_ptr
155
225
  add_index :test_trees, :r_ptr
226
+ add_index :test_trees, :depth
156
227
  end
157
228
 
158
229
  def self.down
@@ -0,0 +1,20 @@
1
+ module FastTree
2
+ module Model
3
+ module Subtree
4
+ module Traverse
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ scope :bfs, -> { order(depth: :asc, l_ptr: :asc) }
9
+ scope :dfs, -> { order(l_ptr: :asc) }
10
+ end
11
+
12
+ def subtree
13
+ self.class.where(self.class.arel_table[:l_ptr].gteq(l_ptr))
14
+ .where(self.class.arel_table[:r_ptr].lteq(r_ptr))
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -2,6 +2,11 @@ module FastTree
2
2
  module Model
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ included do
6
+ include FastTree::Model::Subtree::Traverse
7
+ end
8
+
9
+
5
10
  # =================
6
11
  # Class Methods
7
12
  # =================
@@ -13,22 +18,28 @@ module FastTree
13
18
  # ====================
14
19
 
15
20
  def add_parent(parent, children, &block)
21
+ parent_depth = children.first.depth
22
+
16
23
  # create space for parent
17
24
  ptrs = _create_parent_embedded_space(children)
18
25
 
19
26
  # parent node's pointer
20
27
  parent.l_ptr = ptrs[:l_ptr]
21
28
  parent.r_ptr = ptrs[:r_ptr]
29
+ parent.depth = parent_depth
22
30
  parent.save
23
31
  end
24
32
 
25
33
  def create_parent(attributes = {}, children, &block)
34
+ parent_depth = children.first.depth
35
+
26
36
  # create space for parent
27
37
  ptrs = _create_parent_embedded_space(children)
28
38
 
29
39
  # parent node's pointer
30
40
  attributes[:l_ptr] = ptrs[:l_ptr]
31
41
  attributes[:r_ptr] = ptrs[:r_ptr]
42
+ attributes[:depth] = parent_depth
32
43
 
33
44
  self.create(attributes, &block)
34
45
  end
@@ -40,6 +51,7 @@ module FastTree
40
51
  else
41
52
  attributes[:l_ptr] = 0
42
53
  attributes[:r_ptr] = 1
54
+ attributes[:depth] = 0
43
55
  self.create(attributes, &block)
44
56
  end
45
57
  end
@@ -69,6 +81,12 @@ UPDATE #{self.to_s.underscore.pluralize}
69
81
  WHEN l_ptr > #{right}
70
82
  THEN r_ptr + 2
71
83
  ELSE r_ptr
84
+ END,
85
+ depth = CASE
86
+ WHEN l_ptr >= #{left}
87
+ AND r_ptr <= #{right}
88
+ THEN depth + 1
89
+ ELSE depth
72
90
  END
73
91
  WHERE r_ptr > #{left}
74
92
  EOS
@@ -134,6 +152,7 @@ UPDATE #{self.to_s.underscore.pluralize}
134
152
  # child node's pointer
135
153
  node.l_ptr = r_ptr
136
154
  node.r_ptr = r_ptr + 1
155
+ node.depth = depth + 1
137
156
  node.save
138
157
  end
139
158
 
@@ -144,6 +163,7 @@ UPDATE #{self.to_s.underscore.pluralize}
144
163
  # create child
145
164
  attributes[:l_ptr] = r_ptr
146
165
  attributes[:r_ptr] = r_ptr + 1
166
+ attributes[:depth] = depth + 1
147
167
  self.class.create(attributes, &block)
148
168
  end
149
169
 
@@ -154,19 +174,17 @@ UPDATE #{self.to_s.underscore.pluralize}
154
174
  _update_nodes(node.l_ptr, node.r_ptr, "r_ptr >= #{r_ptr}", width + 1)
155
175
 
156
176
  bias = node.l_ptr + 1 - l_ptr
177
+ base_depth = depth
157
178
  subtree.each do |st_node|
158
179
  attributes = st_node.attributes.to_h
159
180
  attributes.delete("id")
160
- attributes["l_ptr"] = attributes["l_ptr"] + bias
161
- attributes["r_ptr"] = attributes["r_ptr"] + bias
181
+ attributes["l_ptr"] += bias
182
+ attributes["r_ptr"] += bias
183
+ attributes["depth"] += node.depth - base_depth + 1
162
184
  self.class.create(attributes)
163
185
  end
164
186
  end
165
187
 
166
- def depth
167
- path.size - 1
168
- end
169
-
170
188
  def move_to(node)
171
189
  # NOTE:
172
190
  # copy_to and remove change node ids
@@ -184,9 +202,11 @@ UPDATE #{self.to_s.underscore.pluralize}
184
202
 
185
203
  # move subtree under the given node
186
204
  bias = node.l_ptr + 1 - l_ptr
205
+ base_depth = depth
187
206
  subtree.each do |st_node|
188
207
  st_node.l_ptr += bias
189
208
  st_node.r_ptr += bias
209
+ st_node.depth += node.depth - base_depth + 1
190
210
  st_node.save
191
211
  end
192
212
  end
@@ -217,6 +237,23 @@ UPDATE #{self.to_s.underscore.pluralize}
217
237
  end
218
238
 
219
239
 
240
+ #
241
+ # Boolean Methods
242
+ #
243
+
244
+ def root?
245
+ self.l_ptr == 0
246
+ end
247
+
248
+ def leaf?
249
+ l_ptr == r_ptr - 1
250
+ end
251
+
252
+ def has_children?
253
+ l_ptr != r_ptr - 1
254
+ end
255
+
256
+
220
257
  protected
221
258
 
222
259
  def _update_nodes(left, right, condition, diff = 2)
@@ -1,3 +1,3 @@
1
1
  module FastTree
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/fast_tree.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require 'rails'
2
2
 
3
- module FastTree
4
-
5
- # =====================================
6
- # Implementation of Fast Tree Structure
7
- # =====================================
8
3
 
4
+ module FastTree
9
5
  autoload :Model, 'fast_tree/model'
10
6
 
7
+ module Model
8
+ module Subtree
9
+ autoload :Traverse, 'fast_tree/model/subtree/traverse'
10
+ end
11
+ end
12
+
11
13
  end
@@ -26,6 +26,7 @@ module ActiveRecord
26
26
  ## Pointers
27
27
  t.integer :l_ptr
28
28
  t.integer :r_ptr
29
+ t.integer :depth
29
30
  RUBY
30
31
  end
31
32
 
@@ -12,5 +12,6 @@ class FastTreeCreate<%= table_name.camelize %> < ActiveRecord::Migration<%= migr
12
12
 
13
13
  add_index :<%= table_name %>, :l_ptr
14
14
  add_index :<%= table_name %>, :r_ptr
15
+ add_index :<%= table_name %>, :depth
15
16
  end
16
17
  end
@@ -10,6 +10,7 @@ class AddFastTreeTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migra
10
10
 
11
11
  add_index :<%= table_name %>, :l_ptr
12
12
  add_index :<%= table_name %>, :r_ptr
13
+ add_index :<%= table_name %>, :depth
13
14
  end
14
15
 
15
16
  def self.down
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fast_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chisato Hasegawa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-07 00:00:00.000000000 Z
11
+ date: 2017-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -64,6 +64,7 @@ files:
64
64
  - Rakefile
65
65
  - lib/fast_tree.rb
66
66
  - lib/fast_tree/model.rb
67
+ - lib/fast_tree/model/subtree/traverse.rb
67
68
  - lib/fast_tree/version.rb
68
69
  - lib/generators/active_record/fast_tree_generator.rb
69
70
  - lib/generators/active_record/templates/migration.rb
@@ -91,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
92
  version: '0'
92
93
  requirements: []
93
94
  rubyforge_project:
94
- rubygems_version: 2.5.1
95
+ rubygems_version: 2.5.2
95
96
  signing_key:
96
97
  specification_version: 4
97
98
  summary: '["Rails plugin for Fast Tree Structure", "fast_tree is an implementation