parlour 0.1.1 → 0.2.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,58 +1,74 @@
1
1
  # typed: true
2
2
  module Parlour
3
3
  class RbiGenerator
4
- class Namespace
4
+ # A generic namespace. This shouldn't be used, except as the type of
5
+ # {RbiGenerator#root}.
6
+ class Namespace < RbiObject
5
7
  extend T::Sig
6
8
 
7
- include RbiObject
8
-
9
9
  sig do
10
10
  implementation.overridable.params(
11
11
  indent_level: Integer,
12
12
  options: Options
13
13
  ).returns(T::Array[String])
14
14
  end
15
+ # Generates the RBI lines for this namespace.
16
+ #
17
+ # @param indent_level [Integer] The indentation level to generate the lines at.
18
+ # @param options [Options] The formatting options to use.
19
+ # @return [Array<String>] The RBI lines, formatted as specified.
15
20
  def generate_rbi(indent_level, options)
16
- result = []
17
-
18
- if includes.any? || extends.any?
19
- result += includes.map do |i|
20
- options.indented(indent_level, "include #{i}")
21
- end
22
- result += extends.map do |e|
23
- options.indented(indent_level, "extend #{e}")
24
- end
25
- result << ""
26
- end
27
-
28
- first, *rest = children
29
- return [] unless first
30
-
31
- result += first.generate_rbi(indent_level, options) + T.must(rest)
32
- .map { |obj| obj.generate_rbi(indent_level, options) }
33
- .map { |lines| [""] + lines }
34
- .flatten
35
-
36
- result
21
+ generate_comments(indent_level, options) +
22
+ generate_body(indent_level, options)
37
23
  end
38
24
 
39
- sig { params(block: T.nilable(T.proc.params(x: Namespace).void)).void }
40
- def initialize(&block)
25
+ sig do
26
+ params(
27
+ generator: RbiGenerator,
28
+ name: T.nilable(String),
29
+ block: T.nilable(T.proc.params(x: Namespace).void)
30
+ ).void
31
+ end
32
+ # Creates a new namespace.
33
+ # @note Unless you're doing something impressively hacky, this shouldn't
34
+ # be invoked outside of {RbiGenerator#initialize}.
35
+ #
36
+ # @param generator [RbiGenerator] The current RbiGenerator.
37
+ # @param name [String, nil] The name of this module.
38
+ # @param block A block which the new instance yields itself to.
39
+ # @return [void]
40
+ def initialize(generator, name = nil, &block)
41
+ super(generator, name || '<anonymous namespace>')
41
42
  @children = []
42
43
  @extends = []
43
44
  @includes = []
45
+ @constants = []
44
46
  yield_self(&block)
45
47
  end
46
48
 
47
49
  sig { returns(T::Array[RbiObject]) }
50
+ # The child {RbiObject} instances inside this namespace.
51
+ # @return [Array<RbiObject>]
48
52
  attr_reader :children
49
53
 
50
54
  sig { returns(T::Array[String]) }
55
+ # A list of strings which are each used in an +extend+ statement in this
56
+ # namespace.
57
+ # @return [Array<String>]
51
58
  attr_reader :extends
52
59
 
53
60
  sig { returns(T::Array[String]) }
61
+ # A list of strings which are each used in an +include+ statement in this
62
+ # namespace.
63
+ # @return [Array<String>]
54
64
  attr_reader :includes
55
65
 
66
+ sig { returns(T::Array[[String, String]]) }
67
+ # A list of constants which are defined in this namespace, in the form of
68
+ # pairs [name, value].
69
+ # @return [Array<(String, String)>]
70
+ attr_reader :constants
71
+
56
72
  sig do
57
73
  params(
58
74
  name: String,
@@ -61,8 +77,24 @@ module Parlour
61
77
  block: T.nilable(T.proc.params(x: ClassNamespace).void)
62
78
  ).returns(ClassNamespace)
63
79
  end
80
+ # Creates a new class definition as a child of this namespace.
81
+ #
82
+ # @example Create a class with a nested module.
83
+ # namespace.create_class('Foo') do |foo|
84
+ # foo.create_module('Bar')
85
+ # end
86
+ #
87
+ # @example Create a class that is the child of another class.
88
+ # namespace.create_class('Bar', superclass: 'Foo') #=> class Bar < Foo
89
+ #
90
+ # @param name [String] The name of this class.
91
+ # @param superclass [String, nil] The superclass of this class, or nil if it doesn't
92
+ # have one.
93
+ # @param abstract [Boolean] A boolean indicating whether this class is abstract.
94
+ # @param block A block which the new instance yields itself to.
95
+ # @return [ClassNamespace]
64
96
  def create_class(name, superclass: nil, abstract: false, &block)
