class-metrix 0.1.2
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +88 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +41 -0
- data/LICENSE.txt +21 -0
- data/README.md +417 -0
- data/RELEASE_GUIDE.md +158 -0
- data/Rakefile +12 -0
- data/examples/README.md +155 -0
- data/examples/advanced/error_handling.rb +199 -0
- data/examples/advanced/hash_expansion.rb +180 -0
- data/examples/basic/01_simple_constants.rb +56 -0
- data/examples/basic/02_simple_methods.rb +99 -0
- data/examples/basic/03_multi_type_extraction.rb +116 -0
- data/examples/components/configurable_reports.rb +201 -0
- data/examples/csv_output_demo.rb +237 -0
- data/examples/real_world/microservices_audit.rb +312 -0
- data/lib/class_metrix/extractor.rb +121 -0
- data/lib/class_metrix/extractors/constants_extractor.rb +87 -0
- data/lib/class_metrix/extractors/methods_extractor.rb +87 -0
- data/lib/class_metrix/extractors/multi_type_extractor.rb +66 -0
- data/lib/class_metrix/formatters/base/base_component.rb +62 -0
- data/lib/class_metrix/formatters/base/base_formatter.rb +93 -0
- data/lib/class_metrix/formatters/components/footer_component.rb +67 -0
- data/lib/class_metrix/formatters/components/generic_header_component.rb +87 -0
- data/lib/class_metrix/formatters/components/header_component.rb +92 -0
- data/lib/class_metrix/formatters/components/missing_behaviors_component.rb +140 -0
- data/lib/class_metrix/formatters/components/table_component.rb +268 -0
- data/lib/class_metrix/formatters/csv_formatter.rb +98 -0
- data/lib/class_metrix/formatters/markdown_formatter.rb +184 -0
- data/lib/class_metrix/formatters/shared/csv_table_builder.rb +21 -0
- data/lib/class_metrix/formatters/shared/markdown_table_builder.rb +97 -0
- data/lib/class_metrix/formatters/shared/table_builder.rb +267 -0
- data/lib/class_metrix/formatters/shared/value_processor.rb +78 -0
- data/lib/class_metrix/processors/value_processor.rb +40 -0
- data/lib/class_metrix/utils/class_resolver.rb +20 -0
- data/lib/class_metrix/version.rb +5 -0
- data/lib/class_metrix.rb +12 -0
- data/sig/class/metrix.rbs +6 -0
- metadata +118 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "value_processor"
|
|
4
|
+
|
|
5
|
+
module ClassMetrix
|
|
6
|
+
module Formatters
|
|
7
|
+
module Shared
|
|
8
|
+
class TableBuilder
|
|
9
|
+
attr_reader :data, :expand_hashes, :options
|
|
10
|
+
|
|
11
|
+
def initialize(data, expand_hashes = false, options = {})
|
|
12
|
+
@data = data
|
|
13
|
+
@expand_hashes = expand_hashes
|
|
14
|
+
@options = options
|
|
15
|
+
@value_processor = ValueProcessor
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build_simple_table
|
|
19
|
+
{
|
|
20
|
+
headers: @data[:headers],
|
|
21
|
+
rows: @data[:rows].map { |row| process_row(row) }
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def build_expanded_table
|
|
26
|
+
return build_simple_table unless @expand_hashes
|
|
27
|
+
|
|
28
|
+
headers = @data[:headers]
|
|
29
|
+
expanded_rows = process_rows_for_expansion(headers)
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
headers: headers,
|
|
33
|
+
rows: expanded_rows
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_flattened_table
|
|
38
|
+
return build_simple_table unless @expand_hashes
|
|
39
|
+
|
|
40
|
+
headers = @data[:headers]
|
|
41
|
+
rows = @data[:rows]
|
|
42
|
+
|
|
43
|
+
all_hash_keys = collect_all_hash_keys(rows, headers)
|
|
44
|
+
flattened_headers = create_flattened_headers(headers, all_hash_keys)
|
|
45
|
+
flattened_rows = create_flattened_rows(rows, headers, all_hash_keys)
|
|
46
|
+
|
|
47
|
+
{
|
|
48
|
+
headers: flattened_headers,
|
|
49
|
+
rows: flattened_rows
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def process_rows_for_expansion(headers)
|
|
56
|
+
expanded_rows = []
|
|
57
|
+
|
|
58
|
+
@data[:rows].each do |row|
|
|
59
|
+
if row_has_expandable_hash?(row)
|
|
60
|
+
expanded_rows.concat(expand_row(row, headers))
|
|
61
|
+
else
|
|
62
|
+
expanded_rows << process_row(row)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
expanded_rows
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def row_has_expandable_hash?(row)
|
|
70
|
+
row[value_start_index..].any? { |cell| cell.is_a?(Hash) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def create_flattened_rows(rows, headers, all_hash_keys)
|
|
74
|
+
rows.map do |row|
|
|
75
|
+
flatten_row(row, headers, all_hash_keys)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def has_type_column?
|
|
80
|
+
@data[:headers].first == "Type"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def behavior_column_index
|
|
84
|
+
has_type_column? ? 1 : 0
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def value_start_index
|
|
88
|
+
has_type_column? ? 2 : 1
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def class_headers
|
|
92
|
+
if has_type_column?
|
|
93
|
+
@data[:headers][2..] # Skip "Type" and "Behavior"
|
|
94
|
+
else
|
|
95
|
+
@data[:headers][1..] # Skip first column (behavior name)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def process_row(row)
|
|
100
|
+
row.map { |value| process_value(value) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def process_value(value)
|
|
104
|
+
# This will be overridden by subclasses
|
|
105
|
+
value
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def expand_row(row, _headers)
|
|
109
|
+
behavior_name = row[behavior_column_index]
|
|
110
|
+
values = row[value_start_index..]
|
|
111
|
+
|
|
112
|
+
all_hash_keys = collect_hash_keys_from_values(values)
|
|
113
|
+
return [process_row(row)] if all_hash_keys.empty?
|
|
114
|
+
|
|
115
|
+
build_expanded_row_set(row, behavior_name, values, all_hash_keys)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def collect_hash_keys_from_values(values)
|
|
119
|
+
all_hash_keys = Set.new
|
|
120
|
+
values.each do |value|
|
|
121
|
+
all_hash_keys.merge(value.keys.map(&:to_s)) if value.is_a?(Hash)
|
|
122
|
+
end
|
|
123
|
+
all_hash_keys
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def build_expanded_row_set(row, behavior_name, values, all_hash_keys)
|
|
127
|
+
expanded_rows = []
|
|
128
|
+
expanded_rows << build_main_row(row, behavior_name, values)
|
|
129
|
+
expanded_rows.concat(build_sub_rows(all_hash_keys, values))
|
|
130
|
+
expanded_rows
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def build_main_row(row, behavior_name, values)
|
|
134
|
+
processed_values = values.map { |value| process_value(value) }
|
|
135
|
+
|
|
136
|
+
if has_type_column?
|
|
137
|
+
[row[0], behavior_name] + processed_values
|
|
138
|
+
else
|
|
139
|
+
[behavior_name] + processed_values
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def build_sub_rows(all_hash_keys, values)
|
|
144
|
+
all_hash_keys.to_a.sort.map do |key|
|
|
145
|
+
build_single_sub_row(key, values)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def build_single_sub_row(key, values)
|
|
150
|
+
path_name = ".#{key}"
|
|
151
|
+
key_values = extract_key_values(values, key)
|
|
152
|
+
|
|
153
|
+
if has_type_column?
|
|
154
|
+
["", path_name] + key_values # Empty type for sub-rows
|
|
155
|
+
else
|
|
156
|
+
[path_name] + key_values
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def extract_key_values(values, key)
|
|
161
|
+
values.map do |value|
|
|
162
|
+
extract_single_key_value(value, key)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def extract_single_key_value(value, key)
|
|
167
|
+
if value.is_a?(Hash)
|
|
168
|
+
extract_hash_value_for_key(value, key)
|
|
169
|
+
else
|
|
170
|
+
get_null_value
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def extract_hash_value_for_key(hash, key)
|
|
175
|
+
if @value_processor.has_hash_key?(hash, key)
|
|
176
|
+
hash_value = @value_processor.safe_hash_lookup(hash, key)
|
|
177
|
+
process_value(hash_value)
|
|
178
|
+
else
|
|
179
|
+
get_null_value
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def collect_all_hash_keys(rows, _headers)
|
|
184
|
+
value_start_idx = value_start_index
|
|
185
|
+
all_keys = {} # behavior_name => Set of keys
|
|
186
|
+
|
|
187
|
+
rows.each do |row|
|
|
188
|
+
collect_hash_keys_for_row(row, value_start_idx, all_keys)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
all_keys
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def collect_hash_keys_for_row(row, value_start_idx, all_keys)
|
|
195
|
+
behavior_name = row[behavior_column_index]
|
|
196
|
+
values = row[value_start_idx..]
|
|
197
|
+
|
|
198
|
+
values.each do |value|
|
|
199
|
+
next unless value.is_a?(Hash)
|
|
200
|
+
|
|
201
|
+
all_keys[behavior_name] ||= Set.new
|
|
202
|
+
all_keys[behavior_name].merge(value.keys.map(&:to_s))
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def create_flattened_headers(headers, all_hash_keys)
|
|
207
|
+
flattened = headers.dup
|
|
208
|
+
class_hdrs = class_headers
|
|
209
|
+
|
|
210
|
+
add_hash_key_headers(flattened, all_hash_keys, class_hdrs)
|
|
211
|
+
flattened
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def add_hash_key_headers(flattened, all_hash_keys, class_hdrs)
|
|
215
|
+
all_hash_keys.each do |behavior_name, keys|
|
|
216
|
+
add_behavior_key_headers(flattened, behavior_name, keys, class_hdrs)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def add_behavior_key_headers(flattened, behavior_name, keys, class_hdrs)
|
|
221
|
+
keys.to_a.sort.each do |key|
|
|
222
|
+
class_hdrs.each do |class_name|
|
|
223
|
+
flattened << "#{behavior_name}.#{key}.#{class_name}"
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def flatten_row(row, _headers, all_hash_keys)
|
|
229
|
+
behavior_name = row[behavior_column_index]
|
|
230
|
+
flattened = process_row(row)
|
|
231
|
+
|
|
232
|
+
add_flattened_hash_values(flattened, row, behavior_name, all_hash_keys)
|
|
233
|
+
flattened
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def add_flattened_hash_values(flattened, row, behavior_name, all_hash_keys)
|
|
237
|
+
return unless all_hash_keys[behavior_name]
|
|
238
|
+
|
|
239
|
+
values = row[value_start_index..]
|
|
240
|
+
|
|
241
|
+
all_hash_keys[behavior_name].to_a.sort.each do |key|
|
|
242
|
+
add_flattened_values_for_key(flattened, values, key)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def add_flattened_values_for_key(flattened, values, key)
|
|
247
|
+
values.each do |value|
|
|
248
|
+
flattened << extract_flattened_value(value, key)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def extract_flattened_value(value, key)
|
|
253
|
+
if value.is_a?(Hash)
|
|
254
|
+
extract_hash_value_for_key(value, key)
|
|
255
|
+
else
|
|
256
|
+
get_null_value
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def get_null_value
|
|
261
|
+
# Override in subclasses
|
|
262
|
+
""
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClassMetrix
|
|
4
|
+
module Formatters
|
|
5
|
+
module Shared
|
|
6
|
+
class ValueProcessor
|
|
7
|
+
def self.process_for_markdown(value)
|
|
8
|
+
case value
|
|
9
|
+
when Hash
|
|
10
|
+
value.inspect
|
|
11
|
+
when Array
|
|
12
|
+
value.join(", ")
|
|
13
|
+
when true
|
|
14
|
+
"✅"
|
|
15
|
+
when false
|
|
16
|
+
"❌"
|
|
17
|
+
when nil
|
|
18
|
+
"❌"
|
|
19
|
+
when String
|
|
20
|
+
value
|
|
21
|
+
else
|
|
22
|
+
value.to_s
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.process_for_csv(value, options = {})
|
|
27
|
+
null_value = options.fetch(:null_value, "")
|
|
28
|
+
|
|
29
|
+
case value
|
|
30
|
+
when Hash
|
|
31
|
+
# For non-flattened CSV, represent hash as JSON-like string
|
|
32
|
+
value.inspect
|
|
33
|
+
when Array
|
|
34
|
+
value.join("; ") # Use semicolon to avoid CSV comma conflicts
|
|
35
|
+
when true
|
|
36
|
+
"TRUE"
|
|
37
|
+
when false
|
|
38
|
+
"FALSE"
|
|
39
|
+
when nil
|
|
40
|
+
null_value
|
|
41
|
+
when String
|
|
42
|
+
# Clean up emoji for CSV compatibility
|
|
43
|
+
clean_value = value.gsub(/🚫|⚠️|✅|❌/, "").strip
|
|
44
|
+
clean_value.empty? ? null_value : clean_value
|
|
45
|
+
else
|
|
46
|
+
value.to_s
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.safe_hash_lookup(hash, key)
|
|
51
|
+
# Properly handle false values in hash lookup
|
|
52
|
+
hash.key?(key.to_sym) ? hash[key.to_sym] : hash[key.to_s]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.has_hash_key?(hash, key)
|
|
56
|
+
hash.key?(key.to_sym) || hash.key?(key.to_s)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Error message generators
|
|
60
|
+
def self.missing_constant
|
|
61
|
+
"🚫 Not defined"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.missing_method
|
|
65
|
+
"🚫 No method"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.handle_extraction_error(error)
|
|
69
|
+
"⚠️ Error: #{error.message}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.missing_hash_key
|
|
73
|
+
"—"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../formatters/shared/value_processor"
|
|
4
|
+
|
|
5
|
+
module ClassMetrix
|
|
6
|
+
# Legacy value processor - now delegates to the new shared processor
|
|
7
|
+
# Maintained for backward compatibility with existing code
|
|
8
|
+
class ValueProcessor
|
|
9
|
+
def self.process(value, expand_hashes: false)
|
|
10
|
+
if expand_hashes && value.is_a?(Hash)
|
|
11
|
+
expand_hash(value)
|
|
12
|
+
else
|
|
13
|
+
Formatters::Shared::ValueProcessor.process_for_markdown(value)
|
|
14
|
+
end
|
|
15
|
+
rescue StandardError => e
|
|
16
|
+
"⚠️ #{e.class.name}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.format_hash(hash)
|
|
20
|
+
Formatters::Shared::ValueProcessor.process_for_markdown(hash)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.expand_hash(hash)
|
|
24
|
+
# Return an array of key-value pairs for expansion
|
|
25
|
+
hash.map { |k, v| { key: k.to_s, value: process(v, expand_hashes: false) } }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.handle_extraction_error(error)
|
|
29
|
+
Formatters::Shared::ValueProcessor.handle_extraction_error(error)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.missing_constant
|
|
33
|
+
Formatters::Shared::ValueProcessor.missing_constant
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.missing_method
|
|
37
|
+
Formatters::Shared::ValueProcessor.missing_method
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClassMetrix
|
|
4
|
+
class ClassResolver
|
|
5
|
+
def self.normalize_classes(classes)
|
|
6
|
+
classes.map do |klass|
|
|
7
|
+
case klass
|
|
8
|
+
when String
|
|
9
|
+
Object.const_get(klass)
|
|
10
|
+
when Class
|
|
11
|
+
klass
|
|
12
|
+
else
|
|
13
|
+
raise ArgumentError, "Invalid class: #{klass.inspect}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
rescue NameError => e
|
|
17
|
+
raise ArgumentError, "Class not found: #{e.message}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/class_metrix.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: class-metrix
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Huy Nguyen
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-06-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspec
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rubocop
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.0'
|
|
41
|
+
description: ClassMetrix allows you to easily extract and compare constants and class
|
|
42
|
+
methods across multiple Ruby classes, generating clean markdown tables for analysis
|
|
43
|
+
and documentation.
|
|
44
|
+
email:
|
|
45
|
+
- patrick204nqh@gmail.com
|
|
46
|
+
executables: []
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- ".rspec"
|
|
51
|
+
- ".rubocop.yml"
|
|
52
|
+
- ".tool-versions"
|
|
53
|
+
- CHANGELOG.md
|
|
54
|
+
- LICENSE.txt
|
|
55
|
+
- README.md
|
|
56
|
+
- RELEASE_GUIDE.md
|
|
57
|
+
- Rakefile
|
|
58
|
+
- examples/README.md
|
|
59
|
+
- examples/advanced/error_handling.rb
|
|
60
|
+
- examples/advanced/hash_expansion.rb
|
|
61
|
+
- examples/basic/01_simple_constants.rb
|
|
62
|
+
- examples/basic/02_simple_methods.rb
|
|
63
|
+
- examples/basic/03_multi_type_extraction.rb
|
|
64
|
+
- examples/components/configurable_reports.rb
|
|
65
|
+
- examples/csv_output_demo.rb
|
|
66
|
+
- examples/real_world/microservices_audit.rb
|
|
67
|
+
- lib/class_metrix.rb
|
|
68
|
+
- lib/class_metrix/extractor.rb
|
|
69
|
+
- lib/class_metrix/extractors/constants_extractor.rb
|
|
70
|
+
- lib/class_metrix/extractors/methods_extractor.rb
|
|
71
|
+
- lib/class_metrix/extractors/multi_type_extractor.rb
|
|
72
|
+
- lib/class_metrix/formatters/base/base_component.rb
|
|
73
|
+
- lib/class_metrix/formatters/base/base_formatter.rb
|
|
74
|
+
- lib/class_metrix/formatters/components/footer_component.rb
|
|
75
|
+
- lib/class_metrix/formatters/components/generic_header_component.rb
|
|
76
|
+
- lib/class_metrix/formatters/components/header_component.rb
|
|
77
|
+
- lib/class_metrix/formatters/components/missing_behaviors_component.rb
|
|
78
|
+
- lib/class_metrix/formatters/components/table_component.rb
|
|
79
|
+
- lib/class_metrix/formatters/csv_formatter.rb
|
|
80
|
+
- lib/class_metrix/formatters/markdown_formatter.rb
|
|
81
|
+
- lib/class_metrix/formatters/shared/csv_table_builder.rb
|
|
82
|
+
- lib/class_metrix/formatters/shared/markdown_table_builder.rb
|
|
83
|
+
- lib/class_metrix/formatters/shared/table_builder.rb
|
|
84
|
+
- lib/class_metrix/formatters/shared/value_processor.rb
|
|
85
|
+
- lib/class_metrix/processors/value_processor.rb
|
|
86
|
+
- lib/class_metrix/utils/class_resolver.rb
|
|
87
|
+
- lib/class_metrix/version.rb
|
|
88
|
+
- sig/class/metrix.rbs
|
|
89
|
+
homepage: https://github.com/patrick204nqh/class-metrix
|
|
90
|
+
licenses:
|
|
91
|
+
- MIT
|
|
92
|
+
metadata:
|
|
93
|
+
allowed_push_host: https://rubygems.org
|
|
94
|
+
homepage_uri: https://github.com/patrick204nqh/class-metrix
|
|
95
|
+
source_code_uri: https://github.com/patrick204nqh/class-metrix
|
|
96
|
+
changelog_uri: https://github.com/patrick204nqh/class-metrix/blob/master/CHANGELOG.md
|
|
97
|
+
rubygems_mfa_required: 'true'
|
|
98
|
+
post_install_message:
|
|
99
|
+
rdoc_options: []
|
|
100
|
+
require_paths:
|
|
101
|
+
- lib
|
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: 3.1.0
|
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0'
|
|
112
|
+
requirements: []
|
|
113
|
+
rubygems_version: 3.5.22
|
|
114
|
+
signing_key:
|
|
115
|
+
specification_version: 4
|
|
116
|
+
summary: Simple extraction and comparison of Ruby class behaviors with clean markdown
|
|
117
|
+
output
|
|
118
|
+
test_files: []
|