class-metrix 1.0.0 → 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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.vscode/README.md +103 -47
  3. data/.vscode/extensions.json +10 -7
  4. data/.vscode/keybindings.json +26 -0
  5. data/.vscode/rbs.code-snippets +61 -0
  6. data/.vscode/settings.json +25 -1
  7. data/.vscode/tasks.json +141 -0
  8. data/CHANGELOG.md +2 -0
  9. data/README.md +46 -16
  10. data/Steepfile +26 -0
  11. data/docs/CHANGELOG_EVOLUTION_EXAMPLE.md +95 -0
  12. data/lib/class_metrix/extractor.rb +1 -1
  13. data/lib/class_metrix/extractors/constants_extractor.rb +1 -1
  14. data/lib/class_metrix/extractors/methods_extractor.rb +1 -1
  15. data/lib/class_metrix/extractors/multi_type_extractor.rb +2 -2
  16. data/lib/class_metrix/formatters/base/base_formatter.rb +3 -3
  17. data/lib/class_metrix/formatters/components/footer_component.rb +3 -3
  18. data/lib/class_metrix/formatters/components/generic_header_component.rb +2 -2
  19. data/lib/class_metrix/formatters/components/header_component.rb +4 -4
  20. data/lib/class_metrix/formatters/components/missing_behaviors_component.rb +7 -7
  21. data/lib/class_metrix/formatters/components/table_component/row_processor.rb +8 -5
  22. data/lib/class_metrix/formatters/components/table_component/table_data_extractor.rb +4 -1
  23. data/lib/class_metrix/formatters/components/table_component/table_renderer.rb +2 -2
  24. data/lib/class_metrix/formatters/components/table_component.rb +5 -4
  25. data/lib/class_metrix/formatters/csv_formatter.rb +3 -3
  26. data/lib/class_metrix/formatters/markdown_formatter.rb +3 -4
  27. data/lib/class_metrix/formatters/shared/markdown_table_builder.rb +2 -2
  28. data/lib/class_metrix/formatters/shared/table_builder.rb +8 -6
  29. data/lib/class_metrix/version.rb +1 -1
  30. data/sig/class_metrix.rbs +8 -0
  31. data/sig/extractor.rbs +54 -0
  32. data/sig/extractors.rbs +84 -0
  33. data/sig/formatters_base.rbs +59 -0
  34. data/sig/formatters_components.rbs +133 -0
  35. data/sig/formatters_main.rbs +20 -0
  36. data/sig/formatters_shared.rbs +102 -0
  37. data/sig/manifest.yaml +32 -0
  38. data/sig/utils.rbs +57 -0
  39. data/sig/value_processor.rbs +11 -0
  40. data/sig/version.rbs +4 -0
  41. metadata +44 -2
  42. data/sig/class/metrix.rbs +0 -6
data/README.md CHANGED
@@ -5,10 +5,12 @@
5
5
 
6
6
  **ClassMetrix** is a Ruby gem that extracts and compares class behaviors (constants, class methods, and more) across multiple classes, generating clean markdown reports for analysis, documentation, and compliance auditing.
7
7
 
8
+ > **Why "Metrix"?** Short for "metrics" - measuring and analyzing class behaviors.
9
+
8
10
  ## ✨ Features
9
11
 
10
12
  - **🔍 Multi-Type Extraction**: Constants, class methods, and more
11
- - **📊 Hash Expansion**: Expand hash values into readable sub-rows
13
+ - **📊 Hash Expansion**: Expand hash values into readable sub-rows
12
14
  - **🛡️ Error Handling**: Graceful handling of missing methods and constants
13
15
  - **🐛 Debug Mode**: Detailed logging for troubleshooting and analysis
14
16
  - **📝 Rich Markdown Reports**: Professional reports with configurable components
@@ -30,7 +32,7 @@ ClassMetrix.extract(:constants, :class_methods)
30
32
  .filter(/config$/)
31
33
  .expand_hashes
32
34
  .handle_errors
