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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2025, by Samuel Williams.
4
+ # Copyright, 2020-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "definition"
7
7
 
@@ -18,10 +18,23 @@ module Decode
18
18
  super(*arguments, **options)
19
19
 
20
20
  @super_class = super_class
21
+ @includes = []
22
+ @extends = []
23
+ @prepends = []
21
24
  end
22
25
 
26
+ # @attribute [String?] The super class name.
23
27
  attr :super_class
24
28
 
29
+ # @attribute [Array(Symbol)] Modules included into this class.
30
+ attr :includes
31
+
32
+ # @attribute [Array(Symbol)] Modules extended into this class (adds singleton methods).
33
+ attr :extends
34
+
35
+ # @attribute [Array(Symbol)] Modules prepended into this class (method lookup precedence).
36
+ attr :prepends
37
+
25
38
  # A class is a container for other definitions.
26
39
  def container?
27
40
  true
@@ -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
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2025, by Samuel Williams.
4
+ # Copyright, 2020-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "definition"
7
7
 
@@ -10,6 +10,24 @@ module Decode
10
10
  module Ruby
11
11
  # A Ruby-specific module.
12
12
  class Module < Definition
13
+ # Initialize a module with its name and options.
14
+ def initialize(*arguments, **options)
15
+ super(*arguments, **options)
16
+
17
+ @includes = []
18
+ @extends = []
19
+ @prepends = []
20
+ end
21
+
22
+ # @attribute [Array(Symbol)] Modules included into this module.
23
+ attr :includes
24
+
25
+ # @attribute [Array(Symbol)] Modules extended into this module (adds singleton methods).
26
+ attr :extends
27
+
28
+ # @attribute [Array(Symbol)] Modules prepended into this module (method lookup precedence).
29
+ attr :prepends
30
+
13
31
  # A module is a container for other definitions.
14
32
  def container?
15
33
  true
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2025, by Samuel Williams.
4
+ # Copyright, 2020-2026, by Samuel Williams.
5
5
 
6
6
  require "prism"
7
7
 
@@ -93,13 +93,13 @@ module Decode
93
93
  path = nested_path_for(node.constant_path)
94
94
 
95
95
  definition = Module.new(path,
96
- visibility: :public,
97
- comments: comments_for(node),
98
- parent: parent,
99
- node: node,
100
- language: @language,
101
- source: source,
102
- )
96
+ visibility: :public,
97
+ comments: comments_for(node),
98
+ parent: parent,
99
+ node: node,
100
+ language: @language,
101
+ source: source,
102
+ )
103
103
 
104
104
  store_definition(parent, path.last.to_sym, definition)
105
105
  yield definition
@@ -114,14 +114,14 @@ module Decode
114
114
  super_class = nested_name_for(node.superclass)
115
115
 
116
116
  definition = Class.new(path,
117
- super_class: super_class,
118
- visibility: :public,
119
- comments: comments_for(node),
120
- parent: parent,
121
- node: node,
122
- language: @language,
123
- source: source,
124
- )
117
+ super_class: super_class,
118
+ visibility: :public,
119
+ comments: comments_for(node),
120
+ parent: parent,
121
+ node: node,
122
+ language: @language,
123
+ source: source,
124
+ )
125
125
 
126
126
  store_definition(parent, path.last.to_sym, definition)
127
127
  yield definition
@@ -134,13 +134,13 @@ module Decode
134
134
  when :singleton_class_node
135
135
  if name = singleton_name_for(node)
136
136
  definition = Singleton.new(name,
137
- comments: comments_for(node),
138
- parent: parent,
139
- node: node,
140
- language: @language,
141
- visibility: :public,
142
- source: source
143
- )
137
+ comments: comments_for(node),
138
+ parent: parent,
139
+ node: node,
140
+ language: @language,
141
+ visibility: :public,
142
+ source: source
143
+ )
144
144
 
145
145
  yield definition
146
146
 
@@ -152,23 +152,23 @@ module Decode
152
152
  receiver = receiver_for(node.receiver)
153
153
 
154
154
  definition = Method.new(node.name,
155
- visibility: @visibility,
156
- comments: comments_for(node),
157
- parent: parent,
158
- node: node,
159
- language: @language,
160
- receiver: receiver,
161
- source: source,
162
- )
155
+ visibility: @visibility,
156
+ comments: comments_for(node),
157
+ parent: parent,
158
+ node: node,
159
+ language: @language,
160
+ receiver: receiver,
161
+ source: source,
162
+ )
163
163
 
164
164
  yield definition
165
165
  when :constant_write_node
