hierarchy-tree 0.2.0 → 0.3.1

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hierarchy_tree.rb +101 -5
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c2620a107d595da64c5854efec9c90f4ebada54e79ea851e35cd698dd31cb7e
4
- data.tar.gz: 3957eb61b1debe9dcff8049d89947a60b594b348f3e65b31f25615d4bb56a8de
3
+ metadata.gz: 6644c370fbd994c645135ec56cd7aa19c6586cf7da38136e929b1bac2edd0ddf
4
+ data.tar.gz: 938f28a4313baa9ba0dabfdbfed06dd32022fbf79177b2bbdf537233593d15ee
5
5
  SHA512:
6
- metadata.gz: cfe0cdf719a378c501ac89db2df73349f9af3b131c39947e0ad3b9126032ca2898997fad85f4ae532b30fa11062e84fbc339c5a7d6e2cd7f092d486426a84040
7
- data.tar.gz: 0b3677fe95dd795eb1ba93671e9878df59e712d4378c0a638cb62765f7bc49fef2e8ab8e91133934723afb2b91149a6984b25353d383911d7d516bd6539dd9bf
6
+ metadata.gz: 4e3f81dad78bfb864c73153cdebfb2ad3978f1caa70ad2996c2125ce7d88fd2ecf4a362e6c2920877f7e23cfe129f51055f0f945e647392c8f66d69ac3b5c67e
7
+ data.tar.gz: 790d3534a9798da8fea30a5d44252e57f6d5f00f3fbdac7700502a3c2860a2427e9b97594a926c32cab800eaa3b2d86df8107ab5d675fcb48bd3274ed330bf4c
@@ -2,29 +2,104 @@ require 'active_record'
2
2
  require 'active_support/core_ext/object/inclusion.rb'
3
3
 
4
4
  ################ Debug ################
5
- # rm hierarchy-tree-X.Y.Z.gem
5
+ # gem cleanup hierarchy-tree
6
+ # rm hierarchy-tree-0.3.1.gem
6
7
  # gem build hierarchy_tree
7
- # gem install hierarchy-tree-X.Y.Z.gem
8
+ # gem install hierarchy-tree-0.3.1.gem
8
9
  # ruby -Itest test/test_hierarchy_tree.rb
9
10
 
10
11
  class Hierarchy
11
- # Return the full hierarchy starting from the provided class
12
+ # Return the full hierarchy as associations starting from the provided class
12
13
  def self.associations(klass)
13
14
  build_hierarchy(class: klass)
14
15
  end
15
16
 
16
- # Return the full hierarchy starting from the provided class
17
+ # Return the full hierarchy as classes starting from the provided class
17
18
  def self.classes(klass)
18
19
  build_hierarchy(class: klass, classes?: true)
19
20
  end
20
21
 
21
- # Return an array o
22
+ # Return the array of children classes
22
23
  def self.classes_list(klass)
23
24
  @classes_list = []
24
25
  build_descendants(klass)
25
26
  @classes_list
26
27
  end
27
28
 
29
+ # Return all the possible ancestors associations by navigating through :belongs_to
30
+ # Starting from the "from" class towards the "to" class
31
+ def self.ancestors(from:, to:)
32
+ return [] if from.to_s == to.to_s
33
+
34
+ queue = [{ class: from.to_s, path: [] }]
35
+ visited = { from.to_s => [] }
36
+ paths = []
37
+
38
+ while queue.any?
39
+ current = queue.shift
40
+ current_class = current[:class]
41
+ current_path = current[:path]
42
+
43
+ current_class.constantize.reflect_on_all_associations(:belongs_to).each do |relation|
44
+ next_class = relation.klass.to_s
45
+ next_path = current_path + [relation.name]
46
+
47
+ paths << hashify(next_path) if next_class == to.to_s
48
+
49
+ if next_path == next_path.uniq # Non-looped path
50
+ visited[next_class] = next_path
51
+ queue.push({ class: next_class, path: next_path })
52
+ end
53
+ end
54
+ end
55
+
56
+ paths
57
+ end
58
+
59
+ # Return the ancestors associations by navigating through :belongs_to
60
+ # Starting from the "from" class towards the "to" class
61
+ # Using BFS - Breadth First Search, thus finding the Shortest Path
62
+ def self.ancestors_bfs(from:, to:)
63
+ return if from == to
64
+
65
+ queue = [{ class: from, path: [] }]
66
+ visited = [from]
67
+
68
+ while queue.any?
69
+ current = queue.shift
70
+ current_class = current[:class]
71
+ current_path = current[:path]
72
+
73
+ current_class.reflect_on_all_associations(:belongs_to).each do |relation|
74
+ next_class = relation.klass
75
+ next_path = current_path + [relation.name]
76
+
77
+ return hashify(next_path) if next_class.to_s == to.to_s
78
+
79
+ if visited.exclude?(next_class)
80
+ visited << next_class
81
+ queue.push({ class: next_class, path: next_path })
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # Return the ancestors associations by navigating through :belongs_to
88
+ # Starting from the "from" class towards the "to" class
89
+ # Using DFS - Depth First Search, thus finding the Deepest Path (more likely)
90
+ def self.ancestors_dfs(from:, to:, descendants: [])
91
+ return if from.to_s == to.to_s and descendants == [] # Base case
92
+ return 'loop' if from.in? descendants # Avoids cycle
93
+
94
+ descendants.push(from)
95
+
96
+ from.reflect_on_all_associations(:belongs_to).map do |relation|
97
+ return relation.name if relation.klass.to_s == to.to_s # Path is found
98
+ path = ancestors_dfs(from: relation.klass, to: to, descendants: descendants)
99
+ return { relation.name => path } if valid_path?(path, to.model_name.param_key.to_sym)
100
+ end.compact.first
101
+ end
102
+
28
103
  def self.loop?(klass)
29
104
  @cache = {}
30
105
  false if dfs_hierarchy(class: klass, classes?: false)
@@ -105,4 +180,25 @@ class Hierarchy
105
180
  end
106
181
  true
107
182
  end
183
+
184
+ def self.hashify(array)
185
+ if array.length == 1
186
+ array.first
187
+ else
188
+ { array.first => hashify(array.drop(1)) }
189
+ end
190
+ end
191
+
192
+ def self.valid_path?(path, target)
193
+ return true if path == target
194
+
195
+ case path
196
+ when Array
197
+ path.any? { |sub_path| valid_path?(sub_path, target) }
198
+ when Hash
199
+ path.values.any? { |value| valid_path?(value, target) }
200
+ else
201
+ false
202
+ end
203
+ end
108
204
  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.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Cordeiro Costa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-13 00:00:00.000000000 Z
11
+ date: 2023-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest