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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +88 -0
  4. data/.tool-versions +1 -0
  5. data/CHANGELOG.md +41 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +417 -0
  8. data/RELEASE_GUIDE.md +158 -0
  9. data/Rakefile +12 -0
  10. data/examples/README.md +155 -0
  11. data/examples/advanced/error_handling.rb +199 -0
  12. data/examples/advanced/hash_expansion.rb +180 -0
  13. data/examples/basic/01_simple_constants.rb +56 -0
  14. data/examples/basic/02_simple_methods.rb +99 -0
  15. data/examples/basic/03_multi_type_extraction.rb +116 -0
  16. data/examples/components/configurable_reports.rb +201 -0
  17. data/examples/csv_output_demo.rb +237 -0
  18. data/examples/real_world/microservices_audit.rb +312 -0
  19. data/lib/class_metrix/extractor.rb +121 -0
  20. data/lib/class_metrix/extractors/constants_extractor.rb +87 -0
  21. data/lib/class_metrix/extractors/methods_extractor.rb +87 -0
  22. data/lib/class_metrix/extractors/multi_type_extractor.rb +66 -0
  23. data/lib/class_metrix/formatters/base/base_component.rb +62 -0
  24. data/lib/class_metrix/formatters/base/base_formatter.rb +93 -0
  25. data/lib/class_metrix/formatters/components/footer_component.rb +67 -0
  26. data/lib/class_metrix/formatters/components/generic_header_component.rb +87 -0
  27. data/lib/class_metrix/formatters/components/header_component.rb +92 -0
  28. data/lib/class_metrix/formatters/components/missing_behaviors_component.rb +140 -0
  29. data/lib/class_metrix/formatters/components/table_component.rb +268 -0
  30. data/lib/class_metrix/formatters/csv_formatter.rb +98 -0
  31. data/lib/class_metrix/formatters/markdown_formatter.rb +184 -0
  32. data/lib/class_metrix/formatters/shared/csv_table_builder.rb +21 -0
  33. data/lib/class_metrix/formatters/shared/markdown_table_builder.rb +97 -0
  34. data/lib/class_metrix/formatters/shared/table_builder.rb +267 -0
  35. data/lib/class_metrix/formatters/shared/value_processor.rb +78 -0
  36. data/lib/class_metrix/processors/value_processor.rb +40 -0
  37. data/lib/class_metrix/utils/class_resolver.rb +20 -0
  38. data/lib/class_metrix/version.rb +5 -0
  39. data/lib/class_metrix.rb +12 -0
  40. data/sig/class/metrix.rbs +6 -0
  41. metadata +118 -0