166
166
  definition = Constant.new(node.name,
167
- comments: comments_for(node),
168
- parent: parent,
169
- node: node,
170
- language: @language,
171
- )
167
+ comments: comments_for(node),
168
+ parent: parent,
169
+ node: node,
170
+ language: @language,
171
+ )
172
172
 
173
173
  store_definition(parent, node.name, definition)
174
174
  yield definition
@@ -176,6 +176,40 @@ module Decode
176
176
  name = node.name
177
177
 
178
178
  case name
179
+ when :include, :extend, :prepend
180
+ # Handle mixins inside classes/modules
181
+ if parent
182
+ if node.arguments
183
+ node.arguments.arguments.each do |arg|
184
+ mod_name = case arg.type
185
+ when :constant_read_node
186
+ # Qualify with enclosing namespace if available (e.g. Mixins::Greeting)
187
+ if parent.parent && parent.parent.respond_to?(:qualified_name)
188
+ "#{parent.parent.qualified_name}::#{arg.name}"
189
+ else
190
+ arg.name.to_s
191
+ end
192
+ when :constant_path_node
193
+ nested_name_for(arg)
194
+ else
195
+ # Skip unsupported argument types (e.g., dynamic expressions)
196
+ nil
197
+ end
198
+ if mod_name
199
+ case name
200
+ when :include
201
+ parent.respond_to?(:includes) && parent.includes << mod_name
202
+ when :extend
203
+ parent.respond_to?(:extends) && parent.extends << mod_name
204
+ when :prepend
205
+ parent.respond_to?(:prepends) && parent.prepends << mod_name
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ # Don't treat include/extend as definitions.
212
+ return
179
213
  when :public, :protected, :private
180
214
  # Handle cases like "private def foo" where method definitions are arguments
181
215
  if node.arguments
@@ -187,13 +221,13 @@ module Decode
187
221
  receiver = receiver_for(argument_node.receiver)
188
222
 
189
223
  definition = Method.new(argument_node.name,
190
- visibility: name,
191
- comments: comments_for(argument_node),
192
- parent: parent,
193
- node: argument_node,
194
- language: @language,
195
- receiver: receiver,
196
- )
224
+ visibility: name,
225
+ comments: comments_for(argument_node),
226
+ parent: parent,
227
+ node: argument_node,
228
+ language: @language,
229
+ receiver: receiver,
230
+ )
197
231
 
198
232
  yield definition
199
233
  end
@@ -217,9 +251,9 @@ module Decode
217
251
  end
218
252
  when :attr, :attr_reader, :attr_writer, :attr_accessor
219
253
  definition = Attribute.new(attribute_name_for(node),
220
- comments: comments_for(node),
221
- parent: parent, language: @language, node: node
222
- )
254
+ comments: comments_for(node),
255
+ parent: parent, language: @language, node: node
256
+ )
223
257
 
224
258
  yield definition
225
259
  when :alias_method
@@ -233,29 +267,29 @@ module Decode
233
267
  old_name = symbol_name_for(old_name_arg)
234
268
 
235
269
  definition = Alias.new(new_name.to_sym, old_name.to_sym,
236
- comments: comments_for(node),
237
- parent: parent,
238
- node: node,
239
- language: @language,
240
- visibility: @visibility,
241
- source: source,
242
- )
270
+ comments: comments_for(node),
271
+ parent: parent,
272
+ node: node,
273
+ language: @language,
274
+ visibility: @visibility,
275
+ source: source,
276
+ )
243
277
 
244
278
  yield definition
245
279
  end
246
280
  else
247
281
  # Check if this call should be treated as a definition
248
282
  # either because it has a @name comment, @attribute comment, or a block
249
- has_name_comment = comments_for(node).any? {|comment| comment.match(NAME_ATTRIBUTE)}
283
+ has_name_comment = comments_for(node).any?{|comment| comment.match(NAME_ATTRIBUTE)}
250
284
  has_attribute_comment = kind_for(node, comments_for(node))
251
285
  has_block = node.block
252
286
 
253
287
  if has_name_comment || has_attribute_comment || has_block
254
288
  definition = Call.new(
255
- attribute_name_for(node),
256
- comments: comments_for(node),
257
- parent: parent, language: @language, node: node
258
- )
289
+ attribute_name_for(node),
290
+ comments: comments_for(node),
291
+ parent: parent, language: @language, node: node
292
+ )
259
293
 
260
294
  yield definition
261
295
 
@@ -265,19 +299,19 @@ module Decode
265
299
  end
266
300
  end
267
301
  end
268
- when :alias_method_node
269
- # Handle alias new_name old_name syntax
302
+ when :alias_node, :alias_method_node
303
+ # Handle `alias new_name old_name` syntax:
270
304
  new_name = node.new_name.unescaped
