hierarchy-tree 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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