33
- .to_markdown("audit_report.md",
35
+ .to_markdown("audit_report.md",
34
36
  title: "Service Configuration Audit",
35
37
  footer_style: :detailed,
36
38
  show_missing_summary: true
@@ -40,7 +42,7 @@ ClassMetrix.extract(:constants, :class_methods)
40
42
  ClassMetrix.extract(:constants)
41
43
  .from([DatabaseConfig, RedisConfig])
42
44
  .expand_hashes
43
- .to_csv("config_analysis.csv",
45
+ .to_csv("config_analysis.csv",
44
46
  title: "Configuration Analysis",
45
47
  flatten_hashes: true
46
48
  )
@@ -65,6 +67,7 @@ gem install class-metrix
65
67
  ### Basic Extraction
66
68
 
67
69
  #### Constants
70
+
68
71
  ```ruby
69
72
  # Extract constants from multiple classes
70
73
  ClassMetrix.extract(:constants)
@@ -79,6 +82,7 @@ ClassMetrix.extract(:constants)
79
82
  ```
80
83
 
81
84
  #### Class Methods
85
+
82
86
  ```ruby
83
87
  # Extract class method results
84
88
  ClassMetrix.extract(:class_methods)
@@ -94,6 +98,7 @@ ClassMetrix.extract(:class_methods)
94
98
  ```
95
99
 
96
100
  #### Multi-Type Extraction
101
+
97
102
  ```ruby
98
103
  # Combine multiple extraction types in one table
99
104
  ClassMetrix.extract(:constants, :class_methods)
@@ -110,6 +115,7 @@ ClassMetrix.extract(:constants, :class_methods)
110
115
  ### Advanced Features
111
116
 
112
117
  #### Hash Expansion
118
+
113
119
  ```ruby
114
120
  # Expand hash values into readable sub-rows
115
121
  ClassMetrix.extract(:constants)
@@ -127,6 +133,7 @@ ClassMetrix.extract(:constants)
127
133
  ```
128
134
 
129
135
  #### Error Handling
136
+
130
137
  ```ruby
131
138
  # Handle missing methods and constants gracefully
132
139
  ClassMetrix.extract(:class_methods)
@@ -144,6 +151,7 @@ ClassMetrix.extract(:class_methods)
144
151
  ```
145
152
 
146
153
  #### Filtering
154
+
147
155
  ```ruby
148
156
  # Filter behaviors by pattern
149
157
  ClassMetrix.extract(:constants)
@@ -159,6 +167,7 @@ ClassMetrix.extract(:class_methods)
159
167
  ```
160
168
 
161
169
  #### Debug Mode
170
+
162
171
  ```ruby
163
172
  # Enable detailed logging for troubleshooting
164
173
  ClassMetrix.extract(:constants, :class_methods)
@@ -185,17 +194,20 @@ ClassMetrix.extract(:constants)
185
194
  ```
186
195
 
187
196
  **Debug Levels:**
197
+
188
198
  - **`:basic`** (default) - Key decisions and summaries only
189
- - **`:detailed`** - More context and intermediate steps
199
+ - **`:detailed`** - More context and intermediate steps
190
200
  - **`:verbose`** - Full details including individual value processing
191
201
 
192
202
  **Debug Features:**
203
+
193
204
  - **Safe Object Inspection**: Handles objects with problematic `inspect`/`to_s` methods
194
205
  - **Hash Detection Analysis**: Shows why objects are/aren't treated as expandable hashes
195
206
  - **Smart Logging**: Reduces spam by grouping related operations and focusing on key decisions
196
207
  - **Error Diagnostics**: Detailed error context for troubleshooting
197
208
 
198
209
  #### CSV Output
210
+
199
211
  ```ruby
200
212
  # Basic CSV output
201
213
  ClassMetrix.extract(:constants)
@@ -229,6 +241,7 @@ ClassMetrix.extract(:class_methods)
229
241
  ClassMetrix offers extensive configuration options for customizing report generation:
230
242
 
231
243
  ### Markdown Report Options
244
+
232
245
  ```ruby
233
246
  ClassMetrix.extract(:constants)
234
247
  .from([User, Admin])
@@ -236,30 +249,31 @@ ClassMetrix.extract(:constants)
236
249
  # File and title
237
250
  "report.md",
238
251
  title: "Custom Report Title",
239
-
252
+
240
253
  # Content sections
241
254
  show_metadata: true, # Show title and report info
242
255
  show_classes: true, # Show "Classes Analyzed" section
243
256
  show_extraction_info: true, # Show "Extraction Types" section
244
257
  show_missing_summary: false, # Show missing behaviors summary
245
-
258
+
246
259
  # Footer configuration
247
260
  show_footer: true, # Show footer
248
261
  footer_style: :detailed, # :default, :minimal, :detailed
249
262
  show_timestamp: true, # Include generation timestamp
250
263
  custom_footer: "Custom note", # Custom footer message
251
-
264
+
252
265
  # Table formatting
253
266
  table_style: :standard, # :standard, :compact, :wide
254
267
  min_column_width: 3, # Minimum column width
255
268
  max_column_width: 50, # Maximum column width (for :compact style)
256
-
269
+
257
270
  # Missing behaviors analysis
258
271
  summary_style: :grouped # :grouped, :flat, :detailed
259
272
  )
260
273
  ```
261
274
 
262
275
  ### CSV Output Options
276
+
263
277
  ```ruby
264
278
  ClassMetrix.extract(:constants)
265
279
  .from([User, Admin])
@@ -267,16 +281,16 @@ ClassMetrix.extract(:constants)
267
281
  # File and title
268
282
  "report.csv",
269
283
  title: "Custom CSV Report",
270
-
284
+
271
285
  # Content options
272
286
  show_metadata: true, # Show comment headers
273
287
  comment_char: "#", # Comment character for metadata
274
-
288
+
275
289
  # CSV formatting
276
290
  separator: ",", # Column separator (comma, semicolon, tab)
277
291
  quote_char: '"', # Quote character
278
292
  null_value: "", # Value for nil/missing data
279
-
293
+
280
294
  # Hash handling
281
295
  flatten_hashes: true, # Flatten hashes into separate columns
282
296
  # false = expand into sub-rows
@@ -286,20 +300,26 @@ ClassMetrix.extract(:constants)
286
300
  ### Footer Styles
287
301
 
288
302
  #### Default Footer
303
+
289
304
  ```markdown
290
305
  ---
291
- *Report generated by ClassMetrix gem*
306
+
307
+ _Report generated by ClassMetrix gem_
292
308
  ```
293
309
 
294
310
  #### Minimal Footer
311
+
295
312
  ```markdown
296
313
  ---
297
- *Generated by ClassMetrix*
314
+
315
+ _Generated by ClassMetrix_
298
316
  ```
299
317
 
300
318
  #### Detailed Footer
319
+
301
320
  ```markdown
302
321
  ---
322
+
303
323
  ## Report Information
304
324
 
305
325
  - **Generated by**: [ClassMetrix gem](https://github.com/patrick204nqh/class-metrix)
@@ -310,18 +330,22 @@ ClassMetrix.extract(:constants)
310
330
  ### Missing Behaviors Styles
311
331
 
312
332
  #### Grouped (Default)
333
+
313
334
  ```markdown
314
335
  ## Missing Behaviors Summary
315
336
 
316
337
  ### DatabaseConfig
338
+
317
339
  - `redis_config` - 🚫 Not defined
318
340
  - `cache_timeout` - 🚫 No method
319
341
 
320
- ### RedisConfig
342
+ ### RedisConfig
343
+
321
344
  - `db_config` - 🚫 Not defined
322
345
  ```
323
346
 
324
347
  #### Flat
348
+
325
349
  ```markdown
326
350
  ## Missing Behaviors
327
351
 
@@ -331,22 +355,26 @@ ClassMetrix.extract(:constants)
331
355
  ```
332
356
 
333
357
  #### Detailed
358
+
334
359
  ```markdown
335
360
  ## Missing Behaviors Analysis
336
361
 
337
362
  **Summary**: 3 missing behaviors across 2 classes
338
363
 
339
364
  ### 🚫 Not (2 items)
365
+
340
366
  - **DatabaseConfig**: `redis_config` - 🚫 Not defined
341
367
  - **RedisConfig**: `db_config` - 🚫 Not defined
342
368
 
343
369
  ### 🚫 No (1 items)
370
+
344
371
  - **DatabaseConfig**: `cache_timeout` - 🚫 No method
345
372
  ```
346
373
 
347
374
  ## 🎯 Real-World Examples
348
375
 
349
376
  ### Microservices Configuration Audit
377
+
350
378
  ```ruby
351
379
  # Audit configuration consistency across services
352
380
  services = [DatabaseService, RedisService, S3Service, AuthService]
@@ -365,6 +393,7 @@ ClassMetrix.extract(:constants, :class_methods)
365
393
  ```
366
394
 
367
395
  ### Policy Classes Comparison
396
+
368
397
  ```ruby
369
398
  # Compare authorization policies
370
399
  policies = [UserPolicy, AdminPolicy, ModeratorPolicy]
@@ -379,6 +408,7 @@ ClassMetrix.extract(:constants)
379
408
  ```
380
409
 
381
410
  ### API Version Compatibility
411
+
382
412
  ```ruby
383
413
  # Check API compatibility across versions
384
414
  apis = [V1::UsersAPI, V2::UsersAPI, V3::UsersAPI]
@@ -401,7 +431,7 @@ ClassMetrix uses a modular component architecture for maximum flexibility:
401
431
  ```
402
432
  MarkdownFormatter
403
433
  ├── HeaderComponent # Title, classes, extraction info
404
- ├── TableComponent # Table formatting and hash expansion
434
+ ├── TableComponent # Table formatting and hash expansion
405
435
  ├── MissingBehaviorsComponent # Missing behavior analysis
406
436
  └── FooterComponent # Footer with various styles
407
437
  ```
@@ -428,7 +458,7 @@ ruby examples/advanced/hash_expansion.rb
428
458
 
429
459
  ## 📋 Requirements
430
460
 
431
- - Ruby 2.7+
461
+ - Ruby 2.7+
432
462
  - No runtime dependencies (pure Ruby implementation)
433
463
 
434
464
  ## 🤝 Contributing
data/Steepfile ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Steepfile for ClassMetrix gem
4
+ D = Steep::Diagnostic
5
+
6
+ target :lib do
7
+ signature "sig"
8
+
9
+ check "lib"
10
+
11
+ # Configure libraries
12
+ library "pathname"
13
+ library "csv"
14
+ library "json"
15
+ library "fileutils"
16
+
17
+ # Configure typing options
18
+ configure_code_diagnostics(D::Ruby.default)
19
+
20
+ # Disable some noisy diagnostics for better development experience
21
+ configure_code_diagnostics do |hash|
22
+ hash[D::Ruby::UnresolvedOverloading] = :information
23
+ hash[D::Ruby::FallbackAny] = :information
24
+ hash[D::Ruby::ImplicitBreakValueMismatch] = :hint
25
+ end
26
+ end
@@ -0,0 +1,95 @@
1
+ # ClassMetrix Changelog Evolution Example
2
+
3
+ ## Current State (v1.0.0 released)
4
+
5
+ ```markdown
6
+ ## [Unreleased]
7
+
8
+ ## [1.0.0] - 2025-06-07
9
+
10
+ ### Added
11
+
12
+ - Advanced Debug System
13
+ - Enhanced Hash Expansion
14
+
15
+ # ... existing content
16
+ ```
17
+
18
+ ## After working on some features
19
+
20
+ ```markdown
21
+ ## [Unreleased]
22
+
23
+ ### Added
24
+
25
+ - Instance variable extraction support
26
+ - New output format: JSON
27
+ - CLI interface for command-line usage
28
+
29
+ ### Changed
30
+
31
+ - Improved performance for large class hierarchies
32
+ - Better error messages with suggestions
33
+
34
+ ### Fixed
35
+
36
+ - Memory leak in hash expansion
37
+ - CSV encoding issues with special characters
38
+
39
+ ## [1.0.0] - 2025-06-07
40
+
41
+ # ... existing content
42
+ ```
43
+
44
+ ## Ready for v1.1.0 release
45
+
46
+ ```markdown
47
+ ## [Unreleased]
48
+
49
+ ## [1.1.0] - 2025-07-15
50
+
51
+ ### Added
52
+
53
+ - Instance variable extraction support
54
+ - New output format: JSON
55
+ - CLI interface for command-line usage
56
+
57
+ ### Changed
58
+
59
+ - Improved performance for large class hierarchies
60
+ - Better error messages with suggestions
61
+
62
+ ### Fixed
63
+
64
+ - Memory leak in hash expansion
65
+ - CSV encoding issues with special characters
66
+
67
+ ## [1.0.0] - 2025-06-07
68
+
69
+ # ... existing content
70
+ ```
71
+
72
+ ## Working on next features
73
+
74
+ ```markdown
75
+ ## [Unreleased]
76
+
77
+ ### Added
78
+
79
+ - Plugin system for custom extractors
80
+ - Web UI for interactive analysis
81
+
82
+ ### Changed
83
+
84
+ - Refactored formatter architecture
85
+
86
+ ## [1.1.0] - 2025-07-15
87
+
88
+ ### Added
89
+
90
+ - Instance variable extraction support
91
+ - New output format: JSON
92
+ - CLI interface for command-line usage
93
+
94
+ # ... etc
95
+ ```
@@ -48,7 +48,7 @@ module ClassMetrix
48
48
  @debug_mode = true
49
49
  @debug_level = level
50
50
  @logger = Utils::DebugLogger.new("Extractor", @debug_mode, level)
51
- @logger.log("Debug mode enabled (level: #{level})")
51
+ @logger&.log("Debug mode enabled (level: #{level})")
52
52
  self
53
53
  end
54
54
 
@@ -108,7 +108,7 @@ module ClassMetrix
108
108
  end
109
109
 
110
110
  def get_all_included_modules(klass)
111
- modules = []
111
+ modules = [] # : Array[Module]
112
112
  modules.concat(klass.included_modules)
113
113
 
114
114
  if @options[:include_inherited]
@@ -115,7 +115,7 @@ module ClassMetrix
115
115
  end
116
116
 
117
117
  def get_all_singleton_modules(klass)
118
- modules = []
118
+ modules = [] # : Array[Module]
119
119
  modules.concat(klass.singleton_class.included_modules)
120
120
 
121
121
  if @options[:include_inherited]
@@ -20,7 +20,7 @@ module ClassMetrix
20
20
  # Build headers: ["Type", "Behavior", "Class1", "Class2", ...]
21
21
  headers = %w[Type Behavior] + @classes.map(&:name)
22
22
 
23
- all_rows = []
23
+ all_rows = [] # : Array[Array[untyped]]
24
24
 
25
25
  @types.each do |type|
26
26
  type_data = extract_single_type(type)
@@ -28,7 +28,7 @@ module ClassMetrix
28
28
  # Add rows with type prefix
29
29
  type_data[:rows].each do |row|
30
30
  behavior_name = row[0]
31
- values = row[1..]
31
+ values = row[1..] || []
32
32
 
33
33
  new_row = [type_label(type), behavior_name] + values
34
34
  all_rows << new_row
@@ -26,7 +26,7 @@ module ClassMetrix
26
26
  # Common options for all formatters
27
27
  title: nil,
28
28
  show_metadata: true,
29
- extraction_types: []
29
+ extraction_types: [] # : Array[Symbol]
30
30
  }
31
31
  end
32
32
 
@@ -52,11 +52,11 @@ module ClassMetrix
52
52
 
53
53
  def collect_all_hash_keys(rows, _headers)
54
54
  value_start_idx = value_start_index
55
- all_keys = {} # behavior_name => Set of keys
55
+ all_keys = {} # : Hash[String, Set[String]] # behavior_name => Set of keys
56
56
 
57
57
  rows.each do |row|
58
58
  behavior_name = row[behavior_column_index]
59
- values = row[value_start_idx..]
59
+ values = row[value_start_idx..] || []
60
60
 
61
61
  values.each do |value|
62
62
  if value.is_a?(Hash)
@@ -16,7 +16,7 @@ module ClassMetrix
16
16
  def generate
17
17
  return [] unless @show_footer
18
18
 
19
- output = []
19
+ output = [] # : Array[String]
20
20
 
21
21
  # Add separator line
22
22
  output << "---" if @show_separator
@@ -36,7 +36,7 @@ module ClassMetrix
36
36
  private
37
37
 
38
38
  def generate_default_footer
39
- output = []
39
+ output = [] # : Array[String]
40
40
 
41
41
  output << (@custom_footer || "*Report generated by ClassMetrix gem*")
42
42
 
@@ -49,7 +49,7 @@ module ClassMetrix
49
49
  end
50
50
 
51
51
  def generate_detailed_footer
52
- output = []
52
+ output = [] # : Array[String]
53
53
 
54
54
  output << "## Report Information"
55
55
  output << ""
@@ -30,7 +30,7 @@ module ClassMetrix
30
30
  private
31
31
 
32
32
  def generate_markdown_header
33
- output = []
33
+ output = [] # : Array[String]
34
34
 
35
35
  # Add title
36
36
  if @title
@@ -58,7 +58,7 @@ module ClassMetrix
58
58
  end
59
59
 
60
60
  def generate_csv_header
61
- output = []
61
+ output = [] # : Array[String]
62
62
  comment_char = @options.fetch(:comment_char, "#")
63
63
 
64
64
  # Add title as comment
@@ -15,7 +15,7 @@ module ClassMetrix
15
15
  end
16
16
 
17
17
  def generate
18
- output = []
18
+ output = [] # : Array[String]
19
19
 
20
20
  # Add title
21
21
  output.concat(generate_title) if @title || @show_metadata
@@ -32,7 +32,7 @@ module ClassMetrix
32
32
  private
33
33
 
34
34
  def generate_title
35
- output = []
35
+ output = [] # : Array[String]
36
36
 
37
37
  if @title
38
38
  output << "# #{@title}"
@@ -46,7 +46,7 @@ module ClassMetrix
46
46
  end
47
47
 
48
48
  def generate_classes_section
49
- output = []
49
+ output = [] # : Array[String]
50
50
 
51
51
  has_type_column = @data[:headers].first == "Type"
52
52
  class_headers = if has_type_column
@@ -66,7 +66,7 @@ module ClassMetrix
66
66
  end
67
67
 
68
68
  def generate_extraction_info
69
- output = []
69
+ output = [] # : Array[String]
70
70
 
71
71
  output << "## Extraction Types"
72
72
  output << ""
@@ -9,7 +9,7 @@ module ClassMetrix
9
9
  @options = options
10
10
  @show_missing_summary = options.fetch(:show_missing_summary, false)
11
11
  @summary_style = options.fetch(:summary_style, :grouped) # :grouped, :flat, :detailed
12
- @missing_behaviors = {}
12
+ @missing_behaviors = {} # : Hash[String, Hash[String, String]]
13
13
  end
14
14
 
15
15
  def generate
@@ -30,7 +30,7 @@ module ClassMetrix
30
30
  end
31
31
 
32
32
  # Initialize missing behaviors tracking with hash to store behavior name and error message
33
- class_headers.each { |class_name| @missing_behaviors[class_name] = {} }
33
+ class_headers.each { |class_name| @missing_behaviors[class_name] = {} } # : Hash[String, Hash[String, String]]
34
34
 
35
35
  @data[:rows].each do |row|
36
36
  behavior_name = has_type_column ? row[1] : row[0]
@@ -63,7 +63,7 @@ module ClassMetrix
63
63
  end
64
64
 
65
65
  def generate_grouped_summary
66
- output = []
66
+ output = [] # : Array[String]
67
67
 
68
68
  output << "## Missing Behaviors Summary"
69
69
  output << ""
@@ -84,12 +84,12 @@ module ClassMetrix
84
84
  end
85
85
 
86
86
  def generate_flat_summary
87
- output = []
87
+ output = [] # : Array[String]
88
88
 
89
89
  output << "## Missing Behaviors"
90
90
  output << ""
91
91
 
92
- all_missing = []
92
+ all_missing = [] # : Array[String]
93
93
  @missing_behaviors.each do |class_name, behaviors_hash|
94
94
  behaviors_hash.each do |behavior_name, error_message|
95
95
  all_missing << "- **#{class_name}**: `#{behavior_name}` - #{error_message}"
@@ -103,7 +103,7 @@ module ClassMetrix
103
103
  end
104
104
 
105
105
  def generate_detailed_summary
106
- output = []
106
+ output = [] # : Array[String]
107
107
 
108
108
  total_missing = @missing_behaviors.values.map(&:size).sum
109
109
  total_classes = @missing_behaviors.keys.size
@@ -114,7 +114,7 @@ module ClassMetrix
114
114
  output << ""
115
115
 
116
116
  # Group by error type
117
- by_error_type = {}
117
+ by_error_type = {} # : Hash[String, Array[Hash[Symbol, String]]]
118
118
  @missing_behaviors.each do |class_name, behaviors_hash|
119
119
  behaviors_hash.each do |behavior_name, error_message|
120
120
  error_type = error_message.split.first(2).join(" ") # e.g., "🚫 Not", "⚠️ Error:"
@@ -16,7 +16,8 @@ module ClassMetrix
16
16
  def process_simple_rows(rows)
17
17
  rows.map do |row|
18
18
  processed_row = [row[0]] # Keep the behavior name as-is
19
- row[1..].each do |value|
19
+ rest_values = row[1..] || []
20
+ rest_values.each do |value|
20
21
  processed_row << ValueProcessor.process(value)
21
22
  end
22
23
  processed_row
@@ -24,7 +25,7 @@ module ClassMetrix
24
25
  end
25
26
 
26
27
  def process_expanded_rows(rows)
27
- expanded_rows = []
28
+ expanded_rows = [] # : Array[Array[String]]
28
29
 
29
30
  rows.each do |row|
30
31
  if @data_extractor.row_has_expandable_hash?(row)
@@ -41,9 +42,11 @@ module ClassMetrix
41
42
 
42
43
  def process_non_hash_row(row)
43
44
  if @data_extractor.has_type_column?
44
- [row[0], row[1]] + row[2..].map { |value| ValueProcessor.process(value) }
45
+ rest_values = row[2..] || []
46
+ [row[0], row[1]] + rest_values.map { |value| ValueProcessor.process(value) }
45
47
  else
46
- [row[0]] + row[1..].map { |value| ValueProcessor.process(value) }
48
+ rest_values = row[1..] || []
49
+ [row[0]] + rest_values.map { |value| ValueProcessor.process(value) }
47
50
  end
48
51
  end
49
52
 
@@ -57,7 +60,7 @@ module ClassMetrix
57
60
  end
58
61
 
59
62
  def build_expanded_rows(row_data, all_hash_keys, original_row)
60
- expanded_rows = []
63
+ expanded_rows = [] # : Array[Array[String]]
61
64
 
62
65
  # Add main row if configured to show
63
66
  expanded_rows << build_main_expanded_row(row_data) if should_show_main_row?
@@ -37,7 +37,10 @@ module ClassMetrix
37
37
  end
38
38
 
39
39
  def row_has_expandable_hash?(row)
40
- row[value_start_index..].any? { |cell| cell.is_a?(Hash) }
40
+ values = row[value_start_index..] || []
41
+ return false if values.nil?
42
+
43
+ values.any? { |cell| cell.is_a?(Hash) }
41
44
  end
42
45
 
43
46
  def collect_hash_keys(values)
@@ -11,7 +11,7 @@ module ClassMetrix
11
11
  end
12
12
 
13
13
  def render_table(headers, rows, column_widths)
14
- output = []
14
+ output = [] # : Array[String]
15
15
  output << build_row(headers, column_widths)
16
16
  output << build_separator(column_widths)
17
17
 
@@ -19,7 +19,7 @@ module ClassMetrix
19
19
  output << build_row(row, column_widths)
20
20
  end
21
21
 
22
- output.join("\n")
22
+ output
23
23
  end
24
24
 
25
25
  private