hierarchy-tree 0.1.1 → 0.3.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 +4 -4
- data/lib/hierarchy_tree.rb +88 -11
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cab69803c942b2cda581356ae1c3701febf660db154041a799d0c2dd22eefbfa
|
4
|
+
data.tar.gz: de33c12ba0a884a268d0730a3fce604591b9d62ff5b07e994e583b9e8524316a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eecf514e48906d8fcc98e5c80896e56b0d2cf1d2f60d207ce644dc72b131c35fe0d6329d3f786cb2ed35df8f113bf9c11d6b3083835a39e7600c22adc7d3c631
|
7
|
+
data.tar.gz: 9dacd1c66e4bb7355eeee8bf51341bcfa3d3a6cea7d76b6bce0a6057614e48a6bf8751ba064da6ec54b5f84cb1a41063e3c7904c4f8fbf12cec4db86905e98cf
|
data/lib/hierarchy_tree.rb
CHANGED
@@ -1,17 +1,73 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'active_support/core_ext/object/inclusion.rb'
|
3
3
|
|
4
|
+
################ Debug ################
|
5
|
+
# gem uninstall hierarchy-tree -v 0.3.0
|
6
|
+
# rm hierarchy-tree-0.3.0.gem
|
7
|
+
# gem build hierarchy_tree
|
8
|
+
# gem install hierarchy-tree-0.3.0.gem
|
9
|
+
# ruby -Itest test/test_hierarchy_tree.rb
|
10
|
+
|
4
11
|
class Hierarchy
|
5
|
-
# Return the full hierarchy starting from the provided class
|
12
|
+
# Return the full hierarchy as associations starting from the provided class
|
6
13
|
def self.associations(klass)
|
7
|
-
build_hierarchy(klass)
|
14
|
+
build_hierarchy(class: klass)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return the full hierarchy as classes starting from the provided class
|
18
|
+
def self.classes(klass)
|
19
|
+
build_hierarchy(class: klass, classes?: true)
|
8
20
|
end
|
9
21
|
|
10
|
-
# Return
|
11
|
-
def self.
|
12
|
-
@
|
22
|
+
# Return the array of children classes
|
23
|
+
def self.classes_list(klass)
|
24
|
+
@classes_list = []
|
13
25
|
build_descendants(klass)
|
14
|
-
@
|
26
|
+
@classes_list
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return the ancestors associations by navigating through :belongs_to
|
30
|
+
# Starting from the "from" class towards the "to" class
|
31
|
+
# Using DFS - Depth First Search, thus finding the Deepest Path (more likely)
|
32
|
+
def self.ancestors_dfs(from:, to:, descendants: [])
|
33
|
+
return if from.to_s == to.to_s and descendants == [] # Base case
|
34
|
+
return 'loop' if from.in? descendants # Avoids cycle
|
35
|
+
|
36
|
+
descendants.push(from)
|
37
|
+
|
38
|
+
from.reflect_on_all_associations(:belongs_to).map do |relation|
|
39
|
+
return relation.name if relation.klass.to_s == to.to_s # Path is found
|
40
|
+
path = ancestors_dfs(from: relation.klass, to: to, descendants: descendants)
|
41
|
+
return { relation.name => path } if valid_path?(path, to.model_name.param_key.to_sym)
|
42
|
+
end.compact.first
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the ancestors associations by navigating through :belongs_to
|
46
|
+
# Starting from the "from" class towards the "to" class
|
47
|
+
# Using BFS - Breadth First Search, thus finding the Shortest Path
|
48
|
+
def self.ancestors_bfs(from:, to:)
|
49
|
+
return if from == to
|
50
|
+
|
51
|
+
queue = [{ class: from, path: [] }]
|
52
|
+
visited = [from]
|
53
|
+
|
54
|
+
while queue.any?
|
55
|
+
current = queue.shift
|
56
|
+
current_class = current[:class]
|
57
|
+
current_path = current[:path]
|
58
|
+
|
59
|
+
current_class.reflect_on_all_associations(:belongs_to).each do |relation|
|
60
|
+
next_class = relation.klass
|
61
|
+
next_path = current_path + [relation.name]
|
62
|
+
|
63
|
+
return hashify(next_path) if next_class.to_s == to.to_s
|
64
|
+
|
65
|
+
if visited.exclude?(next_class)
|
66
|
+
visited << next_class
|
67
|
+
queue.push({ class: next_class, path: next_path })
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
15
71
|
end
|
16
72
|
|
17
73
|
def self.loop?(klass)
|
@@ -23,9 +79,9 @@ class Hierarchy
|
|
23
79
|
|
24
80
|
private_class_method
|
25
81
|
|
26
|
-
def self.build_hierarchy(
|
82
|
+
def self.build_hierarchy(opts)
|
27
83
|
@cache = {}
|
28
|
-
dfs_hierarchy(
|
84
|
+
dfs_hierarchy(opts)
|
29
85
|
rescue SystemStackError
|
30
86
|
Rails.logger.ap "Infinite loop detected and handled for #{opts[:class]} hierarchy", :warn
|
31
87
|
[]
|
@@ -81,17 +137,38 @@ class Hierarchy
|
|
81
137
|
def self.build_descendants(klass)
|
82
138
|
dfs_descendants(class: klass, classes?: true)
|
83
139
|
rescue SystemStackError
|
84
|
-
Rails.logger.ap "Infinite loop detected and handled for #{opts[:class]}
|
140
|
+
Rails.logger.ap "Infinite loop detected and handled for #{opts[:class]} classes_list", :warn
|
85
141
|
[]
|
86
142
|
end
|
87
143
|
|
88
144
|
def self.dfs_descendants(opts, klass_name = nil)
|
89
|
-
return if klass_name.in? @
|
90
|
-
@
|
145
|
+
return if klass_name.in? @classes_list
|
146
|
+
@classes_list.push(klass_name) if klass_name.present?
|
91
147
|
children_classes(opts).each do |child_klass, child_name|
|
92
148
|
child_opts = { class: child_klass, classes?: opts[:classes?] }
|
93
149
|
dfs_descendants(child_opts, child_name)
|
94
150
|
end
|
95
151
|
true
|
96
152
|
end
|
153
|
+
|
154
|
+
def self.valid_path?(path, target)
|
155
|
+
return true if path == target
|
156
|
+
|
157
|
+
case path
|
158
|
+
when Array
|
159
|
+
path.any? { |sub_path| valid_path?(sub_path, target) }
|
160
|
+
when Hash
|
161
|
+
path.values.any? { |value| valid_path?(value, target) }
|
162
|
+
else
|
163
|
+
false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.hashify(array)
|
168
|
+
if array.length == 1
|
169
|
+
array.first
|
170
|
+
else
|
171
|
+
{ array.first => hashify(array.drop(1)) }
|
172
|
+
end
|
173
|
+
end
|
97
174
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hierarchy-tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Cordeiro Costa
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -66,7 +66,7 @@ homepage: https://github.com/Victorcorcos/hierarchy-tree
|
|
66
66
|
licenses:
|
67
67
|
- MIT
|
68
68
|
metadata: {}
|
69
|
-
post_install_message:
|
69
|
+
post_install_message:
|
70
70
|
rdoc_options: []
|
71
71
|
require_paths:
|
72
72
|
- lib
|
@@ -82,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
82
|
version: '0'
|
83
83
|
requirements: []
|
84
84
|
rubygems_version: 3.1.6
|
85
|
-
signing_key:
|
85
|
+
signing_key:
|
86
86
|
specification_version: 4
|
87
87
|
summary: hierarchy-tree is a gem that shows the whole hierarchy and the associations
|
88
88
|
related to a desired class.
|