sord 1.0.0 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86ef36c437c7c9a02b0cc18fddd6c11253f4d0be6f9d68283c8039cfd69d310e
4
- data.tar.gz: 38f53748c92a22b44745d8fff1dfca5fe41ee4b4424b3c61c63853d07eb5d396
3
+ metadata.gz: 14900bd9ce64eda2373d0844e42032ea041b44dd10a6ca6a82bd66c31b2b0c9b
4
+ data.tar.gz: 983e467e872f0e455da6ca618b2a2ffb159d81f6cc1456e8370e33ecd42179bf
5
5
  SHA512:
6
- metadata.gz: 3359eca2a431b010b81c9920f6dd2705422df7ee3b47ab014caf63d498168945e14e5b3ba789ab08dff2b1a081682ae0d05de3c1796d2ac948778ca1a8a8c9c5
7
- data.tar.gz: 92ef1253541f9d4a837486fe51a886341251db0acc145d224048e8e3d94368beb86e59edab5a3638e486bb877f65a188da38c1543a2708cac10b14eef81e5da1
6
+ metadata.gz: 6a63ef228f6763653b2be68d46c16f20793118a67adec93d6cc2ec016ed9df254803e3b1fb1bc5c5b26a248020715ee72836c3b4fab6bb07e2d1223e685af97b
7
+ data.tar.gz: b9a86b1af67cdfd6d73eb28f2b6b46d10b5e61418d2118693e2103da1d5e9d506c8046b069922b908d1d4ecd908abf832ad4a480868b6720650a386fb010d766
@@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [2.0.0] - 2020-03-16
7
+ ### Added
8
+ - Sord now supports generating `attr_accessor`, `attr_reader` and `attr_writer`
9
+ and will do so automatically when these are used in your code.
10
+ - Depending on what you're doing with Sord, this is **potentially breaking**,
11
+ as for example attributes which would previously generate two `foo` and `foo=`
12
+ methods in Sord will now just generate an `attr_accessor`.
13
+ - `#initialize` is now always typed as returning `void`, which is
14
+ **potentially breaking** if you directly call `#initialize` in code.
15
+ - The `--use-original-initialize-return` flag restores the old behaviour of
16
+ using whatever return type is provided, like any other method.
17
+
6
18
  ## [1.0.0] - 2020-02-16
7
19
  ### Added
8
20
  - Added the `--skip-constants` flag to avoid generating RBIs for constants.
data/exe/sord CHANGED
@@ -20,6 +20,7 @@ command :gen do |c|
20
20
  c.option '--include-messages STRING', String, 'Whitelists a comma-separated string of log message types'
21
21
  c.option '--keep-original-comments', 'Retains original YARD comments rather than converting them to Markdown'
22
22
  c.option '--skip-constants', 'Excludes constants from generated RBI'
23
+ c.option '--use-original-initialize-return', 'Uses the specified return type for #initialize rather than void'
23
24
 
24
25
  c.action do |args, options|
25
26
  options.default(
@@ -31,7 +32,8 @@ command :gen do |c|
31
32
  exclude_messages: nil,
32
33
  include_messages: nil,
33
34
  keep_original_comments: false,
34
- skip_constants: false
35
+ skip_constants: false,
36
+ use_original_initialize_return: false,
35
37
  )
36
38
 
37
39
  if args.length != 1
@@ -40,6 +40,7 @@ module Sord
40
40
  @replace_unresolved_with_untyped = options[:replace_unresolved_with_untyped]
41
41
  @keep_original_comments = options[:keep_original_comments]
42
42
  @skip_constants = options[:skip_constants]
43
+ @use_original_initialize_return = options[:use_original_initialize_return]
43
44
 
44
45
  # Hook the logger so that messages are added as comments to the RBI file
45
46
  Logging.add_hook do |type, msg, item|
@@ -96,6 +97,97 @@ module Sord
96
97
  end
97
98
  end
98
99
 
