decode 0.25.0 → 0.27.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 +1 -1
- data/bake/decode/documentation.rb +378 -0
- data/context/documentation-coverage.md +254 -0
- data/context/getting-started.md +34 -222
- data/context/index.yaml +22 -0
- data/context/ruby-documentation.md +91 -43
- data/lib/decode/comment/constant.rb +1 -1
- data/lib/decode/comment/example.rb +62 -0
- data/lib/decode/comment/option.rb +1 -1
- data/lib/decode/comment/tag.rb +1 -0
- data/lib/decode/definition.rb +9 -1
- data/lib/decode/documentation.rb +1 -0
- data/lib/decode/language/generic.rb +2 -0
- data/lib/decode/language/ruby/alias.rb +13 -1
- data/lib/decode/language/ruby/class.rb +14 -1
- data/lib/decode/language/ruby/generic.rb +2 -0
- data/lib/decode/language/ruby/module.rb +19 -1
- data/lib/decode/language/ruby/parser.rb +115 -81
- data/lib/decode/rbs/class.rb +6 -5
- data/lib/decode/rbs/method.rb +11 -11
- data/lib/decode/rbs/module.rb +6 -6
- data/lib/decode/rbs/type.rb +4 -4
- data/lib/decode/scope.rb +1 -1
- data/lib/decode/version.rb +1 -1
- data/license.md +1 -1
- data/readme.md +27 -1
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +7 -5
- metadata.gz.sig +0 -0
- data/context/coverage.md +0 -325
- data/context/types.md +0 -127
data/context/getting-started.md
CHANGED
|
@@ -1,242 +1,54 @@
|
|
|
1
|
-
# Getting Started
|
|
1
|
+
# Getting Started
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This guide explains how to use `decode` for source code analysis.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
Add to your
|
|
7
|
+
Add the gem to your project:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
~~~ bash
|
|
10
|
+
$ bundle add decode
|
|
11
|
+
~~~
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Indexing
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
bundle add decode
|
|
17
|
-
```
|
|
15
|
+
`decode` turns your source code into a kind of database with rich access to definitions, segments and associated comments. Use {ruby Decode::Index} to build an index of your project by loading in source files:
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
~~~ ruby
|
|
18
|
+
require 'decode/index'
|
|
20
19
|
|
|
21
|
-
|
|
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
|
-
```
|
|
20
|
+
index = Decode::Index.new
|
|
36
21
|
|
|
37
|
-
|
|
22
|
+
# Load all Ruby files into the index:
|
|
23
|
+
index.update(Dir['**/*.rb'])
|
|
24
|
+
~~~
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
# Get segments (blocks of comments + code):
|
|
41
|
-
segments = source.segments.to_a
|
|
26
|
+
Once you've done this, you can print out all the definitions from your project:
|
|
42
27
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
~~~ ruby
|
|
29
|
+
index.definitions.each do |name, symbol|
|
|
30
|
+
puts symbol.long_form
|
|
46
31
|
end
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### Checking Documentation Coverage
|
|
32
|
+
~~~
|
|
50
33
|
|
|
51
|
-
|
|
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 [bool] 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
|
-
```
|
|
34
|
+
## References
|
|
220
35
|
|
|
221
|
-
|
|
36
|
+
References are strings which can be resolved into definitions. The index allows you to efficiently resolve references.
|
|
222
37
|
|
|
223
|
-
|
|
224
|
-
#
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
38
|
+
~~~ ruby
|
|
39
|
+
# Lookup a specific symbol:
|
|
40
|
+
reference = index.languages.parse_reference("ruby Decode::Index#lookup")
|
|
41
|
+
definition = index.lookup(reference).first
|
|
42
|
+
puts definition.long_form
|
|
43
|
+
~~~
|
|
228
44
|
|
|
229
|
-
##
|
|
45
|
+
## Documentation
|
|
230
46
|
|
|
231
|
-
|
|
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.
|
|
47
|
+
The {ruby Decode::Documentation} provides rich access to the comments that preceed a definition. This includes metadata including `@parameter`, `@returns` and other tags.
|
|
236
48
|
|
|
237
|
-
|
|
49
|
+
~~~ ruby
|
|
50
|
+
lines = definition.documentation.text
|
|
51
|
+
puts lines
|
|
52
|
+
~~~
|
|
238
53
|
|
|
239
|
-
|
|
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.
|
|
54
|
+
See {ruby Decode::Comment::Node#traverse} for more details about how to consume this data.
|
data/context/index.yaml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
|
3
|
+
---
|
|
4
|
+
description: Code analysis for documentation generation.
|
|
5
|
+
metadata:
|
|
6
|
+
documentation_uri: https://socketry.github.io/decode/
|
|
7
|
+
funding_uri: https://github.com/sponsors/socketry/
|
|
8
|
+
source_code_uri: https://github.com/socketry/decode.git
|
|
9
|
+
files:
|
|
10
|
+
- path: getting-started.md
|
|
11
|
+
title: Getting Started
|
|
12
|
+
description: This guide explains how to use `decode` for source code analysis.
|
|
13
|
+
- path: documentation-coverage.md
|
|
14
|
+
title: Documentation Coverage
|
|
15
|
+
description: This guide explains how to test and monitor documentation coverage
|
|
16
|
+
in your Ruby projects using the Decode gem's built-in bake tasks.
|
|
17
|
+
- path: ruby-documentation.md
|
|
18
|
+
title: Ruby Documentation
|
|
19
|
+
description: This guide covers documentation practices and pragmas supported by
|
|
20
|
+
the Decode gem for documenting Ruby code. These pragmas provide structured documentation
|
|
21
|
+
that can be parsed and used to generate API documentation and achieve complete
|
|
22
|
+
documentation coverage.
|
|
@@ -8,32 +8,38 @@ This guide covers documentation practices and pragmas supported by the Decode ge
|
|
|
8
8
|
|
|
9
9
|
#### Definition Documentation
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
- Full sentences: All documentation for definitions (classes, modules, methods) should be written as complete sentences with proper grammar and punctuation.
|
|
12
|
+
- Class documentation: Should directly describe what the class *is* or *does*.
|
|
13
|
+
- For data/model classes, "A user account in the system." works well.
|
|
14
|
+
- For functional classes (servers, clients, connections), lead with what the class does: "An HTTP client that manages persistent connections...".
|
|
15
|
+
- Method documentation: Should clearly describe what the method does, not how it does it.
|
|
16
|
+
- Markdown format: All documentation comments are written in Markdown format, allowing for rich formatting including lists, emphasis, code blocks, and links.
|
|
15
17
|
|
|
16
18
|
#### Inline Code Comments
|
|
17
19
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
+
- Explanatory comments: Comments within methods that explain specific lines or sections of code should end with a colon `:` to distinguish them from definition documentation.
|
|
21
|
+
- Purpose: These comments explain the reasoning behind specific implementation details.
|
|
20
22
|
|
|
21
23
|
#### Links and Code Formatting
|
|
22
24
|
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
- Curly braces `{}`: Use curly braces to create links to other methods, classes, or modules. The Decode gem uses `@index.lookup(text)` to resolve these references.
|
|
26
|
+
- Absolute references: `{Decode::Index#lookup}` - Links to a specific method in a specific class
|
|
27
|
+
- Relative references: `{lookup}` - Links to a method in the current scope or class
|
|
28
|
+
- Class references: `{User}` - Links to a class or module
|
|
29
|
+
- Backticks: Use backticks for code formatting of symbols, values, method names, and technical terms that should appear in monospace font.
|
|
30
|
+
- Symbols: `:admin`, `:user`, `:guest`
|
|
31
|
+
- Values: `true`, `false`, `nil`
|
|
32
|
+
- Technical terms: `attr_*`, `catch`/`throw`
|
|
33
|
+
- Code expressions: `**options`
|
|
32
34
|
|
|
33
35
|
#### Examples
|
|
34
36
|
|
|
37
|
+
##### Data/Model Classes
|
|
38
|
+
|
|
39
|
+
For classes that model domain concepts, describe what the class is:
|
|
40
|
+
|
|
35
41
|
```ruby
|
|
36
|
-
#
|
|
42
|
+
# A user account in the system.
|
|
37
43
|
class User
|
|
38
44
|
# @attribute [String] The user's email address.
|
|
39
45
|
attr_reader :email
|
|
@@ -70,26 +76,35 @@ class User
|
|
|
70
76
|
true
|
|
71
77
|
end
|
|
72
78
|
end
|
|
79
|
+
```
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
##### Functional/Service Classes
|
|
82
|
+
|
|
83
|
+
For classes that *do* something (clients, servers, processors), lead with what the class does:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
# An HTTP client that manages persistent connections to a remote server, with automatic retries for idempotent requests.
|
|
87
|
+
class Client
|
|
88
|
+
# Send a request to the remote server.
|
|
89
|
+
# @parameter request [Protocol::HTTP::Request] The request to send.
|
|
90
|
+
# @returns [Protocol::HTTP::Response] The response from the server.
|
|
91
|
+
def call(request)
|
|
92
|
+
# ...
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Close the client and release all connections.
|
|
96
|
+
def close
|
|
97
|
+
# ...
|
|
89
98
|
end
|
|
90
99
|
end
|
|
100
|
+
|
|
101
|
+
# Raised when a connection to the remote server cannot be established.
|
|
102
|
+
class ConnectionError < StandardError
|
|
103
|
+
end
|
|
91
104
|
```
|
|
92
105
|
|
|
106
|
+
Note the difference: `User` is described as a thing ("A user account..."), while `Client` is described by what it does ("An HTTP client that manages..."), and `ConnectionError` is described by when it occurs ("Raised when...").
|
|
107
|
+
|
|
93
108
|
**Key formatting examples from above:**
|
|
94
109
|
- `{disable!}` - Creates a link to the `disable!` method (relative reference)
|
|
95
110
|
- `active?` - Formats the method name in monospace (backticks for code formatting)
|
|
@@ -98,14 +113,14 @@ end
|
|
|
98
113
|
|
|
99
114
|
### Best Practices
|
|
100
115
|
|
|
101
|
-
1.
|
|
102
|
-
2.
|
|
103
|
-
3.
|
|
104
|
-
4.
|
|
105
|
-
5.
|
|
106
|
-
6.
|
|
107
|
-
7.
|
|
108
|
-
8.
|
|
116
|
+
1. Be Consistent: Use the same format for similar types of documentation.
|
|
117
|
+
2. Include Types: Always specify types for parameters, returns, and attributes.
|
|
118
|
+
3. Be Descriptive: Provide clear, actionable descriptions.
|
|
119
|
+
4. Document Exceptions: Always document what exceptions might be raised.
|
|
120
|
+
5. Use Examples: Include usage examples when the behavior isn't obvious.
|
|
121
|
+
6. Keep Updated: Update documentation when you change the code.
|
|
122
|
+
7. Use @namespace wisely: Apply to organizational modules to achieve 100% coverage.
|
|
123
|
+
8. Avoid redundancy: For simple attributes and methods, attach descriptions directly to pragmas rather than repeating obvious information.
|
|
109
124
|
|
|
110
125
|
#### Simple Attributes and Methods
|
|
111
126
|
|
|
@@ -155,7 +170,7 @@ Type signatures are used to specify the expected types of parameters, return val
|
|
|
155
170
|
Documents class attributes, instance variables, and `attr_*` declarations. Prefer to have one attribute per line for clarity.
|
|
156
171
|
|
|
157
172
|
```ruby
|
|
158
|
-
#
|
|
173
|
+
# A person with basic attributes.
|
|
159
174
|
class Person
|
|
160
175
|
# @attribute [String] The person's full name.
|
|
161
176
|
attr_reader :name
|
|
@@ -247,14 +262,14 @@ Documents block parameters and behavior.
|
|
|
247
262
|
|
|
248
263
|
```ruby
|
|
249
264
|
# @yields {|item| ...} Each item in the collection.
|
|
250
|
-
#
|
|
265
|
+
# @parameter item [Object] The current item being processed.
|
|
251
266
|
def each_item(&block)
|
|
252
267
|
items.each(&block)
|
|
253
268
|
end
|
|
254
269
|
|
|
255
270
|
# @yields {|user, index| ...} User and their index.
|
|
256
|
-
#
|
|
257
|
-
#
|
|
271
|
+
# @parameter user [User] The current user.
|
|
272
|
+
# @parameter index [Integer] The user's position in the list.
|
|
258
273
|
def each_user_with_index(&block)
|
|
259
274
|
users.each_with_index(&block)
|
|
260
275
|
end
|
|
@@ -308,6 +323,39 @@ def fetch_data
|
|
|
308
323
|
end
|
|
309
324
|
```
|
|
310
325
|
|
|
326
|
+
### Example Code
|
|
327
|
+
|
|
328
|
+
#### `@example Title?`
|
|
329
|
+
|
|
330
|
+
Includes an example usage snippet for a method or definition. The `@example` directive can have an optional title on the same line, followed by one or more indented lines of code:
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
# @example Create a new thing
|
|
334
|
+
# thing = Thing.new
|
|
335
|
+
# thing.process
|
|
336
|
+
def process
|
|
337
|
+
# ...
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Behaviour:
|
|
342
|
+
|
|
343
|
+
- The optional title is captured verbatim (e.g., `"Create a new thing"`).
|
|
344
|
+
- Indented lines following the directive are captured as the example code.
|
|
345
|
+
- When parsed, examples are available as `Decode::Comment::Example` nodes with:
|
|
346
|
+
- `title` – `String?`, the optional example title.
|
|
347
|
+
- `text` – `Array(String)?`, the raw code lines (including leading indentation).
|
|
348
|
+
- `code` – `String?`, the code as a single string with leading indentation removed (convenience).
|
|
349
|
+
|
|
350
|
+
Multiple `@example` pragmas can be used on the same definition to show different scenarios.
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
definition.documentation.filter(Decode::Comment::Example) do |example|
|
|
354
|
+
puts "Title: #{example.title.inspect}"
|
|
355
|
+
puts example.code # => joined string with indentation removed
|
|
356
|
+
end
|
|
357
|
+
```
|
|
358
|
+
|
|
311
359
|
### Namespace Documentation
|
|
312
360
|
|
|
313
361
|
#### `@namespace`
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "tag"
|
|
7
|
+
|
|
8
|
+
module Decode
|
|
9
|
+
module Comment
|
|
10
|
+
# Represents a code example with an optional title.
|
|
11
|
+
#
|
|
12
|
+
# - `@example Title`
|
|
13
|
+
# - `@example`
|
|
14
|
+
#
|
|
15
|
+
# Should contain nested text lines representing the example code.
|
|
16
|
+
class Example < Tag
|
|
17
|
+
# Parse an example directive from text.
|
|
18
|
+
# @parameter directive [String] The directive name.
|
|
19
|
+
# @parameter text [String?] The optional title text.
|
|
20
|
+
# @parameter lines [Array(String)] The remaining lines.
|
|
21
|
+
# @parameter tags [Tags] The tags parser.
|
|
22
|
+
# @parameter level [Integer] The indentation level.
|
|
23
|
+
def self.parse(directive, text, lines, tags, level = 0)
|
|
24
|
+
node = self.new(directive, text)
|
|
25
|
+
|
|
26
|
+
tags.parse(lines, level + 1) do |child|
|
|
27
|
+
node.add(child)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
return node
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Initialize a new example tag.
|
|
34
|
+
# @parameter directive [String] The directive name.
|
|
35
|
+
# @parameter title [String?] The optional title for the example.
|
|
36
|
+
def initialize(directive, title = nil)
|
|
37
|
+
super(directive)
|
|
38
|
+
|
|
39
|
+
# @type ivar @title: String?
|
|
40
|
+
@title = title&.strip unless title&.empty?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @attribute [String?] The title of the example.
|
|
44
|
+
attr :title
|
|
45
|
+
|
|
46
|
+
# Get the example code as a single string with leading indentation removed.
|
|
47
|
+
# @returns [String?] The example code joined with newlines, or nil if no code.
|
|
48
|
+
def code
|
|
49
|
+
lines = text
|
|
50
|
+
return unless lines
|
|
51
|
+
|
|
52
|
+
# Get the indentation from the first line
|
|
53
|
+
if indentation = lines.first[/\A\s+/]
|
|
54
|
+
# Remove the base indentation from all lines
|
|
55
|
+
lines = lines.map{|line| line.sub(indentation, "")}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
return lines.join("\n")
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/decode/comment/tag.rb
CHANGED
|
@@ -21,6 +21,7 @@ module Decode
|
|
|
21
21
|
def self.build(directive, match)
|
|
22
22
|
raise NotImplementedError, "Subclasses must implement build method"
|
|
23
23
|
end
|
|
24
|
+
|
|
24
25
|
# Build a pattern for bracketed content, supporting nested brackets.
|
|
25
26
|
# @parameter name [Symbol] The name of the group.
|
|
26
27
|
# @returns [String] The pattern.
|
data/lib/decode/definition.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2020-
|
|
4
|
+
# Copyright, 2020-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require_relative "location"
|
|
7
7
|
|
|
@@ -166,6 +166,14 @@ module Decode
|
|
|
166
166
|
false
|
|
167
167
|
end
|
|
168
168
|
|
|
169
|
+
# Whether this definition represents an alias to another definition.
|
|
170
|
+
# Tools can use this to filter aliases from outputs without parsing text.
|
|
171
|
+
#
|
|
172
|
+
# @returns [bool] False by default; specific definition types may override.
|
|
173
|
+
def alias?
|
|
174
|
+
false
|
|
175
|
+
end
|
|
176
|
+
|
|
169
177
|
# Whether this represents a single entity to be documented (along with it's contents).
|
|
170
178
|
#
|
|
171
179
|
# @returns [bool]
|
data/lib/decode/documentation.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2025, by Samuel Williams.
|
|
4
|
+
# Copyright, 2025-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require_relative "definition"
|
|
7
7
|
|
|
@@ -21,6 +21,18 @@ module Decode
|
|
|
21
21
|
|
|
22
22
|
attr :old_name
|
|
23
23
|
|
|
24
|
+
# Whether this definition represents an alias.
|
|
25
|
+
# @returns [bool] Always true for aliases.
|
|
26
|
+
def alias?
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The original name this alias refers to.
|
|
31
|
+
# @returns [Symbol]
|
|
32
|
+
def aliased_name
|
|
33
|
+
@old_name
|
|
34
|
+
end
|
|
35
|
+
|
|
24
36
|
# Aliases don't require separate documentation as they reference existing methods.
|
|
25
37
|
# @returns [bool] Always false for aliases.
|
|
26
38
|
def coverage_relevant?
|