data/RELEASE_GUIDE.md ADDED
@@ -0,0 +1,158 @@
1
+ # Release Guide
2
+
3
+ This guide explains how to release new versions of the ClassMetrix gem.
4
+
5
+ ## 🚀 Quick Release
6
+
7
+ For a simple patch release:
8
+
9
+ ```bash
10
+ # Using the release script (recommended)
11
+ ./bin/release --type patch --push
12
+
13
+ # Or manually
14
+ ./bin/release --type patch
15
+ # Then follow the printed instructions to commit and push
16
+ ```
17
+
18
+ ## 📋 Prerequisites
19
+
20
+ Before creating a release, ensure you have:
21
+
22
+ 1. **RubyGems API Key**: Set up in GitHub repository secrets as `RUBYGEMS_API_KEY`
23
+ 2. **Clean working directory**: All changes committed
24
+ 3. **Tests passing**: Run `bundle exec rake` to verify
25
+ 4. **Updated documentation**: Ensure README and docs are current
26
+
27
+ ## 🛠️ Release Process
28
+
29
+ ### 1. Using the Release Script (Recommended)
30
+
31
+ The `bin/release` script automates version bumping and changelog updates:
32
+
33
+ ```bash
34
+ # Patch release (0.1.0 → 0.1.1)
35
+ ./bin/release --type patch
36
+
37
+ # Minor release (0.1.0 → 0.2.0)
38
+ ./bin/release --type minor
39
+
40
+ # Major release (0.1.0 → 1.0.0)
41
+ ./bin/release --type major
42
+
43
+ # Dry run to see what would happen
44
+ ./bin/release --type patch --dry-run
45
+
46
+ # Automatic commit and push
47
+ ./bin/release --type patch --push
48
+ ```
49
+
50
+ ### 2. Manual Release Process
51
+
52
+ If you prefer to do it manually:
53
+
54
+ 1. **Update Version**:
55
+ ```ruby
56
+ # lib/class_metrix/version.rb
57
+ module ClassMetrix
58
+ VERSION = "0.1.1" # Increment appropriately
59
+ end
60
+ ```
61
+
62
+ 2. **Update CHANGELOG.md**:
63
+ ```markdown
64
+ ## [Unreleased]
65
+
66
+ ## [0.1.1] - 2024-01-15
67
+ ### Fixed
68
+ - Bug fixes and improvements
69
+ ```
70
+
71
+ 3. **Commit and Tag**:
72
+ ```bash
73
+ git add -A
74
+ git commit -m "Release v0.1.1"
75
+ git tag v0.1.1
76
+ git push origin master
77
+ git push origin v0.1.1
78
+ ```
79
+
80
+ ## 🤖 Automated Release Workflow
81
+
82
+ When you push a tag (e.g., `v0.1.1`), GitHub Actions automatically:
83
+
84
+ 1. **Runs tests** across Ruby 3.1, 3.2, and 3.3
85
+ 2. **Verifies version consistency** between tag and gemspec
86
+ 3. **Builds the gem**
87
+ 4. **Publishes to RubyGems**
88
+ 5. **Creates a GitHub Release**
89
+
90
+ ### Workflow Files
91
+
92
+ - **`.github/workflows/main.yml`**: CI pipeline for PRs and pushes
93
+ - **`.github/workflows/release.yml`**: Release automation for tags
94
+
95
+ ## 🔑 Setting Up RubyGems API Key
96
+
97
+ 1. **Generate API Key**:
98
+ ```bash
99
+ gem signin
100
+ # Get your API key from ~/.gem/credentials
101
+ ```
102
+
103
+ 2. **Add to GitHub Secrets**:
104
+ - Go to your repo → Settings → Secrets and variables → Actions
105
+ - Add secret named `RUBYGEMS_API_KEY`
106
+ - Paste your API key as the value
107
+
108
+ ## 📊 Release Checklist
109
+
110
+ Before each release:
111
+
112
+ - [ ] All tests pass (`bundle exec rake`)
113
+ - [ ] RuboCop passes (`bundle exec rubocop`)
114
+ - [ ] Documentation is updated
115
+ - [ ] CHANGELOG.md reflects new changes
116
+ - [ ] Version is bumped appropriately
117
+ - [ ] No TODO items in gemspec
118
+ - [ ] GitHub secrets are configured
119
+
120
+ ## 🐛 Troubleshooting
121
+
122
+ ### Version Mismatch Error
123
+
124
+ If the workflow fails with a version mismatch:
125
+
126
+ ```
127
+ Version mismatch! Gemspec: 0.1.0, Tag: 0.1.1
128
+ ```
129
+
130
+ Ensure the version in `lib/class_metrix/version.rb` matches your git tag.
131
+
132
+ ### RubyGems Push Failed
133
+
134
+ Check that:
135
+ 1. `RUBYGEMS_API_KEY` secret is set correctly
136
+ 2. You have push permissions for the gem
137
+ 3. The version doesn't already exist on RubyGems
138
+
139
+ ### GitHub Release Failed
140
+
141
+ Ensure the `GITHUB_TOKEN` has sufficient permissions (this is usually automatic).
142
+
143
+ ## 📈 Semantic Versioning
144
+
145
+ This project follows [Semantic Versioning](https://semver.org/):
146
+
147
+ - **PATCH** (`0.1.0` → `0.1.1`): Bug fixes, documentation updates
148
+ - **MINOR** (`0.1.0` → `0.2.0`): New features, backwards compatible
149
+ - **MAJOR** (`0.1.0` → `1.0.0`): Breaking changes
150
+
151
+ ## 🎯 Post-Release
152
+
153
+ After a successful release:
154
+
155
+ 1. **Verify on RubyGems**: Check that the new version appears on [rubygems.org](https://rubygems.org/gems/class-metrix)
156
+ 2. **Test installation**: `gem install class-metrix -v X.X.X`
157
+ 3. **Update dependencies**: In projects using the gem
158
+ 4. **Announce**: Consider announcements on relevant channels
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,155 @@
1
+ # ClassMetrix Examples
2
+
3
+ This directory contains comprehensive examples demonstrating all features of the ClassMetrix gem, organized from basic to advanced real-world scenarios.
4
+
5
+ ## 🎯 Quick Start
6
+
7
+ If you're new to ClassMetrix, start with the basic examples:
8
+
9
+ ```bash
10
+ # Run basic examples in order
11
+ ruby examples/basic/01_simple_constants.rb
12
+ ruby examples/basic/02_simple_methods.rb
13
+ ruby examples/basic/03_multi_type_extraction.rb
14
+ ```
15
+
16
+ ## 📁 Directory Structure
17
+
18
+ ### `/basic/` - Getting Started
19
+ Perfect for learning the core concepts and API.
20
+
21
+ - **`01_simple_constants.rb`** - Extract and compare constants across classes
22
+ - Basic constant extraction
23
+ - Filtering with regex and strings
24
+ - Understanding the output format
25
+
26
+ - **`02_simple_methods.rb`** - Extract and compare class methods
27
+ - Class method extraction
28
+ - Handling different return types (strings, numbers, booleans, arrays)
29
+ - Method filtering techniques
30
+
31
+ - **`03_multi_type_extraction.rb`** - Combine constants and methods in one table
32
+ - Multi-type extraction with Type column
33
+ - Comparing constants vs methods with similar names
34
+ - Understanding behavior differences
35
+
36
+ ### `/advanced/` - Powerful Features
37
+ Advanced features for complex scenarios.
38
+
39
+ - **`hash_expansion.rb`** - Hash value expansion into sub-rows
40
+ - Normal vs expanded hash display
41
+ - Hash key comparison across classes
42
+ - Mixed data type handling
43
+ - Multi-type extraction with expansion
44
+
45
+ - **`error_handling.rb`** - Graceful error handling
46
+ - Methods that raise exceptions
47
+ - Missing constants and methods
48
+ - Boolean and nil value processing
49
+ - Error indicators and recovery
50
+
51
+ ### `/real_world/` - Production Examples
52
+ Real-world scenarios you might encounter.
53
+
54
+ - **`microservices_audit.rb`** - Complete microservices configuration audit
55
+ - Service metadata comparison
56
+ - Feature flag consistency analysis
57
+ - Database and cache configuration audits
58
+ - Performance metrics comparison
59
+ - Security configuration analysis
60
+
61
+ ## 🚀 Running Examples
62
+
63
+ Each example is self-contained and can be run independently:
64
+
65
+ ```bash
66
+ # Run any example directly
67
+ ruby examples/basic/01_simple_constants.rb
68
+ ruby examples/advanced/hash_expansion.rb
69
+ ruby examples/real_world/microservices_audit.rb
70
+ ```
71
+
72
+ ## 📊 Example Output
73
+
74
+ All examples generate markdown tables that look like this:
75
+
76
+ ```markdown
77
+ | Constant | UserService | OrderService | PaymentService |
78
+ |--------------|-------------|--------------|----------------|
79
+ | SERVICE_NAME | user-service| order-service| payment-service|
80
+ | VERSION | 2.1.0 | 1.8.5 | 3.0.1 |
81
+ | PORT | 8001 | 8002 | 8003 |
82
+ ```
83
+
84
+ With hash expansion:
85
+ ```markdown
86
+ | Method | UserService | OrderService | PaymentService |
87
+ |----------------|-------------|--------------|----------------|
88
+ | database_config| 📋 7 keys | 📋 8 keys | 📋 8 keys |
89
+ | └─ host | user-db... | order-db... | payment-db... |
90
+ | └─ port | 5432 | 5432 | 5432 |
91
+ | └─ ssl | true | true | true |
92
+ ```
93
+
94
+ ## 🛠️ Building Your Own Examples
95
+
96
+ Use these examples as templates for your own analysis:
97
+
98
+ ```ruby
99
+ require_relative "../lib/class_metrix"
100
+
101
+ # Define your classes...
102
+ class MyService
103
+ CONFIG_SETTING = "value"
104
+
105
+ def self.some_method
106
+ "result"
107
+ end
108
+ end
109
+
110
+ # Extract and compare
111
+ result = ClassMetrix.extract(:constants, :class_methods)
112
+ .from([MyService, AnotherService])
113
+ .filter(/CONFIG|method/)
114
+ .expand_hashes
115
+ .handle_errors
116
+ .to_markdown("my_analysis.md")
117
+
118
+ puts result
119
+ ```
120
+
121
+ ## 📝 Generated Reports
122
+
123
+ Many examples save their output to markdown files:
124
+
125
+ - `config_analysis_expanded.md` - Hash expansion demo output
126
+ - `error_analysis_report.md` - Error handling demo output
127
+ - `microservices_audit_report.md` - Complete microservices audit
128
+ - `service_analysis_report.md` - Service comparison report
129
+
130
+ ## 🎓 Learning Path
131
+
132
+ 1. **Start with Basic Examples** - Learn the core API and concepts
133
+ 2. **Try Advanced Features** - Explore hash expansion and error handling
134
+ 3. **Study Real-World Examples** - See practical applications
135
+ 4. **Build Your Own** - Apply ClassMetrix to your specific use cases
136
+
137
+ ## 💡 Use Case Ideas
138
+
139
+ - **Configuration Management**: Compare settings across environments
140
+ - **API Analysis**: Compare method signatures and return types
141
+ - **Feature Flag Audits**: Ensure consistency across services
142
+ - **Class Architecture Review**: Compare similar classes for consistency
143
+ - **Microservices Governance**: Standardize configurations across services
144
+ - **Legacy Code Analysis**: Understand differences between old and new implementations
145
+
146
+ ## 🤝 Contributing Examples
147
+
148
+ Have a great ClassMetrix use case? Consider contributing an example!
149
+
150
+ 1. Create a new file in the appropriate directory
151
+ 2. Follow the existing example structure
152
+ 3. Include clear comments and output explanations
153
+ 4. Add an entry to this README
154
+
155
+ Happy analyzing! 🎉
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../../lib/class_metrix"
5
+
6
+ puts "=== Advanced Example: Error Handling ==="
7
+ puts
8
+
9
+ # Classes that demonstrate various error scenarios
10
+ class WorkingService
11
+ # Working constants
12
+ SERVICE_NAME = "working"
13
+ VERSION = "1.0.0"
14
+ ENABLED = true
15
+
16
+ # Working methods
17
+ def self.status
18
+ "healthy"
19
+ end
20
+
21
+ def self.version
22
+ "1.0.0"
23
+ end
24
+
25
+ def self.health_check
26
+ { status: "ok", uptime: 12_345 }
27
+ end
28
+
29
+ def self.enabled?
30
+ true
31
+ end
32
+ end
33
+
34
+ class PartiallyBrokenService
35
+ # Working constants
36
+ SERVICE_NAME = "partially_broken"
37
+ VERSION = "0.9.0"
38
+ # Missing ENABLED constant
39
+
40
+ # Working methods
41
+ def self.status
42
+ "degraded"
43
+ end
44
+
45
+ def self.version
46
+ "0.9.0"
47
+ end
48
+
49
+ # Broken method that raises an error
50
+ def self.health_check
51
+ raise StandardError, "Health check failed!"
52
+ end
53
+
54
+ def self.enabled?
55
+ false
56
+ end
57
+
58
+ # Method that returns nil
59
+ def self.uptime
60
+ nil
61
+ end
62
+
63
+ # Method with complex error
64
+ def self.database_connection
65
+ raise ActiveRecord::ConnectionNotEstablished, "Database unavailable"
66
+ rescue NameError
67
+ # If ActiveRecord is not available, raise a different error
68
+ raise "Connection library not available"
69
+ end
70
+ end
71
+
72
+ class CompletelyBrokenService
73
+ # Has some constants
74
+ SERVICE_NAME = "broken"
75
+ # Missing VERSION and ENABLED
76
+
77
+ # Has working method
78
+ def self.status
79
+ "down"
80
+ end
81
+
82
+ # Missing version method
83
+
84
+ # Broken health_check
85
+ def self.health_check
86
+ raise NoMethodError, "Health monitoring not implemented"
87
+ end
88
+
89
+ # Method that raises during execution
90
+ def self.enabled?
91
+ some_undefined_variable.call
92
+ end
93
+
94
+ # Method that returns false
95
+ def self.deprecated?
96
+ true
97
+ end
98
+ end
99
+
100
+ puts "=== Service Classes Defined ==="
101
+ puts "WorkingService: All methods work"
102
+ puts "PartiallyBrokenService: Some methods fail"
103
+ puts "CompletelyBrokenService: Most methods fail"
104
+ puts
105
+
106
+ # 1. Extract without error handling (will raise errors)
107
+ puts "💥 1. WITHOUT ERROR HANDLING (This will show errors)"
108
+ puts "-" * 60
109
+
110
+ begin
111
+ result = ClassMetrix.extract(:class_methods)
112
+ .from([WorkingService, PartiallyBrokenService])
113
+ .filter(/health_check/)
114
+ .to_markdown
115
+ puts result
116
+ rescue StandardError => e
117
+ puts "❌ Error occurred: #{e.class.name}: #{e.message}"
118
+ end
119
+ puts
120
+
121
+ # 2. Extract with error handling
122
+ puts "🛡️ 2. WITH ERROR HANDLING (Graceful failure)"
123
+ puts "-" * 60
124
+ result = ClassMetrix.extract(:class_methods)
125
+ .from([WorkingService, PartiallyBrokenService, CompletelyBrokenService])
126
+ .handle_errors
127
+ .to_markdown
128
+
129
+ puts result
130
+ puts
131
+
132
+ # 3. Constants with missing values
133
+ puts "📋 3. CONSTANTS WITH MISSING VALUES"
134
+ puts "-" * 60
135
+ result = ClassMetrix.extract(:constants)
136
+ .from([WorkingService, PartiallyBrokenService, CompletelyBrokenService])
137
+ .handle_errors
138
+ .to_markdown
139
+
140
+ puts result
141
+ puts
142
+
143
+ # 4. Multi-type extraction with errors
144
+ puts "📊 4. MULTI-TYPE WITH ERROR HANDLING"
145
+ puts "-" * 60
146
+ result = ClassMetrix.extract(:constants, :class_methods)
147
+ .from([WorkingService, PartiallyBrokenService, CompletelyBrokenService])
148
+ .filter(/SERVICE_NAME|status|enabled/)
149
+ .handle_errors
150
+ .to_markdown
151
+
152
+ puts result
153
+ puts
154
+
155
+ # 5. Focus on problematic methods
156
+ puts "🔍 5. PROBLEMATIC METHODS ANALYSIS"
157
+ puts "-" * 60
158
+ result = ClassMetrix.extract(:class_methods)
159
+ .from([WorkingService, PartiallyBrokenService, CompletelyBrokenService])
160
+ .filter(/health_check|database|uptime/)
161
+ .handle_errors
162
+ .to_markdown
163
+
164
+ puts result
165
+ puts
166
+
167
+ # 6. Boolean and nil value handling
168
+ puts "✅ 6. BOOLEAN AND NIL VALUE HANDLING"
169
+ puts "-" * 60
170
+ result = ClassMetrix.extract(:class_methods)
171
+ .from([WorkingService, PartiallyBrokenService, CompletelyBrokenService])
172
+ .filter(/enabled\?|deprecated\?|uptime/)
173
+ .handle_errors
174
+ .to_markdown
175
+
176
+ puts result
177
+ puts
178
+
179
+ # 7. Save comprehensive error analysis report
180
+ puts "💾 7. SAVING COMPREHENSIVE ERROR ANALYSIS REPORT"
181
+ puts "-" * 60
182
+ report = ClassMetrix.extract(:constants, :class_methods)
183
+ .from([WorkingService, PartiallyBrokenService, CompletelyBrokenService])
184
+ .handle_errors
185
+ .to_markdown("error_analysis_report.md", title: "Service Health Analysis Report")
186
+
187
+ puts "✅ Comprehensive error analysis saved to: error_analysis_report.md"
188
+ puts "📊 Report contains #{report.lines.count} lines with rich metadata"
189
+ puts
190
+
191
+ puts "🛡️ Enhanced Error Handling Features Demonstrated:"
192
+ puts "• 🚫 Missing constants: '🚫 Not defined'"
193
+ puts "• 🚫 Missing methods: '🚫 No method'"
194
+ puts "• ⚠️ Runtime errors: '⚠️ Error: [descriptive message]'"
195
+ puts "• ❌ Boolean false and nil values: '❌'"
196
+ puts "• ✅ Boolean true values: '✅'"
197
+ puts "• 📊 Rich markdown reports with titles and metadata"
198
+ puts "• 📋 Missing behaviors summary per class"
199
+ puts "• 🛡️ handle_errors flag enables graceful degradation"
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../../lib/class_metrix"
5
+
6
+ puts "=== Advanced Example: Hash Expansion ==="
7
+ puts
8
+
9
+ # Configuration classes with complex hash structures
10
+ class DatabaseConfig
11
+ # Hash constants
12
+ DEFAULT_CONFIG = {
13
+ host: "localhost",
14
+ port: 5432,
15
+ ssl: true,
16
+ pool_size: 5
17
+ }.freeze
18
+
19
+ CONNECTION_POOLS = {
20
+ read: { size: 10, timeout: 30 },
21
+ write: { size: 5, timeout: 15 },
22
+ admin: { size: 2, timeout: 60 }
23
+ }.freeze
24
+
25
+ # Hash methods
26
+ def self.production_config
27
+ {
28
+ host: "db.production.com",
29
+ port: 5432,
30
+ database: "myapp_production",
31
+ ssl: true,
32
+ pool_size: 20,
33
+ timeout: 30,
34
+ backup_enabled: true
35
+ }
36
+ end
37
+
38
+ def self.development_config
39
+ {
40
+ host: "localhost",
41
+ port: 5432,
42
+ database: "myapp_development",
43
+ ssl: false,
44
+ pool_size: 5,
45
+ timeout: 10
46
+ }
47
+ end
48
+
49
+ def self.simple_value
50
+ "not a hash"
51
+ end
52
+ end
53
+
54
+ class RedisConfig
55
+ # Hash constants
56
+ DEFAULT_CONFIG = {
57
+ host: "localhost",
58
+ port: 6379,
59
+ ssl: false,
60
+ timeout: 5
61
+ }.freeze
62
+
63
+ CLUSTER_CONFIG = {
64
+ nodes: 3,
65
+ replication: true,
66
+ failover: "auto"
67
+ }.freeze
68
+
69
+ # Hash methods
70
+ def self.production_config
71
+ {
72
+ host: "redis.internal",
73
+ port: 6379,
74
+ database: 0,
75
+ ssl: false,
76
+ max_connections: 100,
77
+ cluster_enabled: true
78
+ }
79
+ end
80
+
81
+ def self.development_config
82
+ {
83
+ host: "localhost",
84
+ port: 6379,
85
+ database: 1,
86
+ ssl: false,
87
+ max_connections: 10
88
+ }
89
+ end
90
+
91
+ def self.simple_value
92
+ 42
93
+ end
94
+ end
95
+
96
+ puts "=== Configuration Classes Defined ==="
97
+ puts "DatabaseConfig and RedisConfig with hash constants and methods"
98
+ puts
99
+
100
+ # 1. Normal output (without expansion)
101
+ puts "📋 1. NORMAL OUTPUT (Hashes as inspect strings)"
102
+ puts "-" * 60
103
+ result = ClassMetrix.extract(:class_methods)
104
+ .from([DatabaseConfig, RedisConfig])
105
+ .filter(/config$/)
106
+ .to_markdown
107
+
108
+ puts result
109
+ puts
110
+
111
+ # 2. Expanded output
112
+ puts "🔍 2. EXPANDED OUTPUT (Hash keys as sub-rows)"
113
+ puts "-" * 60
114
+ result = ClassMetrix.extract(:class_methods)
115
+ .from([DatabaseConfig, RedisConfig])
116
+ .filter(/config$/)
117
+ .expand_hashes
118
+ .to_markdown
119
+
120
+ puts result
121
+ puts
122
+
123
+ # 3. Constants with expansion
124
+ puts "📊 3. CONSTANTS WITH HASH EXPANSION"
125
+ puts "-" * 60
126
+ result = ClassMetrix.extract(:constants)
127
+ .from([DatabaseConfig, RedisConfig])
128
+ .filter(/CONFIG/)
129
+ .expand_hashes
130
+ .handle_errors
131
+ .to_markdown
132
+
133
+ puts result
134
+ puts
135
+
136
+ # 4. Mixed data types with expansion
137
+ puts "🔧 4. MIXED DATA TYPES WITH EXPANSION"
138
+ puts "-" * 60
139
+ result = ClassMetrix.extract(:class_methods)
140
+ .from([DatabaseConfig, RedisConfig])
141
+ .expand_hashes
142
+ .to_markdown
143
+
144
+ puts result
145
+ puts
146
+
147
+ # 5. Multi-type extraction with expansion
148
+ puts "🚀 5. MULTI-TYPE WITH HASH EXPANSION"
149
+ puts "-" * 60
150
+ result = ClassMetrix.extract(:constants, :class_methods)
151
+ .from([DatabaseConfig, RedisConfig])
152
+ .filter(/DEFAULT_CONFIG|production_config/)
153
+ .expand_hashes
154
+ .handle_errors
155
+ .to_markdown
156
+
157
+ puts result
158
+ puts
159
+
160
+ # 6. Save expanded report
161
+ puts "💾 6. SAVING EXPANDED REPORT"
162
+ puts "-" * 60
163
+ report = ClassMetrix.extract(:constants, :class_methods)
164
+ .from([DatabaseConfig, RedisConfig])
165
+ .expand_hashes
166
+ .handle_errors
167
+ .to_markdown("config_analysis_expanded.md")
168
+
169
+ puts "✅ Expanded report saved to: config_analysis_expanded.md"
170
+ puts "📊 Report contains #{report.lines.count} lines"
171
+ puts
172
+
173
+ puts "✨ Hash Expansion Features Demonstrated:"
174
+ puts "• 📋 Hash indicators show number of keys"
175
+ puts "• └─ Sub-rows show individual key-value pairs"
176
+ puts "• ❌ Missing keys shown clearly"
177
+ puts "• — Non-hash values shown with dash"
178
+ puts "• Mixed data types handled properly"
179
+ puts "• Works with both constants and methods"
180
+ puts "• Compatible with multi-type extraction"