parlour 0.1.1 → 0.2.0

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