271
305
  old_name = node.old_name.unescaped
272
306
 
273
307
  definition = Alias.new(new_name.to_sym, old_name.to_sym,
274
- comments: comments_for(node),
275
- parent: parent,
276
- node: node,
277
- language: @language,
278
- visibility: @visibility,
279
- source: source,
280
- )
308
+ comments: comments_for(node),
309
+ parent: parent,
310
+ node: node,
311
+ language: @language,
312
+ visibility: @visibility,
313
+ source: source,
314
+ )
281
315
 
282
316
  yield definition
283
317
  when :if_node
@@ -404,8 +438,8 @@ module Decode
404
438
  saved_visibility = @visibility
405
439
  @visibility = visibility
406
440
  yield
407
- ensure
408
- @visibility = saved_visibility
441
+ ensure
442
+ @visibility = saved_visibility
409
443
  end
410
444
 
411
445
  NAME_ATTRIBUTE = /\A@name\s+(?<value>.*?)\Z/
@@ -550,20 +584,20 @@ module Decode
550
584
  # Start a new segment with these comments
551
585
  yield current_segment if current_segment
552
586
  current_segment = Segment.new(
553
- preceding_comments.map{|comment| comment.location.slice.sub(/^#[\s\t]?/, "")},
554
- @language,
555
- statement
556
- )
587
+ preceding_comments.map{|comment| comment.location.slice.sub(/^#[\s\t]?/, "")},
588
+ @language,
589
+ statement
590
+ )
557
591
  elsif current_segment
558
592
  # Extend current segment with this statement
559
593
  current_segment.expand(statement)
560
594
  else
561
595
  # Start a new segment without comments
562
596
  current_segment = Segment.new(
563
- [],
564
- @language,
565
- statement
566
- )
597
+ [],
598
+ @language,
599
+ statement
600
+ )
567
601
  end
568
602
  end
569
603
 
@@ -571,10 +605,10 @@ module Decode
571
605
  else
572
606
  # One top level segment:
573
607
  segment = Segment.new(
574
- [],
575
- @language,
576
- node
577
- )
608
+ [],
609
+ @language,
610
+ node
611
+ )
578
612
 
579
613
  yield segment
580
614
  end
@@ -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 "rbs"
7
7
  require_relative "wrapper"
@@ -35,6 +35,7 @@ module Decode
35
35
  name: generic.to_sym,
36
36
  variance: nil,
37
37
  upper_bound: nil,
38
+ lower_bound: nil,
38
39
  location: nil
39
40
  )
40
41
  end
@@ -51,10 +52,10 @@ module Decode
51
52
  # Extract super class if present:
52
53
  super_class = if @definition.super_class
53
54
  ::RBS::AST::Declarations::Class::Super.new(
54
- name: qualified_name_to_rbs(@definition.super_class),
55
- args: [],
56
- location: nil
57
- )
55
+ name: qualified_name_to_rbs(@definition.super_class),
56
+ args: [],
57
+ location: nil
58
+ )
58
59
  end
59
60
 
60
61
  # Create the class declaration with generics:
@@ -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 "rbs"
7
7
  require "console"
@@ -103,15 +103,15 @@ module Decode
103
103
  kind = @definition.receiver ? :singleton : :instance
104
104
 
105
105
  ::RBS::AST::Members::MethodDefinition.new(
106
- name: method_name.to_sym,
107
- kind: kind,
108
- overloads: overloads,
109
- annotations: [],
110
- location: nil,
111
- comment: comment,
112
- overloading: false,
113
- visibility: @definition.visibility || :public
114
- )
106
+ name: method_name.to_sym,
107
+ kind: kind,
108
+ overloads: overloads,
109
+ annotations: [],
110
+ location: nil,
111
+ comment: comment,
112
+ overloading: false,
113
+ visibility: @definition.visibility || :public
114
+ )
115
115
  end
116
116
 
117
117
  # Build a complete RBS function type from AST information.
@@ -265,7 +265,7 @@ module Decode
265
265
 
266
266
  # Find @parameter tags (but not @option tags, which are handled separately):
267
267
  param_tags = documentation.filter(Decode::Comment::Parameter).to_a
268
- param_tags = param_tags.reject {|tag| tag.is_a?(Decode::Comment::Option)}
268
+ param_tags = param_tags.reject{|tag| tag.is_a?(Decode::Comment::Option)}
269
269
  return [] if param_tags.empty?
270
270
 
271
271
  param_tags.map do |tag|
@@ -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 "rbs"
7
7
  require_relative "wrapper"
