decode 0.22.0 → 0.23.0
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
- checksums.yaml.gz.sig +0 -0
- data/bake/decode/index.rb +16 -9
- data/context/coverage.md +325 -0
- data/context/getting-started.md +242 -0
- data/context/ruby-documentation.md +363 -0
- data/lib/decode/comment/attribute.rb +9 -3
- data/lib/decode/comment/node.rb +4 -2
- data/lib/decode/comment/option.rb +1 -1
- data/lib/decode/comment/parameter.rb +12 -6
- data/lib/decode/comment/pragma.rb +12 -1
- data/lib/decode/comment/raises.rb +1 -1
- data/lib/decode/comment/returns.rb +3 -4
- data/lib/decode/comment/tag.rb +13 -3
- data/lib/decode/comment/tags.rb +17 -2
- data/lib/decode/comment/text.rb +4 -1
- data/lib/decode/comment/throws.rb +1 -1
- data/lib/decode/comment/yields.rb +7 -1
- data/lib/decode/definition.rb +54 -42
- data/lib/decode/documentation.rb +12 -14
- data/lib/decode/index.rb +29 -14
- data/lib/decode/language/generic.rb +30 -14
- data/lib/decode/language/reference.rb +13 -4
- data/lib/decode/language/ruby/alias.rb +41 -0
- data/lib/decode/language/ruby/attribute.rb +7 -6
- data/lib/decode/language/ruby/block.rb +4 -1
- data/lib/decode/language/ruby/call.rb +16 -6
- data/lib/decode/language/ruby/class.rb +19 -36
- data/lib/decode/language/ruby/code.rb +27 -15
- data/lib/decode/language/ruby/constant.rb +9 -8
- data/lib/decode/language/ruby/definition.rb +27 -19
- data/lib/decode/language/ruby/function.rb +2 -1
- data/lib/decode/language/ruby/generic.rb +17 -7
- data/lib/decode/language/ruby/method.rb +47 -12
- data/lib/decode/language/ruby/module.rb +4 -11
- data/lib/decode/language/ruby/parser.rb +358 -207
- data/lib/decode/language/ruby/reference.rb +26 -17
- data/lib/decode/language/ruby/segment.rb +11 -4
- data/lib/decode/language/ruby.rb +4 -2
- data/lib/decode/language.rb +2 -2
- data/lib/decode/languages.rb +25 -6
- data/lib/decode/location.rb +2 -0
- data/lib/decode/scope.rb +1 -1
- data/lib/decode/segment.rb +6 -5
- data/lib/decode/source.rb +12 -4
- data/lib/decode/syntax/link.rb +9 -1
- data/lib/decode/syntax/match.rb +12 -0
- data/lib/decode/syntax/rewriter.rb +10 -0
- data/lib/decode/trie.rb +27 -22
- data/lib/decode/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +9 -10
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddd2b15f3019877bc34174d6d75e65d435431d6b4642fe6806c01cb44a154d20
|
4
|
+
data.tar.gz: 99d6be44ed79584ba17a8ed8479c976890aac2a621d4be6ac0f0ab3edef86c9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6015f4796c39f6c187fdf42fc872da64d85f4472ed1a641c74255a6a39c5cf411b30e31d55eabb8211c09d0d04030eabc23e1cd850d6ecdb92c50e9c72e25388
|
7
|
+
data.tar.gz: 32bb1229c0b4dca21f0334b13c2742dc3b5963f8252da77377b2687322ee94c60b8a2b313df410e113f1865bd48b65be05b3756f968de77de27d238d54001f27
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/bake/decode/index.rb
CHANGED
@@ -6,8 +6,8 @@
|
|
6
6
|
def initialize(...)
|
7
7
|
super
|
8
8
|
|
9
|
-
require
|
10
|
-
require
|
9
|
+
require "decode/index"
|
10
|
+
require "set"
|
11
11
|
end
|
12
12
|
|
13
13
|
# Process the given source root and report on comment coverage.
|
@@ -19,7 +19,7 @@ def coverage(root)
|
|
19
19
|
index.update(paths)
|
20
20
|
|
21
21
|
documented = Set.new
|
22
|
-
missing =
|
22
|
+
missing = {}
|
23
23
|
|
24
24
|
index.trie.traverse do |path, node, descend|
|
25
25
|
public_definition = node.values.nil?
|
@@ -28,10 +28,10 @@ def coverage(root)
|
|
28
28
|
if definition.public?
|
29
29
|
level = path.size
|
30
30
|
|
31
|
-
if definition.
|
32
|
-
missing << definition.qualified_name
|
33
|
-
else
|
31
|
+
if definition.documented?
|
34
32
|
documented << definition.qualified_name
|
33
|
+
else
|
34
|
+
missing[definition.qualified_name] ||= definition
|
35
35
|
end
|
36
36
|
|
37
37
|
public_definition = true
|
@@ -45,7 +45,9 @@ def coverage(root)
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Since there can be multiple definitions for a given symbol, we can ignore any missing definitions that have been documented elsewhere:
|
48
|
-
|
48
|
+
documented.each do |qualified_name|
|
49
|
+
missing.delete(qualified_name)
|
50
|
+
end
|
49
51
|
|
50
52
|
documented_count = documented.size
|
51
53
|
public_count = documented_count + missing.size
|
@@ -53,8 +55,13 @@ def coverage(root)
|
|
53
55
|
|
54
56
|
if documented_count < public_count
|
55
57
|
$stderr.puts nil, "Missing documentation for:"
|
56
|
-
missing.each do |
|
57
|
-
|
58
|
+
missing.each do |qualified_name, definition|
|
59
|
+
location = definition.location
|
60
|
+
if location
|
61
|
+
$stderr.puts "- #{qualified_name} (#{location.path}:#{location.line})"
|
62
|
+
else
|
63
|
+
$stderr.puts "- #{qualified_name}"
|
64
|
+
end
|
58
65
|
end
|
59
66
|
|
60
67
|
raise RuntimeError, "Insufficient documentation!"
|
data/context/coverage.md
ADDED
@@ -0,0 +1,325 @@
|
|
1
|
+
# Documentation Coverage
|
2
|
+
|
3
|
+
This guide explains how to test and monitor documentation coverage in your Ruby projects using the Decode gem's built-in bake tasks.
|
4
|
+
|
5
|
+
## Available Bake Tasks
|
6
|
+
|
7
|
+
The Decode gem provides several bake tasks for analyzing your codebase:
|
8
|
+
|
9
|
+
- `bake decode:index:coverage` - Check documentation coverage.
|
10
|
+
- `bake decode:index:symbols` - List all symbols in the codebase.
|
11
|
+
- `bake decode:index:documentation` - Extract all documentation.
|
12
|
+
|
13
|
+
## Checking Documentation Coverage
|
14
|
+
|
15
|
+
### Basic Coverage Check
|
16
|
+
|
17
|
+
```bash
|
18
|
+
# Check coverage for the lib directory:
|
19
|
+
bake decode:index:coverage lib
|
20
|
+
|
21
|
+
# Check coverage for a specific directory:
|
22
|
+
bake decode:index:coverage app/models
|
23
|
+
```
|
24
|
+
|
25
|
+
### Understanding Coverage Output
|
26
|
+
|
27
|
+
When you run the coverage command, you'll see output like:
|
28
|
+
|
29
|
+
```
|
30
|
+
15 definitions have documentation, out of 20 public definitions.
|
31
|
+
|
32
|
+
Missing documentation for:
|
33
|
+
- MyGem::SomeClass#method_name
|
34
|
+
- MyGem::AnotherClass
|
35
|
+
- MyGem::Utility.helper_method
|
36
|
+
```
|
37
|
+
|
38
|
+
The coverage check:
|
39
|
+
- **Counts only public definitions** (public methods, classes, modules).
|
40
|
+
- **Reports the ratio** of documented vs total public definitions.
|
41
|
+
- **Lists missing documentation** by qualified name.
|
42
|
+
- **Fails with an error** if coverage is incomplete.
|
43
|
+
|
44
|
+
### What Counts as "Documented"
|
45
|
+
|
46
|
+
A definition is considered documented if it has:
|
47
|
+
- Any comment preceding it.
|
48
|
+
- Documentation pragmas (like `@parameter`, `@returns`).
|
49
|
+
- A `@namespace` pragma (for organizational modules).
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Represents a user in the system.
|
53
|
+
class MyClass
|
54
|
+
end
|
55
|
+
|
56
|
+
# @namespace
|
57
|
+
module OrganizationalModule
|
58
|
+
# Contains helper functionality.
|
59
|
+
end
|
60
|
+
|
61
|
+
# Process user data and return formatted results.
|
62
|
+
# @parameter name [String] The user's name.
|
63
|
+
# @returns [Boolean] Success status.
|
64
|
+
def process(name)
|
65
|
+
# Validation logic here:
|
66
|
+
return false if name.empty?
|
67
|
+
|
68
|
+
# Processing logic:
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
class UndocumentedClass
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
## Analyzing Symbols
|
77
|
+
|
78
|
+
### List All Symbols
|
79
|
+
|
80
|
+
```bash
|
81
|
+
# See the structure of your codebase
|
82
|
+
bake decode:index:symbols lib
|
83
|
+
```
|
84
|
+
|
85
|
+
This shows the hierarchical structure of your code:
|
86
|
+
|
87
|
+
```
|
88
|
+
[] -> []
|
89
|
+
["MyGem"] -> [#<Decode::Language::Ruby::Module:...>]
|
90
|
+
MyGem
|
91
|
+
["MyGem", "User"] -> [#<Decode::Language::Ruby::Class:...>]
|
92
|
+
MyGem::User
|
93
|
+
["MyGem", "User", "initialize"] -> [#<Decode::Language::Ruby::Method:...>]
|
94
|
+
MyGem::User#initialize
|
95
|
+
```
|
96
|
+
|
97
|
+
### Extract Documentation
|
98
|
+
|
99
|
+
```bash
|
100
|
+
# Extract all documentation from your codebase
|
101
|
+
bake decode:index:documentation lib
|
102
|
+
```
|
103
|
+
|
104
|
+
This outputs formatted documentation for all documented definitions:
|
105
|
+
|
106
|
+
~~~markdown
|
107
|
+
## `MyGem::User#initialize`
|
108
|
+
|
109
|
+
Initialize a new user with the given email address.
|
110
|
+
|
111
|
+
## `MyGem::User#authenticate`
|
112
|
+
|
113
|
+
Authenticate the user with a password.
|
114
|
+
Returns true if authentication is successful.
|
115
|
+
~~~
|
116
|
+
|
117
|
+
## Achieving 100% Coverage
|
118
|
+
|
119
|
+
### Strategy for Complete Coverage
|
120
|
+
|
121
|
+
1. **Document all public APIs**
|
122
|
+
```ruby
|
123
|
+
# Represents a user management system.
|
124
|
+
class User
|
125
|
+
# @attribute [String] The user's email address.
|
126
|
+
attr_reader :email
|
127
|
+
|
128
|
+
# Initialize a new user.
|
129
|
+
# @parameter email [String] The user's email address.
|
130
|
+
def initialize(email)
|
131
|
+
# Store the email address:
|
132
|
+
@email = email
|
133
|
+
end
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
2. **Use @namespace for organizational modules**
|
138
|
+
```ruby
|
139
|
+
# @namespace
|
140
|
+
module MyGem
|
141
|
+
# Contains the main functionality.
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
3. **Document edge cases**
|
146
|
+
```ruby
|
147
|
+
# @private
|
148
|
+
def internal_helper
|
149
|
+
# Internal implementation details.
|
150
|
+
end
|
151
|
+
```
|
152
|
+
|
153
|
+
### Common Coverage Issues
|
154
|
+
|
155
|
+
**Issue: Missing namespace documentation**
|
156
|
+
```ruby
|
157
|
+
# This module has no documentation and will show as missing coverage:
|
158
|
+
module MyGem
|
159
|
+
end
|
160
|
+
|
161
|
+
# Solution: Add @namespace pragma:
|
162
|
+
# @namespace
|
163
|
+
module MyGem
|
164
|
+
# Provides core functionality.
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
**Issue: Undocumented methods**
|
169
|
+
|
170
|
+
Problem: Methods without any comments will show as missing coverage:
|
171
|
+
```ruby
|
172
|
+
def process_data
|
173
|
+
# Implementation here
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
Solution: Add description and pragmas:
|
178
|
+
```ruby
|
179
|
+
# Process the input data and return results.
|
180
|
+
# @parameter data [Hash] Input data to process.
|
181
|
+
# @returns [Array] Processed results.
|
182
|
+
def process_data(data)
|
183
|
+
# Process the input:
|
184
|
+
results = data.map { |item| transform(item) }
|
185
|
+
|
186
|
+
# Return processed results:
|
187
|
+
results
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
**Issue: Missing attr documentation**
|
192
|
+
|
193
|
+
Problem: Attributes without documentation will show as missing coverage:
|
194
|
+
```ruby
|
195
|
+
attr_reader :name
|
196
|
+
```
|
197
|
+
|
198
|
+
Solution: Document with @attribute pragma:
|
199
|
+
```ruby
|
200
|
+
# @attribute [String] The user's full name.
|
201
|
+
attr_reader :name
|
202
|
+
```
|
203
|
+
|
204
|
+
## Integrating into CI/CD
|
205
|
+
|
206
|
+
### GitHub Actions Example
|
207
|
+
|
208
|
+
```yaml
|
209
|
+
name: Documentation Coverage
|
210
|
+
|
211
|
+
on: [push, pull_request]
|
212
|
+
|
213
|
+
jobs:
|
214
|
+
docs:
|
215
|
+
runs-on: ubuntu-latest
|
216
|
+
steps:
|
217
|
+
- uses: actions/checkout@v3
|
218
|
+
- uses: ruby/setup-ruby@v1
|
219
|
+
with:
|
220
|
+
bundler-cache: true
|
221
|
+
- name: Check documentation coverage
|
222
|
+
run: bake decode:index:coverage lib
|
223
|
+
```
|
224
|
+
|
225
|
+
### Rake Task Integration
|
226
|
+
|
227
|
+
Add to your `Rakefile`:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
require 'decode'
|
231
|
+
|
232
|
+
desc "Check documentation coverage"
|
233
|
+
task :doc_coverage do
|
234
|
+
system("bake decode:index:coverage lib") || exit(1)
|
235
|
+
end
|
236
|
+
|
237
|
+
task default: [:test, :doc_coverage]
|
238
|
+
```
|
239
|
+
|
240
|
+
## Monitoring Coverage Over Time
|
241
|
+
|
242
|
+
### Generate Coverage Reports
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
# Generate a coverage percentage for the specified directory.
|
246
|
+
# @parameter root [String] The root directory to analyze.
|
247
|
+
# @returns [Float] The coverage percentage.
|
248
|
+
def coverage_percentage(root)
|
249
|
+
index = Decode::Index.new
|
250
|
+
index.update(Dir.glob(File.join(root, "**/*.rb")))
|
251
|
+
|
252
|
+
documented = 0
|
253
|
+
total = 0
|
254
|
+
|
255
|
+
index.trie.traverse do |path, node, descend|
|
256
|
+
node.values&.each do |definition|
|
257
|
+
if definition.public?
|
258
|
+
total += 1
|
259
|
+
documented += 1 if definition.comments&.any?
|
260
|
+
end
|
261
|
+
end
|
262
|
+
descend.call if node.values.nil?
|
263
|
+
end
|
264
|
+
|
265
|
+
(documented.to_f / total * 100).round(2)
|
266
|
+
end
|
267
|
+
|
268
|
+
puts "Coverage: #{coverage_percentage('lib')}%"
|
269
|
+
```
|
270
|
+
|
271
|
+
### Exclude Patterns
|
272
|
+
|
273
|
+
If you need to exclude certain files from coverage:
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
# Custom coverage script with exclusions.
|
277
|
+
paths = Dir.glob("lib/**/*.rb").reject do |path|
|
278
|
+
# Exclude vendor files and test files:
|
279
|
+
path.include?('vendor/') || path.end_with?('_test.rb')
|
280
|
+
end
|
281
|
+
|
282
|
+
index = Decode::Index.new
|
283
|
+
index.update(paths)
|
284
|
+
# ... continue with coverage analysis
|
285
|
+
```
|
286
|
+
|
287
|
+
## Best Practices
|
288
|
+
|
289
|
+
1. **Run coverage checks regularly** - Include in your CI pipeline
|
290
|
+
2. **Set coverage targets** - Aim for 100% coverage of public APIs
|
291
|
+
3. **Document incrementally** - Add documentation as you write code
|
292
|
+
4. **Use meaningful descriptions** - Don't just add empty comments
|
293
|
+
5. **Leverage @namespace** - For modules that only serve as containers
|
294
|
+
6. **Review coverage reports** - Use the missing documentation list to prioritize
|
295
|
+
|
296
|
+
## Troubleshooting
|
297
|
+
|
298
|
+
### Common Error Messages
|
299
|
+
|
300
|
+
**"Insufficient documentation!"**
|
301
|
+
- Some public definitions are missing documentation
|
302
|
+
- Check the list of missing items and add appropriate comments
|
303
|
+
|
304
|
+
**No output from coverage command**
|
305
|
+
- Verify the path exists: `bake decode:index:coverage lib`
|
306
|
+
- Check that Ruby files exist in the specified directory
|
307
|
+
|
308
|
+
**Coverage shows 0/0 definitions**
|
309
|
+
- The directory might not contain any Ruby files
|
310
|
+
- Try a different path or check your file extensions
|
311
|
+
|
312
|
+
### Debug Coverage Issues
|
313
|
+
|
314
|
+
```bash
|
315
|
+
# First, see what symbols are being detected
|
316
|
+
bake decode:index:symbols lib
|
317
|
+
|
318
|
+
# Then check what documentation exists
|
319
|
+
bake decode:index:documentation lib
|
320
|
+
|
321
|
+
# Finally, run coverage to see what's missing
|
322
|
+
bake decode:index:coverage lib
|
323
|
+
```
|
324
|
+
|
325
|
+
This workflow helps you understand what the tool is detecting and why certain items might be missing documentation.
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# Getting Started with Decode
|
2
|
+
|
3
|
+
The Decode gem provides programmatic access to Ruby code structure and metadata. It can parse Ruby files and extract definitions, comments, and documentation pragmas, enabling code analysis, documentation generation, and other programmatic manipulations of Ruby codebases.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add to your Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'decode'
|
11
|
+
```
|
12
|
+
|
13
|
+
Or install directly:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle add decode
|
17
|
+
```
|
18
|
+
|
19
|
+
## Basic Usage
|
20
|
+
|
21
|
+
### Analyzing a Ruby File
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'decode'
|
25
|
+
|
26
|
+
# Create a source object:
|
27
|
+
source = Decode::Source.new('lib/my_class.rb', Decode::Language::Ruby.new)
|
28
|
+
|
29
|
+
# Extract definitions (classes, methods, etc.):
|
30
|
+
definitions = source.definitions.to_a
|
31
|
+
|
32
|
+
definitions.each do |definition|
|
33
|
+
puts "#{definition.name}: #{definition.short_form}"
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
### Extracting Documentation
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# Get segments (blocks of comments + code):
|
41
|
+
segments = source.segments.to_a
|
42
|
+
|
43
|
+
segments.each do |segment|
|
44
|
+
puts "Comments: #{segment.comments.join(' ')}"
|
45
|
+
puts "Code: #{segment.code}"
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### Checking Documentation Coverage
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Check which definitions have documentation:
|
53
|
+
definitions.each do |definition|
|
54
|
+
status = definition.comments.any? ? 'documented' : 'missing docs'
|
55
|
+
puts "#{definition.name}: #{status}"
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
## Working with Documentation Pragmas
|
60
|
+
|
61
|
+
The Decode gem understands structured documentation pragmas:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# This will be parsed and structured:
|
65
|
+
source_code = <<~RUBY
|
66
|
+
# Represents a user in the system.
|
67
|
+
class User
|
68
|
+
# @attribute [String] The user's email address.
|
69
|
+
attr_reader :email
|
70
|
+
|
71
|
+
# Initialize a new user.
|
72
|
+
# @parameter email [String] The user's email address.
|
73
|
+
# @parameter options [Hash] Additional options.
|
74
|
+
# @option :active [Boolean] Whether the account is active.
|
75
|
+
# @raises [ArgumentError] If email is invalid.
|
76
|
+
def initialize(email, options = {})
|
77
|
+
# Validate the email format:
|
78
|
+
raise ArgumentError, "Invalid email" if email.empty?
|
79
|
+
|
80
|
+
# Set instance variables:
|
81
|
+
@email = email
|
82
|
+
@active = options.fetch(:active, true)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
RUBY
|
86
|
+
|
87
|
+
# Parse and analyze:
|
88
|
+
result = Decode::Language::Ruby.new.parser.parse_source(source_code)
|
89
|
+
definitions = Decode::Language::Ruby.new.parser.definitions_for(source_code).to_a
|
90
|
+
|
91
|
+
definitions.each do |definition|
|
92
|
+
puts "#{definition.name}: #{definition.comments.join(' ')}"
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
## Common Use Cases
|
97
|
+
|
98
|
+
### 1. Code Analysis and Metrics
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
# Analyze a codebase and return metrics.
|
102
|
+
# @parameter file_path [String] Path to the Ruby file to analyze.
|
103
|
+
def analyze_codebase(file_path)
|
104
|
+
source = Decode::Source.new(file_path, Decode::Language::Ruby.new)
|
105
|
+
definitions = source.definitions.to_a
|
106
|
+
|
107
|
+
# Count different definition types:
|
108
|
+
classes = definitions.count { |d| d.is_a?(Decode::Language::Ruby::Class) }
|
109
|
+
methods = definitions.count { |d| d.is_a?(Decode::Language::Ruby::Method) }
|
110
|
+
modules = definitions.count { |d| d.is_a?(Decode::Language::Ruby::Module) }
|
111
|
+
|
112
|
+
puts "Classes: #{classes}, Methods: #{methods}, Modules: #{modules}"
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
### 2. Documentation Coverage Reports
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
# Calculate documentation coverage for a file.
|
120
|
+
# @parameter file_path [String] Path to the Ruby file to analyze.
|
121
|
+
def documentation_coverage(file_path)
|
122
|
+
source = Decode::Source.new(file_path, Decode::Language::Ruby.new)
|
123
|
+
definitions = source.definitions.to_a
|
124
|
+
|
125
|
+
# Calculate coverage statistics:
|
126
|
+
total = definitions.count
|
127
|
+
documented = definitions.count { |d| d.comments.any? }
|
128
|
+
|
129
|
+
puts "Coverage: #{documented}/#{total} (#{(documented.to_f / total * 100).round(1)}%)"
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
### 3. Extracting API Information
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
# Extract API information from a Ruby file.
|
137
|
+
# @parameter file_path [String] Path to the Ruby file to analyze.
|
138
|
+
def extract_api_info(file_path)
|
139
|
+
source = Decode::Source.new(file_path, Decode::Language::Ruby.new)
|
140
|
+
definitions = source.definitions.to_a
|
141
|
+
|
142
|
+
# Get public methods only:
|
143
|
+
public_methods = definitions.select do |definition|
|
144
|
+
definition.is_a?(Decode::Language::Ruby::Method) &&
|
145
|
+
definition.visibility == :public
|
146
|
+
end
|
147
|
+
|
148
|
+
public_methods.each do |method|
|
149
|
+
puts "#{method.long_form}"
|
150
|
+
puts " Comments: #{method.comments.join(' ')}" if method.comments.any?
|
151
|
+
end
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
### 4. Code Structure Analysis
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
# Analyze the structure of Ruby files in a directory.
|
159
|
+
# @parameter directory [String] Path to the directory to analyze.
|
160
|
+
def analyze_structure(directory)
|
161
|
+
Dir.glob("#{directory}/**/*.rb").each do |file|
|
162
|
+
source = Decode::Source.new(file, Decode::Language::Ruby.new)
|
163
|
+
definitions = source.definitions.to_a
|
164
|
+
|
165
|
+
# Find nested classes and modules:
|
166
|
+
nested = definitions.select { |d| d.parent }
|
167
|
+
|
168
|
+
if nested.any?
|
169
|
+
puts "#{file}:"
|
170
|
+
nested.each do |definition|
|
171
|
+
puts " #{definition.qualified_name}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
### 5. Finding Undocumented Code
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
# Find undocumented code in a directory.
|
182
|
+
# @parameter directory [String] Path to the directory to analyze.
|
183
|
+
def find_undocumented(directory)
|
184
|
+
Dir.glob("#{directory}/**/*.rb").each do |file|
|
185
|
+
source = Decode::Source.new(file, Decode::Language::Ruby.new)
|
186
|
+
definitions = source.definitions.to_a
|
187
|
+
|
188
|
+
# Filter for undocumented public definitions:
|
189
|
+
undocumented = definitions.select { |d| d.comments.empty? && d.visibility == :public }
|
190
|
+
|
191
|
+
if undocumented.any?
|
192
|
+
puts "#{file}:"
|
193
|
+
undocumented.each do |definition|
|
194
|
+
puts " - #{definition.short_form}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
```
|
200
|
+
|
201
|
+
## Advanced Features
|
202
|
+
|
203
|
+
### Using the Index
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
# Create an index for multiple files:
|
207
|
+
index = Decode::Index.new
|
208
|
+
index.update(Dir.glob("lib/**/*.rb"))
|
209
|
+
|
210
|
+
# Search through the index:
|
211
|
+
index.trie.traverse do |path, node, descend|
|
212
|
+
if node.values
|
213
|
+
node.values.each do |definition|
|
214
|
+
puts "#{path.join('::')} - #{definition.short_form}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
descend.call
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
### Custom Language Support
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
# The decode gem is extensible:
|
225
|
+
language = Decode::Language::Generic.new("custom")
|
226
|
+
# You can implement your own parser for other languages:
|
227
|
+
```
|
228
|
+
|
229
|
+
## Tips for Effective Usage
|
230
|
+
|
231
|
+
1. **Use structured pragmas** - They help tools understand your code better.
|
232
|
+
2. **Leverage programmatic access** - Build tools that analyze and manipulate code.
|
233
|
+
3. **Use @namespace** - For organizational modules to achieve complete coverage.
|
234
|
+
4. **Analyze code patterns** - Use decode to understand codebase structure.
|
235
|
+
5. **Build automation** - Use decode in CI/CD pipelines for code quality checks.
|
236
|
+
|
237
|
+
## Next Steps
|
238
|
+
|
239
|
+
- See [Ruby Documentation](ruby-documentation.md) for complete pragma reference.
|
240
|
+
- Check out [Documentation Coverage](coverage.md) for coverage monitoring.
|
241
|
+
- Use decode to build code analysis tools for your projects.
|
242
|
+
- Integrate decode into your development workflow and CI/CD pipelines.
|