decode 0.25.0 → 0.26.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.
@@ -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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
4
+ # Copyright, 2024-2025, by Samuel Williams.
5
5
 
6
6
  require_relative "parameter"
7
7
 
@@ -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.
@@ -15,6 +15,7 @@ require_relative "comment/raises"
15
15
  require_relative "comment/returns"
16
16
  require_relative "comment/throws"
17
17
  require_relative "comment/yields"
18
+ require_relative "comment/example"
18
19
 
19
20
  module Decode
20
21
  # Structured access to a set of comment lines.
@@ -27,6 +27,8 @@ module Decode
27
27
 
28
28
  tags["public"] = Comment::Pragma
29
29
  tags["private"] = Comment::Pragma
30
+
31
+ tags["example"] = Comment::Example
30
32
  end
31
33
 
32
34
  # Initialize a new generic language.
@@ -34,6 +34,8 @@ module Decode
34
34
  tags["public"] = Comment::Pragma
35
35
  tags["private"] = Comment::Pragma
36
36
 
37
+ tags["example"] = Comment::Example
38
+
37
39
  tags["rbs"] = Comment::RBS
38
40
  end
39
41
 
@@ -404,8 +404,8 @@ module Decode
404
404
  saved_visibility = @visibility
405
405
  @visibility = visibility
406
406
  yield
407
- ensure
408
- @visibility = saved_visibility
407
+ ensure
408
+ @visibility = saved_visibility
409
409
  end
410
410
 
411
411
  NAME_ATTRIBUTE = /\A@name\s+(?<value>.*?)\Z/
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2024, by Samuel Williams.
4
+ # Copyright, 2025, by Samuel Williams.
5
5
 
6
6
  require "rbs"
7
7
  require "console"
@@ -49,4 +49,4 @@ module Decode
49
49
  end
50
50
  end
51
51
  end
52
- end
52
+ end
data/lib/decode/scope.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2024, by Samuel Williams.
4
+ # Copyright, 2020-2025, by Samuel Williams.
5
5
 
6
6
  require_relative "definition"
7
7
 
@@ -5,5 +5,5 @@
5
5
 
6
6
  module Decode
7
7
  # @constant [String] The version of the gem.
8
- VERSION = "0.25.0"
8
+ VERSION = "0.26.0"
9
9
  end