100
+ # Adds comments to an RBI object based on a docstring.
101
+ # @param [YARD::CodeObjects::NamespaceObject] item
102
+ # @param [Parlour::RbiGenerator::RbiObject] rbi_object
103
+ # @return [void]
104
+ def add_comments(item, rbi_object)
105
+ if @keep_original_comments
106
+ rbi_object.add_comments(item.docstring.all.split("\n"))
107
+ else
108
+ parser = YARD::Docstring.parser
109
+ parser.parse(item.docstring.all)
110
+
111
+ docs_array = parser.text.split("\n")
112
+
113
+ # Add @param tags if there are any with names and descriptions.
114
+ params = parser.tags.select { |tag| tag.tag_name == 'param' && tag.is_a?(YARD::Tags::Tag) && !tag.name.nil? }
115
+ # Add a blank line if there's anything before the params.
116
+ docs_array << '' if docs_array.length.positive? && params.length.positive?
117
+ params.each do |param|
118
+ docs_array << '' if docs_array.last != '' && docs_array.length.positive?
119
+ # Output params in the form of:
120
+ # _@param_ `foo` — Lorem ipsum.
121
+ # _@param_ `foo`
122
+ if param.text.nil? || param.text == ''
123
+ docs_array << "_@param_ `#{param.name}`"
124
+ else
125
+ docs_array << "_@param_ `#{param.name}` — #{param.text.gsub("\n", " ")}"
126
+ end
127
+ end
128
+
129
+ # Add @return tags (there could possibly be more than one, despite this not being supported)
130
+ returns = parser.tags.select { |tag| tag.tag_name == 'return' && tag.is_a?(YARD::Tags::Tag) && !tag.text.nil? && tag.text.strip != '' }
131
+ # Add a blank line if there's anything before the returns.
132
+ docs_array << '' if docs_array.length.positive? && returns.length.positive?
133
+ returns.each do |retn|
134
+ docs_array << '' if docs_array.last != '' && docs_array.length.positive?
135
+ # Output returns in the form of:
136
+ # _@return_ — Lorem ipsum.
137
+ docs_array << "_@return_ — #{retn.text}"
138
+ end
139
+
140
+ # Iterate through the @example tags for a given YARD doc and output them in Markdown codeblocks.
141
+ examples = parser.tags.select { |tag| tag.tag_name == 'example' && tag.is_a?(YARD::Tags::Tag) }
142
+ examples.each do |example|
143
+ # Only add a blank line if there's anything before the example.
144
+ docs_array << '' if docs_array.length.positive?
145
+ # Include the example's 'name' if there is one.
146
+ docs_array << example.name unless example.name.nil? || example.name == ""
147
+ docs_array << "```ruby"
148
+ docs_array.concat(example.text.split("\n"))
149
+ docs_array << "```"
150
+ end if examples.length.positive?
151
+
152
+ # Add @note and @deprecated tags.
153
+ notice_tags = parser.tags.select { |tag| ['note', 'deprecated'].include?(tag.tag_name) && tag.is_a?(YARD::Tags::Tag) }
154
+ # Add a blank line if there's anything before the params.
155
+ docs_array << '' if docs_array.last != '' && docs_array.length.positive? && notice_tags.length.positive?
156
+ notice_tags.each do |notice_tag|
157
+ docs_array << '' if docs_array.last != ''
158
+ # Output note/deprecated/see in the form of:
159
+ # _@note_ — Lorem ipsum.
160
+ # _@note_
161
+ if notice_tag.text.nil?
162
+ docs_array << "_@#{notice_tag.tag_name}_"
163
+ else
164
+ docs_array << "_@#{notice_tag.tag_name}_ — #{notice_tag.text}"
165
+ end
166
+ end
167
+
168
+ # Add @see tags.
169
+ see_tags = parser.tags.select { |tag| tag.tag_name == 'see' && tag.is_a?(YARD::Tags::Tag) }
170
+ # Add a blank line if there's anything before the params.
171
+ docs_array << '' if docs_array.last != '' && docs_array.length.positive? && see_tags.length.positive?
172
+ see_tags.each do |see_tag|
173
+ docs_array << '' if docs_array.last != ''
174
+ # Output note/deprecated/see in the form of:
175
+ # _@see_ `B` — Lorem ipsum.
176
+ # _@see_ `B`
177
+ if see_tag.text.nil?
178
+ docs_array << "_@see_ `#{see_tag.name}`"
179
+ else
180
+ docs_array << "_@see_ `#{see_tag.name}` — #{see_tag.text}"
181
+ end
182
+ end
183
+
184
+ # fix: yard text may contains multiple line. should deal \n.
185
+ # else generate text will be multiple line and only first line is commented
186
+ docs_array = docs_array.flat_map {|line| line.empty? ? [""] : line.split("\n")}
187
+ rbi_object.add_comments(docs_array)
188
+ end
189
+ end
190
+
99
191
  # Given a YARD NamespaceObject, add lines defining its methods and their
