foobara-util 1.0.3 → 1.0.4

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
  SHA256:
3
- metadata.gz: 18c1181684054682ef921400e25c976d94c10f64dff2c036ebe9ba7b08cf09d5
4
- data.tar.gz: a91bb09d64d336e9c46bbfa20746da5abbedcb4fa4fa93f3ade09876ddaa6692
3
+ metadata.gz: dfa3f65a13306f37f57d39d0bcf7cbebc98cb9e580c6ee8b562744f5cb3091f9
4
+ data.tar.gz: 5e9e6cb4a398fbcd9386a79932e30a5d0d4f1c48cbf228cca0189b29c17c5d8b
5
5
  SHA512:
6
- metadata.gz: 5a387cf68464f15054d99112d0b89f64f2516581c207e0587f8dbe5ff5a788144e6798860ea36a942889dab2dbdd0e1fe1aea6657f12d0f3d883a9c06460a857
7
- data.tar.gz: fb8471223c146c78e749697001a5edf034c37ac0e3f28d8bdca07f1dcd565546d2bff9cbb5d552027d1d177f1d2b24aee6daa9439a97bca647e381864c824d60
6
+ metadata.gz: d2f56dd4ca2a33e244375c12aa9b2a2e09e8e78c1c5102123f2897340333cd7b8934dd12a0fe9e6722ba110f331154a2169c8e7417cde5fcc1791b78cfc9e2d4
7
+ data.tar.gz: a68d604c7e20ca081fb67ae1208fe2b22b6de28146b8f9685678dc0ddb6e8b04d30761769b284f16aceca262cff4323f78b8a077e35d98c40c93a25ad31ad192
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [1.0.4] - 2025-11-01
2
+
3
+ - Add .referencing_paths to help debugging finding what object is referencing another
4
+
1
5
  ## [1.0.3] - 2025-09-21
2
6
 
3
7
  - Fix bug that gives non-deterministic load order for file names that have the same depth and length