65
- new_class = ClassNamespace.new(name, superclass, abstract, &block)
97
+ new_class = ClassNamespace.new(generator, name, superclass, abstract, &block)
66
98
  children << new_class
67
99
  new_class
68
100
  end
@@ -74,8 +106,23 @@ module Parlour
74
106
  block: T.nilable(T.proc.params(x: ClassNamespace).void)
75
107
  ).returns(ModuleNamespace)
76
108
  end
109
+ # Creates a new module definition as a child of this namespace.
110
+ #
111
+ # @example Create a basic module.
112
+ # namespace.create_module('Foo')
113
+ #
114
+ # @example Create a module with a method.
115
+ # namespace.create_module('Foo') do |foo|
116
+ # foo.create_method('method_name', [], 'Integer')
117
+ # end
118
+ #
119
+ # @param name [String] The name of this module.
120
+ # @param interface [Boolean] A boolean indicating whether this module is an
121
+ # interface.
122
+ # @param block A block which the new instance yields itself to.
123
+ # @return [ModuleNamespace]
77
124
  def create_module(name, interface: false, &block)
78
- new_module = ModuleNamespace.new(name, interface, &block)
125
+ new_module = ModuleNamespace.new(generator, name, interface, &block)
79
126
  children << new_module
80
127
  new_module
81
128
  end
@@ -89,11 +136,31 @@ module Parlour
89
136
  implementation: T::Boolean,
90
137
  override: T::Boolean,
91
138
  overridable: T::Boolean,
92
- class_method: T::Boolean
139
+ class_method: T::Boolean,
140
+ block: T.nilable(T.proc.params(x: Method).void)
93
141
  ).returns(Method)
94
142
  end
95
- def create_method(name, parameters, return_type = nil, abstract: false, implementation: false, override: false, overridable: false, class_method: false)
143
+ # Creates a new method definition as a child of this namespace.
144
+ #
145
+ # @param name [String] The name of this method. You should not specify +self.+ in
146
+ # this - use the +class_method+ parameter instead.
147
+ # @param parameters [Array<Parameter>] An array of {Parameter} instances representing this
148
+ # method's parameters.
149
+ # @param return_type [String, nil] A Sorbet string of what this method returns, such as
150
+ # +"String"+ or +"T.untyped"+. Passing nil denotes a void return.
151
+ # @param abstract [Boolean] Whether this method is abstract.
152
+ # @param implementation [Boolean] Whether this method is an implementation of a
153
+ # parent abstract method.
154
+ # @param override [Boolean] Whether this method is overriding a parent overridable
155
+ # method.
156
+ # @param overridable [Boolean] Whether this method is overridable by subclasses.
157
+ # @param class_method [Boolean] Whether this method is a class method; that is, it
158
+ # it is defined using +self.+.
159
+ # @param block A block which the new instance yields itself to.
160
+ # @return [Method]
161
+ def create_method(name, parameters, return_type = nil, abstract: false, implementation: false, override: false, overridable: false, class_method: false, &block)
96
162
  new_method = RbiGenerator::Method.new(
163
+ generator,
97
164
  name,
98
165
  parameters,
99
166
  return_type,
@@ -101,26 +168,133 @@ module Parlour
101
168
  implementation: implementation,
102
169
  override: override,
103
170
  overridable: overridable,
104
- class_method: class_method
171
+ class_method: class_method,
172
+ &block
105
173
  )
106
174
  children << new_method
107
175
  new_method
108
176
  end
109
177
 