@@ -62,11 +62,11 @@ module Decode
62
62
  type = ::Decode::RBS::Type.parse(type_string)
63
63
 
64
64
  ::RBS::AST::Declarations::Constant.new(
65
- name: constant_definition.name.to_sym,
66
- type: type,
67
- location: nil,
68
- comment: nil
69
- )
65
+ name: constant_definition.name.to_sym,
66
+ type: type,
67
+ location: nil,
68
+ comment: nil
69
+ )
70
70
  end
71
71
  end
72
72
 
@@ -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-2026, by Samuel Williams.
5
5
 
6
6
  require "rbs"
7
7
  require "console"
@@ -21,10 +21,10 @@ module Decode
21
21
  true
22
22
  when ::RBS::Types::Union
23
23
  # Type | nil form - recursively check all union members
24
- rbs_type.types.any? {|type| nullable?(type)}
24
+ rbs_type.types.any?{|type| nullable?(type)}
25
25
  when ::RBS::Types::Tuple
26
26
  # [Type] form - recursively check all tuple elements
27
- rbs_type.types.any? {|type| nullable?(type)}
27
+ rbs_type.types.any?{|type| nullable?(type)}
28
28
  when ::RBS::Types::Bases::Nil
29
29
  # Direct nil type
30
30
  true
@@ -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.27.0"
9
9
  end
data/license.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2020-2025, by Samuel Williams.
3
+ Copyright, 2020-2026, by Samuel Williams.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -14,14 +14,24 @@ 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.27.0
28
+
29
+ - Add `decode:documentation:markdown` bake task for generating LLM-optimized Markdown documentation.
30
+
31
+ ### v0.26.0
32
+
33
+ - Add support for `@example` pragmas in Ruby documentation comments.
34
+
25
35
  ### v0.25.0
26
36
 
27
37
  - Singleton classes are not relevant for coverage, so they are now ignored by the coverage reporter.
@@ -54,6 +64,22 @@ We welcome contributions to this project.
54
64
  4. Push to the branch (`git push origin my-new-feature`).
55
65
  5. Create new Pull Request.
56
66
 
67
+ ### Running Tests
68
+
69
+ To run the test suite:
70
+
71
+ ``` shell
72
+ bundle exec sus
73
+ ```
74
+
75
+ ### Making Releases
76
+
77
+ To make a new release:
78
+
79
+ ``` shell
80
+ bundle exec bake gem:release:patch # or minor or major
81
+ ```
82
+
57
83
  ### Developer Certificate of Origin
58
84
 
59
85
  In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
data/releases.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Releases
2
2
 
3
+ ## v0.27.0
4
+
5
+ - Add `decode:documentation:markdown` bake task for generating LLM-optimized Markdown documentation.
6
+
7
+ ## v0.26.0
8
+
9
+ - Add support for `@example` pragmas in Ruby documentation comments.
10
+
3
11
  ## v0.25.0
4
12
 
5
13
  - Singleton classes are not relevant for coverage, so they are now ignored by the coverage reporter.
data.tar.gz.sig CHANGED
Binary file
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.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -71,15 +71,17 @@ extensions: []
71
71
  extra_rdoc_files: []
72
72
  files:
73
73
  - agent.md
74
+ - bake/decode/documentation.rb
74
75
  - bake/decode/index.rb
75
76
  - bake/decode/rbs.rb
76
- - context/coverage.md
77
+ - context/documentation-coverage.md
77
78
  - context/getting-started.md
79
+ - context/index.yaml
78
80
  - context/ruby-documentation.md
79
- - context/types.md
80
81
  - lib/decode.rb
81
82
  - lib/decode/comment/attribute.rb
82
83
  - lib/decode/comment/constant.rb
84
+ - lib/decode/comment/example.rb
83
85
  - lib/decode/comment/node.rb
84
86
  - lib/decode/comment/option.rb
85
87
  - lib/decode/comment/parameter.rb
@@ -149,14 +151,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
151
  requirements:
150
152
  - - ">="
151
153
  - !ruby/object:Gem::Version
152
- version: '3.2'
154
+ version: '3.3'
153
155
  required_rubygems_version: !ruby/object:Gem::Requirement
154
156
  requirements:
155
157
  - - ">="
156
158
  - !ruby/object:Gem::Version
157
159
  version: '0'
158
160
  requirements: []
159
- rubygems_version: 3.6.9
161
+ rubygems_version: 4.0.6
160
162
  specification_version: 4
161
163
  summary: Code analysis for documentation generation.
162
164
  test_files: []
metadata.gz.sig CHANGED
Binary file