100
192
  # signatures to the current RBI file.
101
193
  # @param [YARD::CodeObjects::NamespaceObject] item
@@ -110,6 +202,11 @@ module Sord
110
202
  next
111
203
  end
112
204
 
205
+ # If the method is an attribute, it'll be handled by add_attributes
206
+ if meth.is_attribute?
207
+ next
208
+ end
209
+
113
210
  # Sort parameters
114
211
  meth.parameters.reverse.sort! { |pair1, pair2| sort_params(pair1, pair2) }
115
212
  # This is better than iterating over YARD's "@param" tags directly
@@ -191,7 +288,9 @@ module Sord
191
288
  end
192
289
 
193
290
  return_tags = meth.tags('return')
194
- returns = if return_tags.length == 0
291
+ returns = if meth.name == :initialize && !@use_original_initialize_return
292
+ nil
293
+ elsif return_tags.length == 0
195
294
  Logging.omit("no YARD return type given, using T.untyped", meth)
196
295
  'T.untyped'
197
296
  elsif return_tags.length == 1 && return_tags&.first&.types&.first&.downcase == "void"
@@ -221,89 +320,65 @@ module Sord
221
320
  returns: returns,
222
321
  class_method: meth.scope == :class
223
322
  ) do |m|
224
- if @keep_original_comments
225
- m.add_comments(meth.docstring.all.split("\n"))
226
- else
227
- parser = YARD::Docstring.parser
228
- parser.parse(meth.docstring.all)
229
-
230
- docs_array = parser.text.split("\n")
231
-
232
- # Add @param tags if there are any with names and descriptions.
233
- params = parser.tags.select { |tag| tag.tag_name == 'param' && tag.is_a?(YARD::Tags::Tag) && !tag.name.nil? }
234
- # Add a blank line if there's anything before the params.
235
- docs_array << '' if docs_array.length.positive? && params.length.positive?
236
- params.each do |param|
237
- docs_array << '' if docs_array.last != '' && docs_array.length.positive?
238
- # Output params in the form of:
239
- # _@param_ `foo` — Lorem ipsum.
240
- # _@param_ `foo`
241
- if param.text.nil? || param.text == ''
242
- docs_array << "_@param_ `#{param.name}`"
243
- else
244
- docs_array << "_@param_ `#{param.name}` — #{param.text.gsub("\n", " ")}"
245
- end
246
- end
323
+ add_comments(meth, m)
324
+ end
325
+ end
326
+ end
247
327
 
248
- # Add @return tags (there could possibly be more than one, despite this not being supported)
249
- returns = parser.tags.select { |tag| tag.tag_name == 'return' && tag.is_a?(YARD::Tags::Tag) && !tag.text.nil? && tag.text.strip != '' }
250
- # Add a blank line if there's anything before the returns.
251
- docs_array << '' if docs_array.length.positive? && returns.length.positive?
252
- returns.each do |retn|
253
- docs_array << '' if docs_array.last != '' && docs_array.length.positive?
254
- # Output returns in the form of:
255
- # _@return_ — Lorem ipsum.
256
- docs_array << "_@return_ — #{retn.text}"
257
- end
328
+ # Given a YARD NamespaceObject, add lines defining either its class
329
+ # and instance attributes and their signatures to the current RBI file.
330
+ # @param [YARD::CodeObjects::NamespaceObject] item
331
+ # @return [void]
332
+ def add_attributes(item)
333
+ [:class, :instance].each do |attr_loc|
334
+ # Grab attributes for the current location (class or instance)
335
+ attrs = item.attributes[attr_loc]
336
+ attrs.each do |name, attribute|
337
+ reader = attribute[:read]
338
+ writer = attribute[:write]
339
+
340
+ unless reader || writer
341
+ Logging.warn("attribute is not readable or writable somehow, skipping", attribute)
342
+ next
343
+ end
258
344
 
