sord 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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