@@ -0,0 +1,193 @@
1
+ module Foobara
2
+ module Util
3
+ class ReferencePathPart
4
+ attr_accessor :referencer, :referencee, :via, :via_type
5
+
6
+ def initialize(referencer, referencee, via, via_type)
7
+ self.referencer = referencer
8
+ self.referencee = referencee
9
+ self.via = via
10
+ self.via_type = via_type
11
+ end
12
+ end
13
+
14
+ class << self
15
+ def object_id_to_object(object_id)
16
+ ObjectSpace._id2ref(object_id)
17
+ rescue RangeError
18
+ nil
19
+ end
20
+
21
+ def referencing_paths(object)
22
+ paths = _referencing_paths(object.object_id, object_id_referenced_by_map)
23
+ paths.sort_by!(&:size)
24
+ paths.reverse!
25
+ paths
26
+ end
27
+
28
+ private
29
+
30
+ def _referencing_paths(object_id, map, seen = Set.new, parts = [], paths = [])
31
+ seen << object_id
32
+
33
+ path_parts = map[object_id]&.uniq&.reject do |path_part|
34
+ seen.include?(path_part.referencer)
35
+ end&.uniq
36
+
37
+ if path_parts && !path_parts.empty?
38
+ path_parts.each do |path_part|
39
+ _referencing_paths(path_part.referencer, map, seen, [*parts, path_part], paths)
40
+ end
41
+ else
42
+ resolved_parts = []
43
+
44
+ parts.each do |part|
45
+ referenced_by = object_id_to_object(part.referencer)
46
+
47
+ via = part.via
48
+
49
+ via = case part.via_type
50
+ when :constant
51
+ "::#{via}"
52
+ when :array_index, :hash_value
53
+ "[#{via.inspect}]"
54
+ when :hash_key
55
+ "{#{via}}"
56
+ when :ivar
57
+ via
58
+ else
59
+ # :nocov:
60
+ raise "Unexpected via_type #{part.via_type}"
61
+ # :nocov:
62
+ end
63
+
64
+ class_part = if referenced_by.is_a?(Module)
65
+ name = referenced_by.name
66
+
67
+ if name
68
+ name
69
+ elsif referenced_by.is_a?(Class)
70
+ "AnonClass:#{referenced_by.object_id}"
71
+ else
72
+ "AnonModule:#{referenced_by.object_id}"
73
+ end
74
+ else
75
+ "<#{referenced_by.class}:#{referenced_by.object_id}>"
76
+ end
77
+
78
+ resolved_parts << "#{class_part}#{via}"
79
+ end
80
+
81
+ resolved_parts.reverse!
82
+
83
+ paths << resolved_parts
84
+ end
85
+
86
+ paths
87
+ end
88
+
89
+ def all_objects
90
+ ObjectSpace.each_object.to_a
91
+ end
92
+
93
+ UNINTERESTING_REFERENCE_CLASSES = [
94
+ "String",
95
+ "Proc",
96
+ "Time",
97
+ "Regexp",
98
+ "WeakRef",
99
+ "Thread::Mutex",
100
+ "Gem::Requirement",
101
+ "Symbol"
102
+ ].freeze
103
+
104
+ private_constant :UNINTERESTING_REFERENCE_CLASSES
105
+
106
+ def object_id_references_map
107
+ references = {}
108
+
109
+ objects = all_objects
110
+
111
+ uninteresting_classes = UNINTERESTING_REFERENCE_CLASSES.map do |class_name|
112
+ if Object.const_defined?(class_name)
113
+ Object.const_get(class_name)
114
+ end
115
+ end.compact
116
+
117
+ objects = objects.reject do |object|
118
+ uninteresting_classes.include?(object.class)
119
+ end
120
+
121
+ objects.each do |object|
122
+ # rubocop:disable Lint/HashCompareByIdentity
123
+ references[object.object_id] = object_id_references(object)
124
+ # rubocop:enable Lint/HashCompareByIdentity
125
+ end
126
+
127
+ references
128
+ end
129
+
130
+ def object_id_referenced_by_map
131
+ referenced_by = {}
132
+
133
+ object_id_references_map.each_value do |references|
134
+ references.each do |referenced_path_part|
135
+ referenced_object_id = referenced_path_part.referencee
136
+
137
+ referenced_by[referenced_object_id] ||= []
138
+ referenced_by[referenced_object_id] << referenced_path_part
139
+ end
140
+ end
141
+
142
+ referenced_by
143
+ end
144
+
145
+ def object_id_references(object)
146
+ object_id = object.object_id
147
+
148
+ references = object.instance_variables.map do |ivar|
149
+ ReferencePathPart.new(
150
+ object_id,
151
+ object.instance_variable_get(ivar).object_id,
152
+ ivar,
153
+ :ivar
154
+ )
155
+ end
156
+
157
+ case object
158
+ when ::Module
159
+ object.constants(false).each do |constant_name|
160
+ references << ReferencePathPart.new(
161
+ object_id,
162
+ object.const_get(constant_name).object_id,
163
+ constant_name,
164
+ :constant
165
+ )
166
+ # rubocop:disable Lint/RescueException
167
+ rescue Exception
168
+ # rubocop:enable Lint/RescueException
169
+ nil
170
+ end
171
+ when ::Array, ::Set
172
+
173
+ object.each.with_index do |element, index|
174
+ references << ReferencePathPart.new(
175
+ object_id,
176
+ element.object_id,
177
+ index,
178
+ :array_index
179
+ )
180
+ end
181
+
182
+ when ::Hash
183
+ object.each_pair do |k, v|
184
+ references << ReferencePathPart.new(object_id, k.object_id, k, :hash_key)
185
+ references << ReferencePathPart.new(object_id, v.object_id, k, :hash_value)
186
+ end
187
+ end
188
+
189
+ references
190
+ end
191
+ end
192
+ end
193
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara-util
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
@@ -28,6 +28,7 @@ files:
28
28
  - lib/foobara/util/hash.rb
29
29
  - lib/foobara/util/meta.rb
30
30
  - lib/foobara/util/module.rb
31
+ - lib/foobara/util/object_space.rb
31
32
  - lib/foobara/util/require.rb
32
33
  - lib/foobara/util/string.rb
33
34
  - lib/foobara/util/structured.rb