259
- # Iterate through the @example tags for a given YARD doc and output them in Markdown codeblocks.
260
- examples = parser.tags.select { |tag| tag.tag_name == 'example' && tag.is_a?(YARD::Tags::Tag) }
261
- examples.each do |example|
262
- # Only add a blank line if there's anything before the example.
263
- docs_array << '' if docs_array.length.positive?
264
- # Include the example's 'name' if there is one.
265
- docs_array << example.name unless example.name.nil? || example.name == ""
266
- docs_array << "```ruby"
267
- docs_array.concat(example.text.split("\n"))
268
- docs_array << "```"
269
- end if examples.length.positive?
270
-
271
- # Add @note and @deprecated tags.
272
- notice_tags = parser.tags.select { |tag| ['note', 'deprecated'].include?(tag.tag_name) && tag.is_a?(YARD::Tags::Tag) }
273
- # Add a blank line if there's anything before the params.
274
- docs_array << '' if docs_array.last != '' && docs_array.length.positive? && notice_tags.length.positive?
275
- notice_tags.each do |notice_tag|
276
- docs_array << '' if docs_array.last != ''
277
- # Output note/deprecated/see in the form of:
278
- # _@note_ — Lorem ipsum.
279
- # _@note_
280
- if notice_tag.text.nil?
281
- docs_array << "_@#{notice_tag.tag_name}_"
282
- else
283
- docs_array << "_@#{notice_tag.tag_name}_ — #{notice_tag.text}"
284
- end
285
- end
345
+ # Get all given types
346
+ yard_types = []
347
+ if reader
348
+ yard_types += reader.tags('return').flat_map(&:types).compact.reject { |x| x.downcase == 'void' } +
349
+ reader.tags('param').flat_map(&:types)
350
+ end
351
+ if writer
352
+ yard_types += writer.tags('return').flat_map(&:types).compact.reject { |x| x.downcase == 'void' } +
353
+ writer.tags('param').flat_map(&:types)
354
+ end
286
355
 
287
- # Add @see tags.
288
- see_tags = parser.tags.select { |tag| tag.tag_name == 'see' && tag.is_a?(YARD::Tags::Tag) }
289
- # Add a blank line if there's anything before the params.
290
- docs_array << '' if docs_array.last != '' && docs_array.length.positive? && see_tags.length.positive?
291
- see_tags.each do |see_tag|
292
- docs_array << '' if docs_array.last != ''
293
- # Output note/deprecated/see in the form of:
294
- # _@see_ `B` Lorem ipsum.
295
- # _@see_ `B`
296
- if see_tag.text.nil?
297
- docs_array << "_@see_ `#{see_tag.name}`"
298
- else
299
- docs_array << "_@see_ `#{see_tag.name}` — #{see_tag.text}"
300
- end
301
- end
356
+ # Use T.untyped if not types specified anywhere, otherwise try to
357
+ # compute Sorbet type given all these types
358
+ if yard_types.empty?
359
+ Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", reader || writer)
360
+ sorbet_type = 'T.untyped'
361
+ else
362
+ sorbet_type = TypeConverter.yard_to_sorbet(
363
+ yard_types, reader || writer, @replace_errors_with_untyped, @replace_unresolved_with_untyped)
364
+ end
365
+
366
+ # Generate attribute
367
+ if reader && writer
368
+ kind = :accessor
369
+ elsif reader
370
+ kind = :reader
371
+ elsif writer
372
+ kind = :writer
373
+ end
302
374
 
303
- # fix: yard text may contains multiple line. should deal \n.
304
- # else generate text will be multiple line and only first line is commented
305
- docs_array = docs_array.flat_map {|line| line.empty? ? [""] : line.split("\n")}
306
- m.add_comments(docs_array)
375
+ @current_object.create_attribute(
376
+ name.to_s,
377
+ kind: kind,
378
+ type: sorbet_type,
379
+ class_attribute: (attr_loc == :class)
380
+ ) do |m|
381
+ add_comments(reader || writer, m)
307
382
  end
308
383
  end
309
384
  end
@@ -327,6 +402,7 @@ module Sord
327
402
 
328
403
  add_mixins(item)
329
404
  add_methods(item)
405
+ add_attributes(item)
330
406
  add_constants(item) unless @skip_constants
331
407
 
332
408
  item.children.select { |x| [:class, :module].include?(x.type) }
@@ -1,4 +1,4 @@
1
1
  # typed: strong
2
2
  module Sord
3
- VERSION = '1.0.0'
3
+ VERSION = '2.0.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sord
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Christiansen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-16 00:00:00.000000000 Z
11
+ date: 2020-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: yard
@@ -185,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
185
  - !ruby/object:Gem::Version
186
186
  version: '0'
187
187
  requirements: []
188
- rubygems_version: 3.1.2
188
+ rubygems_version: 3.0.3
189
189
  signing_key:
190
190
  specification_version: 4
191
191
  summary: Generate Sorbet RBI files from YARD documentation