178
+ # Creates a new attribute.
179
+ #
180
+ # @example Create an +attr_reader+.
181
+ # module.create_attribute('readable', :reader, 'String')
182
+ # # #=> sig { returns(String) }
183
+ # # attr_reader :readable
184
+ #
185
+ # @example Create an +attr_writer+.
186
+ # module.create_attribute('writable', :writer, 'Integer')
187
+ # # #=> sig { params(writable: Integer).returns(Integer) }
188
+ # # attr_writer :writable
189
+ #
190
+ # @example Create an +attr_accessor+.
191
+ # module.create_attribute('accessible', :accessor, 'T::Boolean')
192
+ # # #=> sig { returns(T::Boolean) }
193
+ # # attr_accessor :accessible
194
+ #
195
+ # @param name [String] The name of this attribute.
196
+ # @param kind [Symbol] The kind of attribute this is; one of +:writer+, +:reader+, or
197
+ # +:accessor+.
198
+ # @param type [String] A Sorbet string of this attribute's type, such as
199
+ # +"String"+ or +"T.untyped"+.
200
+ # @param block A block which the new instance yields itself to.
201
+ # @return [RbiGenerator::Attribute]
202
+ def create_attribute(name, kind, type, &block)
203
+ new_attribute = RbiGenerator::Attribute.new(
204
+ generator,
205
+ name,
206
+ kind,
207
+ type,
208
+ &block
209
+ )
210
+ children << new_attribute
211
+ new_attribute
212
+ end
213
+ alias_method :create_attr, :create_attribute
214
+
215
+ # Creates a new read-only attribute (+attr_reader+).
216
+ #
217
+ # @param name [String] The name of this attribute.
218
+ # @param type [String] A Sorbet string of this attribute's type, such as
219
+ # +"String"+ or +"T.untyped"+.
220
+ # @param block A block which the new instance yields itself to.
221
+ # @return [RbiGenerator::Attribute]
222
+ def create_attr_reader(name, type, &block)
223
+ create_attribute(name, :reader, type, &block)
224
+ end
225
+
226
+ # Creates a new write-only attribute (+attr_writer+).
227
+ #
228
+ # @param name [String] The name of this attribute.
229
+ # @param type [String] A Sorbet string of this attribute's type, such as
230
+ # +"String"+ or +"T.untyped"+.
231
+ # @param block A block which the new instance yields itself to.
232
+ # @return [RbiGenerator::Attribute]
233
+ def create_attr_writer(name, type, &block)
234
+ create_attribute(name, :writer, type, &block)
235
+ end
236
+
237
+ # Creates a new read and write attribute (+attr_accessor+).
238
+ #
239
+ # @param name [String] The name of this attribute.
240
+ # @param type [String] A Sorbet string of this attribute's type, such as
241
+ # +"String"+ or +"T.untyped"+.
242
+ # @param block A block which the new instance yields itself to.
243
+ # @return [RbiGenerator::Attribute]
244
+ def create_attr_accessor(name, type, &block)
245
+ create_attribute(name, :accessor, type, &block)
246
+ end
247
+
110
248
  sig { params(name: String).void }
249
+ # Adds a new +extend+ to this namespace.
250
+ #
251
+ # @example Add an +extend+ to a class.
252
+ # class.add_extend('ExtendableClass') #=> extend ExtendableClass
253
+ #
254
+ # @param name [String] A code string for what is extended, for example
255
+ # +"MyModule"+.
256
+ # @return [void]
111
257
  def add_extend(name)
112
258
  extends << name
113
259
  end
260
+
114
261
  sig { params(name: String).void }
262
+ # Adds a new +include+ to this namespace.
263
+ #
264
+ # @example Add an +include+ to a class.
265
+ # class.add_include('IncludableClass') #=> include IncludableClass
266
+ #
267
+ # @param name [String] A code string for what is included, for example
268
+ # +"Enumerable"+.
269
+ # @return [void]
115
270
  def add_include(name)
116
271
  includes << name
117
272
  end
118
273
 
274
+ sig { params(name: String, value: String).void }
275
+ # Adds a new constant definition to this namespace.
276
+ #
277
+ # @param name [String] The name of the constant.
278
+ # @param value [String] A Ruby code string for this constant's value, for
279
+ # example +"3.14"+ or +"T.type_alias(X)"+
280
+ # @return [void]
281
+ def add_constant(name, value)
282
+ constants << [name, value]
283
+ end
284
+
119
285
  sig do
120
286
  implementation.overridable.params(
121
287
  others: T::Array[RbiGenerator::RbiObject]
122
288
  ).returns(T::Boolean)
123
289
  end
290
+ # Given an array of {Namespace} instances, returns true if they may be
291
+ # merged into this instance using {merge_into_self}. All bare namespaces
292
+ # can be merged into each other, as they lack definitions for themselves,
293
+ # so there is nothing to conflict. (This isn't the case for subclasses
294
+ # such as {ClassNamespace}.)
295
+ #
296
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances.
297
+ # @return [true] Always true.
124
298
  def mergeable?(others)
125
299
  true
126
300
  end
@@ -130,6 +304,12 @@ module Parlour
130
304
  others: T::Array[RbiGenerator::RbiObject]
131
305
  ).void
132
306
  end
307
+ # Given an array of {Namespace} instances, merges them into this one.
308
+ # All children, constants, extends and includes are copied into this
309
+ # instance.
310
+ #
311
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances.
312
+ # @return [void]
133
313
  def merge_into_self(others)
134
314
  others.each do |other|