data/readme.md CHANGED
@@ -14,14 +14,20 @@ Please see the [project documentation](https://socketry.github.io/decode/) for m
14
14
 
15
15
  - [Getting Started](https://socketry.github.io/decode/guides/getting-started/index) - This guide explains how to use `decode` for source code analysis.
16
16
 
17
- - [Code Coverage](https://socketry.github.io/decode/guides/code-coverage/index) - This guide explains how to compute documentation code coverage.
17
+ - [Documentation Coverage](https://socketry.github.io/decode/guides/documentation-coverage/index) - This guide explains how to test and monitor documentation coverage in your Ruby projects using the Decode gem's built-in bake tasks.
18
18
 
19
19
  - [Extract Symbols](https://socketry.github.io/decode/guides/extract-symbols/index) - This example demonstrates how to extract symbols using the index. An instance of <code class="language-ruby">Decode::Index</code> is used for loading symbols from source code files. These symbols are available as a flat list and as a trie structure. You can look up specific symbols using a reference using <code class="language-ruby">Decode::Index\#lookup</code>.
20
20
 
21
+ - [Ruby Documentation](https://socketry.github.io/decode/guides/ruby-documentation/index) - This guide covers documentation practices and pragmas supported by the Decode gem for documenting Ruby code. These pragmas provide structured documentation that can be parsed and used to generate API documentation and achieve complete documentation coverage.
22
+
21
23
  ## Releases
22
24
 
23
25
  Please see the [project releases](https://socketry.github.io/decode/releases/index) for all releases.
24
26
 
27
+ ### v0.26.0
28
+
29
+ - Add support for `@example` pragmas in Ruby documentation comments.
30
+
25
31
  ### v0.25.0
26
32
 
27
33
  - Singleton classes are not relevant for coverage, so they are now ignored by the coverage reporter.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.26.0
4
+
5
+ - Add support for `@example` pragmas in Ruby documentation comments.
6
+
3
7
  ## v0.25.0
4
8
 
5
9
  - Singleton classes are not relevant for coverage, so they are now ignored by the coverage reporter.
data.tar.gz.sig CHANGED
@@ -1,3 +1 @@
1
- eE�,V7U��.��~� Zcg$Ȝ���C�,M�#7\d0_gag�e_]� 8+E6!9�./��b ]��q�D%r��
2
- �R#=`|��9��R�ݍ�_���,M��
3
- ��3I �n�j��C�PƎ`�IC�e������#�R-e�[y +�T���c���lt�(.��D_�5J�KO�%c=�~
1
+ s]f����w��b�C˦�MOJ��ƛ瞾ן��\#hnR}�?|6�\�i��`��sN����k3���b�d�)�2*2�~��6iw�!E���a�~��ՠ��SG¨b��3@��Sx|��Q���4���h��P�e1���w3By�[���e_М�U5��ŠK�aHVX�jc��ҝ�+v������k���O�����y��^2��{X�#��.m�"��7���4?[�c��=��3��������|Q�ln��Ў��x��Hl�6�e���H�륹H���D�����'����_A���䈡Ԡ�$���m![p%`aF\��E3�P�������Q�(NO:������h���4}�g���H��ZMy$�4
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.0
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -73,13 +73,14 @@ files:
73
73
  - agent.md
74
74
  - bake/decode/index.rb
75
75
  - bake/decode/rbs.rb
76
- - context/coverage.md
76
+ - context/documentation-coverage.md
77
77
  - context/getting-started.md
78
+ - context/index.yaml
78
79
  - context/ruby-documentation.md
79
- - context/types.md
80
80
  - lib/decode.rb
81
81
  - lib/decode/comment/attribute.rb
82
82
  - lib/decode/comment/constant.rb
83
+ - lib/decode/comment/example.rb
83
84
  - lib/decode/comment/node.rb
84
85
  - lib/decode/comment/option.rb
85
86
  - lib/decode/comment/parameter.rb
@@ -156,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
157
  - !ruby/object:Gem::Version
157
158
  version: '0'
158
159
  requirements: []
159
- rubygems_version: 3.6.9
160
+ rubygems_version: 3.7.2
160
161
  specification_version: 4
161
162
  summary: Code analysis for documentation generation.
162
163
  test_files: []
metadata.gz.sig CHANGED
Binary file
data/context/coverage.md DELETED
@@ -1,325 +0,0 @@
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 [bool] 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.coverage_relevant?
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.
data/context/types.md DELETED
@@ -1,127 +0,0 @@
1
- # Setting Up RBS Types and Steep Type Checking for Ruby Gems
2
-
3
- This guide covers the process for establishing robust type checking in Ruby gems using RBS and Steep, focusing on automated generation from source documentation and proper validation.
4
-
5
- ## Core Process
6
-
7
- ### Documentation-Driven RBS Generation
8
-
9
- Generate RBS files from documentation:
10
-
11
- ```bash
12
- bake decode:rbs:generate lib > sig/example/gem.rbs`
13
- ```
14
-
15
- At a minimum, add `@parameter`, `@attribute` and `@returns` documentation to all public methods.
16
-
17
- #### Parametric Types
18
-
19
- Use `@rbs generic` comments to define type parameters for classes and modules:
20
-
21
- ```ruby
22
- # @rbs generic T
23
- class Container
24
- # @parameter item [T] The item to store
25
- def initialize(item)
26
- @item = item
27
- end
28
-
29
- # @returns [T] The stored item
30
- def get
31
- @item
32
- end
33
- end
34
- ```
35
-
36
- Use `@rbs` comments for parametric method signatures:
37
-
38
- ```ruby
39
- # From above:
40
- class Container
41
- # @rbs () { (T) -> void } -> void
42
- def each
43
- yield @item
44
- end
45
- ```
46
-
47
- #### Interfaces
48
-
49
- Create interfaces in `sig/example/gem/interface.rbs`:
50
-
51
- ```rbs
52
- module Example
53
- module Gem
54
- interface _Interface
55
- end
56
- end
57
- end
58
- ```
59
-
60
- You can use the interface in `@parameter`, `@attribute` and `@returns` types.
61
-
62
- ### Testing
63
-
64
- Run tests using the `steep` gem.
65
-
66
- ```bash
67
- steep check
68
- ```
69
-
70
- **Process**: Start with basic generation, then refine based on Steep feedback.
71
-
72
- 1. Generate initial RBS from documentation
73
- 2. Run `steep check lib` to identify issues
74
- 3. Fix structural problems (inheritance, missing docs)
75
- 4. Iterate until clean validation
76
-
77
- ### Deploymnet
78
-
79
- Make sure `bake-test-types` is added to the `test` group in `gems.rb` (or `Gemfile`).
80
-
81
- ```ruby
82
- group :test do
83
- # ...
84
- gem "bake-test"
85
- gem "bake-test-external"
86
- gem "bake-test-types"
87
- # ...
88
- end
89
- ```
90
-
91
- Then, create `.github/workflows/test-types.yaml`:
92
-
93
- ```yaml
94
- name: Test Types
95
-
96
- on: [push, pull_request]
97
-
98
- permissions:
99
- contents: read
100
-
101
- env:
102
- CONSOLE_OUTPUT: XTerm
103
-
104
- jobs:
105
- test:
106
- name: ${{matrix.ruby}} on ${{matrix.os}}
107
- runs-on: ${{matrix.os}}-latest
108
-
109
- strategy:
110
- matrix:
111
- os:
112
- - ubuntu
113
-
114
- ruby:
115
- - "3.4"
116
-
117
- steps:
118
- - uses: actions/checkout@v4
119
- - uses: ruby/setup-ruby@v1
120
- with:
121
- ruby-version: ${{matrix.ruby}}
122
- bundler-cache: true
123
-
124
- - name: Run tests
125
- timeout-minutes: 10
126
- run: bundle exec bake test:types
127
- ```