class-metrix 0.1.2 → 1.0.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.
- checksums.yaml +4 -4
- data/.editorconfig +48 -0
- data/.vscode/README.md +128 -0
- data/.vscode/extensions.json +31 -0
- data/.vscode/keybindings.json +26 -0
- data/.vscode/launch.json +32 -0
- data/.vscode/rbs.code-snippets +61 -0
- data/.vscode/settings.json +112 -0
- data/.vscode/tasks.json +240 -0
- data/CHANGELOG.md +73 -4
- data/README.md +86 -22
- data/Steepfile +26 -0
- data/docs/ARCHITECTURE.md +501 -0
- data/docs/CHANGELOG_EVOLUTION_EXAMPLE.md +95 -0
- data/examples/README.md +161 -114
- data/examples/basic_usage.rb +88 -0
- data/examples/debug_levels_demo.rb +65 -0
- data/examples/debug_mode_demo.rb +75 -0
- data/examples/inheritance_and_modules.rb +155 -0
- data/lib/class_metrix/extractor.rb +106 -11
- data/lib/class_metrix/extractors/constants_extractor.rb +155 -21
- data/lib/class_metrix/extractors/methods_extractor.rb +186 -21
- data/lib/class_metrix/extractors/multi_type_extractor.rb +8 -7
- data/lib/class_metrix/formatters/base/base_formatter.rb +3 -3
- data/lib/class_metrix/formatters/components/footer_component.rb +4 -4
- data/lib/class_metrix/formatters/components/generic_header_component.rb +2 -2
- data/lib/class_metrix/formatters/components/header_component.rb +4 -4
- data/lib/class_metrix/formatters/components/missing_behaviors_component.rb +7 -7
- data/lib/class_metrix/formatters/components/table_component/column_width_calculator.rb +56 -0
- data/lib/class_metrix/formatters/components/table_component/row_processor.rb +141 -0
- data/lib/class_metrix/formatters/components/table_component/table_data_extractor.rb +57 -0
- data/lib/class_metrix/formatters/components/table_component/table_renderer.rb +55 -0
- data/lib/class_metrix/formatters/components/table_component.rb +32 -245
- data/lib/class_metrix/formatters/csv_formatter.rb +3 -3
- data/lib/class_metrix/formatters/markdown_formatter.rb +3 -4
- data/lib/class_metrix/formatters/shared/markdown_table_builder.rb +12 -7
- data/lib/class_metrix/formatters/shared/table_builder.rb +92 -27
- data/lib/class_metrix/formatters/shared/value_processor.rb +72 -16
- data/lib/class_metrix/utils/debug_logger.rb +159 -0
- data/lib/class_metrix/version.rb +1 -1
- data/sig/class_metrix.rbs +8 -0
- data/sig/extractor.rbs +54 -0
- data/sig/extractors.rbs +84 -0
- data/sig/formatters_base.rbs +59 -0
- data/sig/formatters_components.rbs +133 -0
- data/sig/formatters_main.rbs +20 -0
- data/sig/formatters_shared.rbs +102 -0
- data/sig/manifest.yaml +32 -0
- data/sig/utils.rbs +57 -0
- data/sig/value_processor.rbs +11 -0
- data/sig/version.rbs +4 -0
- metadata +60 -10
- data/examples/advanced/error_handling.rb +0 -199
- data/examples/advanced/hash_expansion.rb +0 -180
- data/examples/basic/01_simple_constants.rb +0 -56
- data/examples/basic/02_simple_methods.rb +0 -99
- data/examples/basic/03_multi_type_extraction.rb +0 -116
- data/examples/components/configurable_reports.rb +0 -201
- data/examples/csv_output_demo.rb +0 -237
- data/examples/real_world/microservices_audit.rb +0 -312
- data/sig/class/metrix.rbs +0 -6
@@ -0,0 +1,155 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "../lib/class_metrix"
|
5
|
+
|
6
|
+
puts "ClassMetrix: Inheritance & Module Analysis"
|
7
|
+
puts "=" * 50
|
8
|
+
|
9
|
+
# Example: Service Configuration Analysis
|
10
|
+
# This demonstrates analyzing service classes that use inheritance and modules
|
11
|
+
|
12
|
+
module Configurable
|
13
|
+
DEFAULT_TIMEOUT = 30
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def configuration
|
21
|
+
{ timeout: DEFAULT_TIMEOUT, enabled: true }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Cacheable
|
27
|
+
CACHE_TTL = 3600
|
28
|
+
|
29
|
+
def self.included(base)
|
30
|
+
base.extend(ClassMethods)
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
def cache_config
|
35
|
+
{ ttl: CACHE_TTL, enabled: true }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class BaseService
|
41
|
+
SERVICE_VERSION = "1.0"
|
42
|
+
|
43
|
+
def self.service_type
|
44
|
+
"base"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.health_check
|
48
|
+
{ status: "ok", version: SERVICE_VERSION }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class DatabaseService < BaseService
|
53
|
+
include Configurable
|
54
|
+
|
55
|
+
SERVICE_NAME = "database"
|
56
|
+
CONNECTION_POOL_SIZE = 5
|
57
|
+
|
58
|
+
def self.service_type
|
59
|
+
"database"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.connection_config
|
63
|
+
{ pool_size: CONNECTION_POOL_SIZE, timeout: 60 }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class CacheService < BaseService
|
68
|
+
include Configurable
|
69
|
+
include Cacheable
|
70
|
+
|
71
|
+
SERVICE_NAME = "cache"
|
72
|
+
MAX_MEMORY = "512MB"
|
73
|
+
|
74
|
+
def self.service_type
|
75
|
+
"cache"
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.memory_config
|
79
|
+
{ max_memory: MAX_MEMORY, eviction_policy: "lru" }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Demo the functionality
|
84
|
+
services = [DatabaseService, CacheService]
|
85
|
+
|
86
|
+
puts "\n1. Basic Analysis (Own Constants Only)"
|
87
|
+
puts "-" * 40
|
88
|
+
result = ClassMetrix.extract(:constants)
|
89
|
+
.from(services)
|
90
|
+
.to_markdown
|
91
|
+
puts result
|
92
|
+
|
93
|
+
puts "\n2. With Inheritance (Own + Parent Constants)"
|
94
|
+
puts "-" * 40
|
95
|
+
result = ClassMetrix.extract(:constants)
|
96
|
+
.from(services)
|
97
|
+
.include_inherited
|
98
|
+
.to_markdown
|
99
|
+
puts result
|
100
|
+
|
101
|
+
puts "\n3. With Modules (Own + Module Constants)"
|
102
|
+
puts "-" * 40
|
103
|
+
result = ClassMetrix.extract(:constants)
|
104
|
+
.from(services)
|
105
|
+
.include_modules
|
106
|
+
.to_markdown
|
107
|
+
puts result
|
108
|
+
|
109
|
+
puts "\n4. Complete Analysis (Own + Inherited + Modules)"
|
110
|
+
puts "-" * 40
|
111
|
+
result = ClassMetrix.extract(:constants, :class_methods)
|
112
|
+
.from(services)
|
113
|
+
.include_all
|
114
|
+
.handle_errors
|
115
|
+
.to_markdown
|
116
|
+
puts result
|
117
|
+
|
118
|
+
puts "\n5. Filtered Configuration Analysis"
|
119
|
+
puts "-" * 40
|
120
|
+
result = ClassMetrix.extract(:constants, :class_methods)
|
121
|
+
.from(services)
|
122
|
+
.include_all
|
123
|
+
.filter(/config|timeout|service/i)
|
124
|
+
.expand_hashes
|
125
|
+
.to_markdown
|
126
|
+
puts result
|
127
|
+
|
128
|
+
puts "\n6. Hash Expansion Modes"
|
129
|
+
puts "-" * 40
|
130
|
+
|
131
|
+
puts "\n6a. Default: Show Only Main Rows (Collapsed Hashes)"
|
132
|
+
result = ClassMetrix.extract(:class_methods)
|
133
|
+
.from([CacheService])
|
134
|
+
.filter(/config/)
|
135
|
+
.expand_hashes
|
136
|
+
.to_markdown
|
137
|
+
puts result
|
138
|
+
|
139
|
+
puts "\n6b. Show Only Key Rows (Expanded Details)"
|
140
|
+
result = ClassMetrix.extract(:class_methods)
|
141
|
+
.from([CacheService])
|
142
|
+
.filter(/config/)
|
143
|
+
.expand_hashes
|
144
|
+
.show_only_keys
|
145
|
+
.to_markdown
|
146
|
+
puts result
|
147
|
+
|
148
|
+
puts "\n6c. Show Both Main and Key Rows"
|
149
|
+
result = ClassMetrix.extract(:class_methods)
|
150
|
+
.from([CacheService])
|
151
|
+
.filter(/config/)
|
152
|
+
.expand_hashes
|
153
|
+
.show_expanded_details
|
154
|
+
.to_markdown
|
155
|
+
puts result
|
@@ -6,6 +6,7 @@ require_relative "extractors/multi_type_extractor"
|
|
6
6
|
require_relative "formatters/markdown_formatter"
|
7
7
|
require_relative "formatters/csv_formatter"
|
8
8
|
require_relative "utils/class_resolver"
|
9
|
+
require_relative "utils/debug_logger"
|
9
10
|
|
10
11
|
module ClassMetrix
|
11
12
|
class Extractor
|
@@ -16,10 +17,19 @@ module ClassMetrix
|
|
16
17
|
@expand_hashes = false
|
17
18
|
@handle_errors = false
|
18
19
|
@modules = []
|
20
|
+
@include_inherited = false
|
21
|
+
@include_modules = false
|
22
|
+
@show_source = false
|
23
|
+
@hide_main_row = false
|
24
|
+
@hide_key_rows = true # Default: show only main rows
|
25
|
+
@debug_mode = false
|
26
|
+
@debug_level = :basic
|
27
|
+
@logger = nil # Will be initialized when debug mode is enabled
|
19
28
|
end
|
20
29
|
|
21
30
|
def from(classes)
|
22
31
|
@classes = ClassResolver.normalize_classes(classes)
|
32
|
+
@logger&.log("Normalized classes: #{@classes.map(&:name)}")
|
23
33
|
self
|
24
34
|
end
|
25
35
|
|
@@ -30,6 +40,15 @@ module ClassMetrix
|
|
30
40
|
|
31
41
|
def expand_hashes
|
32
42
|
@expand_hashes = true
|
43
|
+
@logger&.log("Hash expansion enabled")
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def debug(level = :basic)
|
48
|
+
@debug_mode = true
|
49
|
+
@debug_level = level
|
50
|
+
@logger = Utils::DebugLogger.new("Extractor", @debug_mode, level)
|
51
|
+
@logger&.log("Debug mode enabled (level: #{level})")
|
33
52
|
self
|
34
53
|
end
|
35
54
|
|
@@ -43,10 +62,63 @@ module ClassMetrix
|
|
43
62
|
self
|
44
63
|
end
|
45
64
|
|
65
|
+
# Inheritance and module inclusion options
|
66
|
+
def include_inherited
|
67
|
+
@include_inherited = true
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def include_modules
|
72
|
+
@include_modules = true
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def show_source
|
77
|
+
@show_source = true
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def include_all
|
82
|
+
@include_inherited = true
|
83
|
+
@include_modules = true
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
# Hash expansion display options
|
88
|
+
def show_only_main
|
89
|
+
@hide_main_row = false
|
90
|
+
@hide_key_rows = true
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def show_only_keys
|
95
|
+
@hide_main_row = true
|
96
|
+
@hide_key_rows = false
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def show_expanded_details
|
101
|
+
@hide_main_row = false
|
102
|
+
@hide_key_rows = false
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
# Lower-level options (for advanced usage)
|
107
|
+
def hide_main_row
|
108
|
+
@hide_main_row = true
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def hide_key_rows
|
113
|
+
@hide_key_rows = true
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
46
117
|
def to_markdown(filename = nil, **options)
|
118
|
+
@logger&.log("Starting markdown generation...")
|
47
119
|
data = extract_all_data
|
120
|
+
@logger&.log("Extracted data structure with #{data[:rows]&.length || 0} rows")
|
48
121
|
|
49
|
-
# Merge default options with passed options
|
50
122
|
format_options = {
|
51
123
|
extraction_types: @types,
|
52
124
|
show_missing_summary: false,
|
@@ -57,7 +129,11 @@ module ClassMetrix
|
|
57
129
|
show_classes: true,
|
58
130
|
show_extraction_info: true,
|
59
131
|
table_style: :standard,
|
60
|
-
summary_style: :grouped
|
132
|
+
summary_style: :grouped,
|
133
|
+
hide_main_row: @hide_main_row,
|
134
|
+
hide_key_rows: @hide_key_rows,
|
135
|
+
debug_mode: @debug_mode,
|
136
|
+
debug_level: @debug_level
|
61
137
|
}.merge(options)
|
62
138
|
|
63
139
|
formatted = MarkdownFormatter.new(data, @expand_hashes, format_options).format
|
@@ -67,9 +143,9 @@ module ClassMetrix
|
|
67
143
|
end
|
68
144
|
|
69
145
|
def to_csv(filename = nil, **options)
|
146
|
+
@logger&.log("Starting CSV generation...")
|
70
147
|
data = extract_all_data
|
71
148
|
|
72
|
-
# Merge default options with passed options
|
73
149
|
format_options = {
|
74
150
|
extraction_types: @types,
|
75
151
|
show_metadata: true,
|
@@ -77,7 +153,11 @@ module ClassMetrix
|
|
77
153
|
quote_char: '"',
|
78
154
|
flatten_hashes: true,
|
79
155
|
null_value: "",
|
80
|
-
comment_char: "#"
|
156
|
+
comment_char: "#",
|
157
|
+
hide_main_row: @hide_main_row,
|
158
|
+
hide_key_rows: @hide_key_rows,
|
159
|
+
debug_mode: @debug_mode,
|
160
|
+
debug_level: @debug_level
|
81
161
|
}.merge(options)
|
82
162
|
|
83
163
|
formatted = CsvFormatter.new(data, @expand_hashes, format_options).format
|
@@ -89,7 +169,7 @@ module ClassMetrix
|
|
89
169
|
private
|
90
170
|
|
91
171
|
def extract_all_data
|
92
|
-
|
172
|
+
@logger&.log("Extracting data for types: #{@types}")
|
93
173
|
if @types.size == 1
|
94
174
|
extract_single_type(@types.first)
|
95
175
|
else
|
@@ -98,24 +178,39 @@ module ClassMetrix
|
|
98
178
|
end
|
99
179
|
|
100
180
|
def extract_single_type(type)
|
101
|
-
|
102
|
-
|
181
|
+
@logger&.log("Extracting single type: #{type}")
|
182
|
+
get_extractor(type).extract
|
103
183
|
end
|
104
184
|
|
105
185
|
def extract_multiple_types
|
106
|
-
|
107
|
-
|
186
|
+
@logger&.log("Extracting multiple types: #{@types}")
|
187
|
+
extraction_config = {
|
188
|
+
modules: @modules,
|
189
|
+
handle_errors: @handle_errors,
|
190
|
+
options: extraction_options
|
191
|
+
}
|
192
|
+
MultiTypeExtractor.new(@classes, @types, @filters, extraction_config).extract
|
108
193
|
end
|
109
194
|
|
110
195
|
def get_extractor(type)
|
111
196
|
case type
|
112
197
|
when :constants
|
113
|
-
ConstantsExtractor.new(@classes, @filters, @handle_errors)
|
198
|
+
ConstantsExtractor.new(@classes, @filters, @handle_errors, extraction_options)
|
114
199
|
when :class_methods
|
115
|
-
MethodsExtractor.new(@classes, @filters, @handle_errors)
|
200
|
+
MethodsExtractor.new(@classes, @filters, @handle_errors, extraction_options)
|
116
201
|
else
|
117
202
|
raise ArgumentError, "Unknown extraction type: #{type}"
|
118
203
|
end
|
119
204
|
end
|
205
|
+
|
206
|
+
def extraction_options
|
207
|
+
{
|
208
|
+
include_inherited: @include_inherited,
|
209
|
+
include_modules: @include_modules,
|
210
|
+
show_source: @show_source,
|
211
|
+
debug_mode: @debug_mode,
|
212
|
+
debug_level: @debug_level
|
213
|
+
}
|
214
|
+
end
|
120
215
|
end
|
121
216
|
end
|
@@ -1,57 +1,131 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "../processors/value_processor"
|
4
|
+
require_relative "../utils/debug_logger"
|
4
5
|
|
5
6
|
module ClassMetrix
|
6
7
|
class ConstantsExtractor
|
7
|
-
def initialize(classes, filters, handle_errors)
|
8
|
+
def initialize(classes, filters, handle_errors, options = {})
|
8
9
|
@classes = classes
|
9
10
|
@filters = filters
|
10
11
|
@handle_errors = handle_errors
|
12
|
+
@options = default_options.merge(options)
|
13
|
+
@debug_level = @options[:debug_level] || :basic
|
14
|
+
@logger = Utils::DebugLogger.new("ConstantsExtractor", @options[:debug_mode], @debug_level)
|
11
15
|
end
|
12
16
|
|
13
17
|
def extract
|
14
18
|
return { headers: [], rows: [] } if @classes.empty?
|
15
19
|
|
16
|
-
# Get all constant names across all classes
|
17
20
|
constant_names = get_all_constant_names
|
18
|
-
|
19
|
-
# Apply filters
|
20
21
|
constant_names = apply_filters(constant_names)
|
22
|
+
headers = build_headers
|
23
|
+
rows = build_rows(constant_names)
|
21
24
|
|
22
|
-
|
23
|
-
|
25
|
+
{ headers: headers, rows: rows }
|
26
|
+
end
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
private
|
29
|
+
|
30
|
+
def default_options
|
31
|
+
{
|
32
|
+
include_inherited: false,
|
33
|
+
include_modules: false,
|
34
|
+
show_source: false
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_headers
|
39
|
+
if @options[:show_source]
|
40
|
+
["Constant (Source)"] + @classes.map(&:name)
|
41
|
+
else
|
42
|
+
["Constant"] + @classes.map(&:name)
|
43
|
+
end
|
44
|
+
end
|
28
45
|
|
46
|
+
def build_rows(constant_names)
|
47
|
+
constant_names.map do |const_name|
|
48
|
+
row = [const_name]
|
29
49
|
@classes.each do |klass|
|
30
50
|
value = extract_constant_value(klass, const_name)
|
31
|
-
# Pass the raw value for hash expansion to work properly
|
32
51
|
row << value
|
33
52
|
end
|
34
|
-
|
35
53
|
row
|
36
54
|
end
|
37
|
-
|
38
|
-
{ headers: headers, rows: rows }
|
39
55
|
end
|
40
56
|
|
41
|
-
private
|
42
|
-
|
43
57
|
def get_all_constant_names
|
44
58
|
all_constants = Set.new
|
45
59
|
|
46
60
|
@classes.each do |klass|
|
47
|
-
|
48
|
-
|
49
|
-
|
61
|
+
constants = if inheritance_or_modules_enabled?
|
62
|
+
get_comprehensive_constants(klass)
|
63
|
+
else
|
64
|
+
klass.constants(false)
|
65
|
+
end
|
66
|
+
all_constants.merge(constants.map(&:to_s))
|
50
67
|
end
|
51
68
|
|
52
69
|
all_constants.to_a.sort
|
53
70
|
end
|
54
71
|
|
72
|
+
def inheritance_or_modules_enabled?
|
73
|
+
@options[:include_inherited] || @options[:include_modules]
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_comprehensive_constants(klass)
|
77
|
+
constants = Set.new
|
78
|
+
constants.merge(klass.constants(false))
|
79
|
+
|
80
|
+
constants.merge(get_inherited_constants(klass)) if @options[:include_inherited]
|
81
|
+
|
82
|
+
constants.merge(get_module_constants(klass)) if @options[:include_modules]
|
83
|
+
|
84
|
+
constants.to_a
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_inherited_constants(klass)
|
88
|
+
constants = Set.new
|
89
|
+
parent = klass.superclass
|
90
|
+
|
91
|
+
while parent && !core_class?(parent)
|
92
|
+
constants.merge(parent.constants(false))
|
93
|
+
parent = parent.superclass
|
94
|
+
end
|
95
|
+
|
96
|
+
constants
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_module_constants(klass)
|
100
|
+
constants = Set.new
|
101
|
+
all_modules = get_all_included_modules(klass)
|
102
|
+
|
103
|
+
all_modules.each do |mod|
|
104
|
+
constants.merge(mod.constants(false))
|
105
|
+
end
|
106
|
+
|
107
|
+
constants
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_all_included_modules(klass)
|
111
|
+
modules = [] # : Array[Module]
|
112
|
+
modules.concat(klass.included_modules)
|
113
|
+
|
114
|
+
if @options[:include_inherited]
|
115
|
+
parent = klass.superclass
|
116
|
+
while parent && !core_class?(parent)
|
117
|
+
modules.concat(parent.included_modules)
|
118
|
+
parent = parent.superclass
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
modules
|
123
|
+
end
|
124
|
+
|
125
|
+
def core_class?(klass)
|
126
|
+
[Object, BasicObject].include?(klass)
|
127
|
+
end
|
128
|
+
|
55
129
|
def apply_filters(constant_names)
|
56
130
|
return constant_names if @filters.empty?
|
57
131
|
|
@@ -72,16 +146,76 @@ module ClassMetrix
|
|
72
146
|
end
|
73
147
|
|
74
148
|
def extract_constant_value(klass, const_name)
|
75
|
-
|
76
|
-
|
77
|
-
|
149
|
+
constant_info = find_constant_source(klass, const_name)
|
150
|
+
|
151
|
+
if constant_info
|
152
|
+
value = constant_info[:value]
|
153
|
+
debug_log("Extracted constant '#{const_name}' from #{klass.name}: #{@logger.safe_inspect(value)} (#{@logger.safe_class(value)})")
|
154
|
+
value
|
78
155
|
else
|
156
|
+
debug_log("Constant '#{const_name}' not found in #{klass.name}")
|
79
157
|
@handle_errors ? ValueProcessor.missing_constant : nil
|
80
158
|
end
|
81
159
|
rescue NameError => e
|
160
|
+
debug_log("NameError extracting constant '#{const_name}' from #{klass.name}: #{e.message}")
|
82
161
|
@handle_errors ? ValueProcessor.handle_extraction_error(e) : (raise e)
|
83
162
|
rescue StandardError => e
|
163
|
+
debug_log("Error extracting constant '#{const_name}' from #{klass.name}: #{e.message}")
|
84
164
|
@handle_errors ? ValueProcessor.handle_extraction_error(e) : (raise e)
|
85
165
|
end
|
166
|
+
|
167
|
+
def find_constant_source(klass, const_name)
|
168
|
+
# Check own constants first
|
169
|
+
return build_constant_info(klass.const_get(const_name), klass.name, :own) if klass.const_defined?(const_name, false)
|
170
|
+
|
171
|
+
# Check inherited constants
|
172
|
+
if @options[:include_inherited]
|
173
|
+
inherited_info = find_inherited_constant(klass, const_name)
|
174
|
+
return inherited_info if inherited_info
|
175
|
+
end
|
176
|
+
|
177
|
+
# Check module constants
|
178
|
+
if @options[:include_modules]
|
179
|
+
module_info = find_module_constant(klass, const_name)
|
180
|
+
return module_info if module_info
|
181
|
+
end
|
182
|
+
|
183
|
+
# Fallback check with inheritance
|
184
|
+
return build_constant_info(klass.const_get(const_name), "inherited", :unknown) if klass.const_defined?(const_name, true)
|
185
|
+
|
186
|
+
nil
|
187
|
+
end
|
188
|
+
|
189
|
+
def find_inherited_constant(klass, const_name)
|
190
|
+
parent = klass.superclass
|
191
|
+
while parent && !core_class?(parent)
|
192
|
+
return build_constant_info(parent.const_get(const_name), parent.name, :inherited) if parent.const_defined?(const_name, false)
|
193
|
+
|
194
|
+
parent = parent.superclass
|
195
|
+
end
|
196
|
+
nil
|
197
|
+
end
|
198
|
+
|
199
|
+
def find_module_constant(klass, const_name)
|
200
|
+
all_modules = get_all_included_modules(klass)
|
201
|
+
|
202
|
+
all_modules.each do |mod|
|
203
|
+
return build_constant_info(mod.const_get(const_name), mod.name, :module) if mod.const_defined?(const_name, false)
|
204
|
+
end
|
205
|
+
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
|
209
|
+
def build_constant_info(value, source, type)
|
210
|
+
{
|
211
|
+
value: value,
|
212
|
+
source: source,
|
213
|
+
type: type
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
def debug_log(message)
|
218
|
+
@logger.log(message)
|
219
|
+
end
|
86
220
|
end
|
87
221
|
end
|