135
315
  other = T.cast(other, Namespace)
@@ -137,8 +317,60 @@ module Parlour
137
317
  other.children.each { |c| children << c }
138
318
  other.extends.each { |e| extends << e }
139
319
  other.includes.each { |i| includes << i }
320
+ other.constants.each { |i| constants << i }
140
321
  end
141
322
  end
323
+
324
+ sig { implementation.overridable.returns(String) }
325
+ # Returns a human-readable brief string description of this namespace.
326
+ #
327
+ # @return [String]
328
+ def describe
329
+ "Namespace #{name} - #{children.length} children, #{includes.length} " +
330
+ "includes, #{extends.length} extends, #{constants.length} constants"
331
+ end
332
+
333
+ private
334
+
335
+ sig do
336
+ params(
337
+ indent_level: Integer,
338
+ options: Options
339
+ ).returns(T::Array[String])
340
+ end
341
+ # Generates the RBI lines for the body of this namespace. This consists of
342
+ # {includes}, {extends} and {children}.
343
+ #
344
+ # @param indent_level [Integer] The indentation level to generate the lines at.
345
+ # @param options [Options] The formatting options to use.
346
+ # @return [Array<String>] The RBI lines for the body, formatted as specified.
347
+ def generate_body(indent_level, options)
348
+ result = []
349
+
350
+ if includes.any? || extends.any? || constants.any?
351
+ result += includes.map do |i|
352
+ options.indented(indent_level, "include #{i}")
353
+ end
354
+ result += extends.map do |e|
355
+ options.indented(indent_level, "extend #{e}")
356
+ end
357
+ result += constants.map do |c|
358
+ name, value = c
359
+ options.indented(indent_level, "#{name} = #{value}")
360
+ end
361
+ result << ""
362
+ end
363
+
364
+ first, *rest = children
365
+ return [] unless first
366
+
367
+ result += first.generate_rbi(indent_level, options) + T.must(rest)
368
+ .map { |obj| obj.generate_rbi(indent_level, options) }
369
+ .map { |lines| [""] + lines }
370
+ .flatten
371
+
372
+ result
373
+ end
142
374
  end
143
375
  end
144
- end
376
+ end
@@ -1,25 +1,61 @@
1
1
  # typed: true
2
2
  module Parlour
3
3
  class RbiGenerator
4
+ # A set of immutable formatting options passed to all calls of
5
+ # {RbiObject#generate_rbi}.
4
6
  class Options
5
7
  extend T::Sig
6
8
 
7
9
  sig { params(break_params: Integer, tab_size: Integer).void }
10
+ # Creates a new set of formatting options.
11
+ #
12
+ # @example Create Options with +break_params+ of +4+ and +tab_size+ of +2+.
13
+ # Parlour::RbiGenerator::Options.new(break_params: 4, tab_size: 2)
14
+ #
15
+ # @param break_params [Integer] If there are at least this many parameters in a
16
+ # Sorbet +sig+, then it is broken onto separate lines.
17
+ # @param tab_size [Integer] The number of spaces to use per indent.
18
+ # @return [void]
8
19
  def initialize(break_params:, tab_size:)
9
20
  @break_params = break_params
10
21
  @tab_size = tab_size
11
22
  end
12
23
 
13
24
  sig { returns(Integer) }
25
+ # If there are at least this many parameters in a Sorbet +sig+, then it
26
+ # is broken onto separate lines.
27
+ #
28
+ # # With break_params: 5
29
+ # sig { params(name: String, age: Integer, hobbies: T::Array(String), country: Symbol).void }
30
+ #
31
+ # # With break_params: 4
32
+ # sig do
33
+ # params(
34
+ # name: String,
35
+ # age: Integer,
36
+ # hobbies: T::Array(String),
37
+ # country: Symbol
38
+ # ).void
39
+ # end
40
+ #
41
+ # @return [Integer]
14
42
  attr_reader :break_params
15
43
 
16
44
  sig { returns(Integer) }
45
+ # The number of spaces to use per indent.
46
+ # @return [Integer]
17
47
  attr_reader :tab_size
18
48
 
19
49
  sig { params(level: Integer, str: String).returns(String) }
50
+ # Returns a string indented to the given indent level, according to the
51
+ # set {tab_size}.
52
+ #
53
+ # @param level [Integer] The indent level, as an integer. 0 is totally unindented.
54
+ # @param str [String] The string to indent.
55
+ # @return [String] The indented string.
20
56
  def indented(level, str)
21
57
  " " * (level * tab_size) + str
22
58
  end
23
59
  end
24
60
  end
25
- end
61
+ end