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.
@@ -0,0 +1,68 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbiGenerator
4
+ # Represents an attribute reader, writer or accessor.
5
+ class Attribute < Method
6
+ sig do
7
+ params(
8
+ generator: RbiGenerator,
9
+ name: String,
10
+ kind: Symbol,
11
+ type: String,
12
+ block: T.nilable(T.proc.params(x: Method).void)
13
+ ).void
14
+ end
15
+ # Creates a new attribute.
16
+ # @note You should use {Namespace#create_attribute} rather than this directly.
17
+ #
18
+ # @param generator [RbiGenerator] The current RbiGenerator.
19
+ # @param name [String] The name of this attribute.
20
+ # @param kind [Symbol] The kind of attribute this is; one of :writer, :reader or
21
+ # :accessor.
22
+ # @param type [String] A Sorbet string of this attribute's type, such as
23
+ # +"String"+ or +"T.untyped"+.
24
+ # @param block A block which the new instance yields itself to.
25
+ # @return [void]
26
+ def initialize(generator, name, kind, type, &block)
27
+ # According to this source:
28
+ # https://github.com/sorbet/sorbet/blob/2275752e51604acfb79b30a0a96debc996c089d9/test/testdata/dsl/attr_multi.rb
29
+ # attr_accessor and attr_reader should have: sig { returns(X) }
30
+ # attr_writer :foo should have: sig { params(foo: X).returns(X) }
31
+
32
+ @kind = kind
33
+ case kind
34
+ when :accessor, :reader
35
+ super(generator, name, [], type, &block)
36
+ when :writer
37
+ super(generator, name, [
38
+ Parameter.new(name, type: type)
39
+ ], type, &block)
40
+ else
41
+ raise 'unknown kind'
42
+ end
43
+ end
44
+
45
+ sig { returns(Symbol) }
46
+ # The kind of attribute this is; one of +:writer+, +:reader+, or +:accessor+.
47
+ # @return [Symbol]
48
+ attr_reader :kind
49
+
50
+ private
51
+
52
+ sig do
53
+ override.params(
54
+ indent_level: Integer,
55
+ options: Options
56
+ ).returns(T::Array[String])
57
+ end
58
+ # Generates the RBI lines for this method.
59
+ #
60
+ # @param indent_level [Integer] The indentation level to generate the lines at.
61
+ # @param options [Options] The formatting options to use.
62
+ # @return [Array<String>] The RBI lines, formatted as specified.
63
+ def generate_definition(indent_level, options)
64
+ [options.indented(indent_level, "attr_#{kind} :#{name}")]
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,20 +1,31 @@
1
1
  # typed: true
2
2
  module Parlour
3
3
  class RbiGenerator
4
+ # Represents a class definition.
4
5
  class ClassNamespace < Namespace
5
6
  extend T::Sig
6
7
 
7
8
  sig do
8
9
  params(
10
+ generator: RbiGenerator,
9
11
  name: String,
10
12
  superclass: T.nilable(String),
11
13
  abstract: T::Boolean,
12
14
  block: T.nilable(T.proc.params(x: ClassNamespace).void)
13
15
  ).void
14
16
  end
15
- def initialize(name, superclass, abstract, &block)
16
- super(&block)
17
- @name = name
17
+ # Creates a new class definition.
18
+ # @note You should use {Namespace#create_class} rather than this directly.
19
+ #
20
+ # @param generator [RbiGenerator] The current RbiGenerator.
21
+ # @param name [String] The name of this class.
22
+ # @param superclass [String, nil] The superclass of this class, or nil if it doesn't
23
+ # have one.
24
+ # @param abstract [Boolean] A boolean indicating whether this class is abstract.
25
+ # @param block A block which the new instance yields itself to.
26
+ # @return [void]
27
+ def initialize(generator, name, superclass, abstract, &block)
28
+ super(generator, name, &block)
18
29
  @superclass = superclass
19
30
  @abstract = abstract
20
31
  end
@@ -25,25 +36,31 @@ module Parlour
25
36
  options: Options
26
37
  ).returns(T::Array[String])
27
38
  end
39
+ # Generates the RBI lines for this class.
40
+ #
41
+ # @param indent_level [Integer] The indentation level to generate the lines at.
42
+ # @param options [Options] The formatting options to use.
43
+ # @return [Array<String>] The RBI lines, formatted as specified.
28
44
  def generate_rbi(indent_level, options)
29
45
  class_definition = superclass.nil? \
30
46
  ? "class #{name}"
31
47
  : "class #{name} < #{superclass}"
32
48
 
33
- lines = []
49
+ lines = generate_comments(indent_level, options)
34
50
  lines << options.indented(indent_level, class_definition)
35
51
  lines += [options.indented(indent_level + 1, "abstract!"), ""] if abstract
36
- lines += super(indent_level + 1, options)
52
+ lines += generate_body(indent_level + 1, options)
37
53
  lines << options.indented(indent_level, "end")
38
54
  end
39
55
 
40
- sig { returns(String) }
41
- attr_reader :name
42
-
43
56
  sig { returns(T.nilable(String)) }
57
+ # The superclass of this class, or nil if it doesn't have one.
58
+ # @return [String, nil]
44
59
  attr_reader :superclass
45
60
 
46
61
  sig { returns(T::Boolean) }
62
+ # A boolean indicating whether this class is abstract or not.
63
+ # @return [Boolean]
47
64
  attr_reader :abstract
48
65
 
49
66
  sig do
@@ -51,6 +68,13 @@ module Parlour
51
68
  others: T::Array[RbiGenerator::RbiObject]
52
69
  ).returns(T::Boolean)
53
70
  end
71
+ # Given an array of {ClassNamespace} instances, returns true if they may
72
+ # be merged into this instance using {merge_into_self}. For instances to
73
+ # be mergeable, they must either all be abstract or all not be abstract,
74
+ # and they must define the same superclass (or none at all).
75
+ #
76
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {ClassNamespace} instances.
77
+ # @return [Boolean] Whether this instance may be merged with them.
54
78
  def mergeable?(others)
55
79
  others = T.cast(others, T::Array[ClassNamespace]) rescue (return false)
56
80
  all = others + [self]
@@ -64,17 +88,29 @@ module Parlour
64
88
  others: T::Array[RbiGenerator::RbiObject]
65
89
  ).void
66
90
  end
91
+ # Given an array of {ClassNamespace} instances, merges them into this one.
92
+ # You MUST ensure that {mergeable?} is true for those instances.
93
+ #
94
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {ClassNamespace} instances.
95
+ # @return [void]
67
96
  def merge_into_self(others)
97
+ super
98
+
68
99
  others.each do |other|
69
100
  other = T.cast(other, ClassNamespace)
70
101
 
71
- other.children.each { |c| children << c }
72
- other.extends.each { |e| extends << e }
73
- other.includes.each { |i| includes << i }
74
-
75
102
  @superclass = other.superclass unless superclass
76
103
  end
77
104
  end
105
+
106
+ sig { override.returns(String) }
107
+ # Returns a human-readable brief string description of this class.
108
+ # @return [String]
109
+ def describe
110
+ "Class #{name} - #{"superclass #{superclass}, " if superclass}" +
111
+ "#{"abstract, " if abstract}#{children.length} children, " +
112
+ "#{includes.length} includes, #{extends.length} extends"
113
+ end
78
114
  end
79
115
  end
80
- end
116
+ end
@@ -1,13 +1,13 @@
1
1
  # typed: true
2
2
  module Parlour
3
3
  class RbiGenerator
4
- class Method
4
+ # Represents a method definition.
5
+ class Method < RbiObject
5
6
  extend T::Sig
6
7
 
7
- include RbiObject
8
-
9
8
  sig do
10
9
  params(
10
+ generator: RbiGenerator,
11
11
  name: String,
12
12
  parameters: T::Array[Parameter],
13
13
  return_type: T.nilable(String),
@@ -15,11 +15,32 @@ module Parlour
15
15
  implementation: T::Boolean,
16
16
  override: T::Boolean,
17
17
  overridable: T::Boolean,
18
- class_method: T::Boolean
18
+ class_method: T::Boolean,
19
+ block: T.nilable(T.proc.params(x: Method).void)
19
20
  ).void
20
21
  end
21
- def initialize(name, parameters, return_type = nil, abstract: false, implementation: false, override: false, overridable: false, class_method: false)
22
- @name = name
22
+ # Creates a new method definition.
23
+ # @note You should use {Namespace#create_method} rather than this directly.
24
+ #
25
+ # @param generator [RbiGenerator] The current RbiGenerator.
26
+ # @param name [String] The name of this method. You should not specify +self.+ in
27
+ # this - use the +class_method+ parameter instead.
28
+ # @param parameters [Array<Parameter>] An array of {Parameter} instances representing this
29
+ # method's parameters.
30
+ # @param return_type [String, nil] A Sorbet string of what this method returns, such as
31
+ # +"String"+ or +"T.untyped"+. Passing nil denotes a void return.
32
+ # @param abstract [Boolean] Whether this method is abstract.
33
+ # @param implementation [Boolean] Whether this method is an implementation of a
34
+ # parent abstract method.
35
+ # @param override [Boolean] Whether this method is overriding a parent overridable
36
+ # method.
37
+ # @param overridable [Boolean] Whether this method is overridable by subclasses.
38
+ # @param class_method [Boolean] Whether this method is a class method; that is, it
39
+ # it is defined using +self.+.
40
+ # @param block A block which the new instance yields itself to.
41
+ # @return [void]
42
+ def initialize(generator, name, parameters, return_type = nil, abstract: false, implementation: false, override: false, overridable: false, class_method: false, &block)
43
+ super(generator, name)
23
44
  @parameters = parameters
24
45
  @return_type = return_type
25
46
  @abstract = abstract
@@ -27,9 +48,15 @@ module Parlour
27
48
  @override = override
28
49
  @overridable = overridable
29
50
  @class_method = class_method
51
+ yield_self(&block)
30
52
  end
31
53
 
32
54
  sig { params(other: Object).returns(T::Boolean) }
55
+ # Returns true if this instance is equal to another method.
56
+ #
57
+ # @param other [Object] The other instance. If this is not a {Method} (or a
58
+ # subclass of it), this will always return false.
59
+ # @return [Boolean]
33
60
  def ==(other)
34
61
  Method === other &&
35
62
  name == other.name &&
@@ -42,28 +69,41 @@ module Parlour
42
69
  class_method == other.class_method
43
70
  end
44
71
 
45
- sig { returns(String) }
46
- attr_reader :name
47
-
48
72
  sig { returns(T::Array[Parameter]) }
73
+ # An array of {Parameter} instances representing this method's parameters.
74
+ # @return [Array<Parameter>]
49
75
  attr_reader :parameters
50
76
 
51
77
  sig { returns(T.nilable(String)) }
78
+ # A Sorbet string of what this method returns, such as "String" or
79
+ # "T.untyped". Passing nil denotes a void return.
80
+ # @return [String, nil]
52
81
  attr_reader :return_type
53
82
 
54
83
  sig { returns(T::Boolean) }
84
+ # Whether this method is abstract.
85
+ # @return [Boolean]
55
86
  attr_reader :abstract
56
87
 
57
88
  sig { returns(T::Boolean) }
89
+ # Whether this method is an implementation of a parent abstract method.
90
+ # @return [Boolean]
58
91
  attr_reader :implementation
59
92
 
60
93
  sig { returns(T::Boolean) }
94
+ # Whether this method is overriding a parent overridable method.
95
+ # @return [Boolean]
61
96
  attr_reader :override
62
97
 
63
98
  sig { returns(T::Boolean) }
99
+ # Whether this method is overridable by subclasses.
100
+ # @return [Boolean]
64
101
  attr_reader :overridable
65
102
 
66
103
  sig { returns(T::Boolean) }
104
+ # Whether this method is a class method; that is, it it is defined using
105
+ # +self.+.
106
+ # @return [Boolean]
67
107
  attr_reader :class_method
68
108
 
69
109
  sig do
@@ -72,6 +112,11 @@ module Parlour
72
112
  options: Options
73
113
  ).returns(T::Array[String])
74
114
  end
115
+ # Generates the RBI lines for this method.
116
+ #
117
+ # @param indent_level [Integer] The indentation level to generate the lines at.
118
+ # @param options [Options] The formatting options to use.
119
+ # @return [Array<String>] The RBI lines, formatted as specified.
75
120
  def generate_rbi(indent_level, options)
76
121
  return_call = return_type ? "returns(#{return_type})" : 'void'
77
122
 
@@ -98,26 +143,10 @@ module Parlour
98
143
  }#{
99
144
  qualifiers.empty? && parameters.empty? ? '' : '.'
100
145
  }#{return_call} }"
101
- )]
102
-
103
- def_params = parameters.map(&:to_def_param)
104
- name_prefix = class_method ? 'self.' : ''
105
- def_line = options.indented(
106
- indent_level,
107
- "def #{name_prefix}#{name}(#{def_params.join(', ')}); end"
108
- )
109
-
110
- sig_lines + [def_line]
111
- end
146
+ )]
112
147
 
113
- sig { returns(String) }
114
- def qualifiers
115
- result = ''
116
- result += 'abstract.' if abstract
117
- result += 'implementation.' if implementation
118
- result += 'override.' if override
119
- result += 'overridable.' if overridable
120
- result
148
+ generate_comments(indent_level, options) + sig_lines +
149
+ generate_definition(indent_level, options)
121
150
  end
122
151
 
123
152
  sig do
@@ -125,8 +154,14 @@ module Parlour
125
154
  others: T::Array[RbiGenerator::RbiObject]
126
155
  ).returns(T::Boolean)
127
156
  end
157
+ # Given an array of {Method} instances, returns true if they may be merged
158
+ # into this instance using {merge_into_self}. For instances to be
159
+ # mergeable, their signatures and definitions must be identical.
160
+ #
161
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {Method} instances.
162
+ # @return [Boolean] Whether this instance may be merged with them.
128
163
  def mergeable?(others)
129
- false
164
+ others.all? { |other| self == other }
130
165
  end
131
166
 
132
167
  sig do
@@ -134,9 +169,66 @@ module Parlour
134
169
  others: T::Array[RbiGenerator::RbiObject]
135
170
  ).void
136
171
  end
172
+ # Given an array of {Method} instances, merges them into this one.
173
+ # This particular implementation in fact does nothing, because {Method}
174
+ # instances are only mergeable if they are identical, so nothing needs
175
+ # to be changed.
176
+ # You MUST ensure that {mergeable?} is true for those instances.
177
+ #
178
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {Method} instances.
179
+ # @return [void]
137
180
  def merge_into_self(others)
138
- raise 'methods can never be merged like this'
181
+ # We don't need to change anything! We only merge identical methods
182
+ end
183
+
184
+ sig { override.returns(String) }
185
+ # Returns a human-readable brief string description of this method.
186
+ #
187
+ # @return [String]
188
+ def describe
189
+ # TODO: more info
190
+ "Method #{name} - #{parameters.length} parameters, " +
191
+ " returns #{return_type}"
192
+ end
193
+
194
+ private
195
+
196
+ sig do
197
+ overridable.params(
198
+ indent_level: Integer,
199
+ options: Options
200
+ ).returns(T::Array[String])
201
+ end
202
+ # Generates the RBI lines for this method.
203
+ #
204
+ # @param indent_level [Integer] The indentation level to generate the lines at.
205
+ # @param options [Options] The formatting options to use.
206
+ # @return [Array<String>] The RBI lines, formatted as specified.
207
+ def generate_definition(indent_level, options)
208
+ def_params = parameters.map(&:to_def_param)
209
+ name_prefix = class_method ? 'self.' : ''
210
+ def_line = options.indented(
211
+ indent_level,
212
+ "def #{name_prefix}#{name}#{
213
+ "(#{def_params.join(', ')})" unless parameters.empty?}; end"
214
+ )
215
+ [def_line]
216
+ end
217
+
218
+ sig { returns(String) }
219
+ # Returns the qualifiers which go in front of the +params+ part of this
220
+ # method's Sorbet +sig+. For example, if {abstract} is true, then this
221
+ # will return +abstract.+.
222
+ #
223
+ # @return [String]
224
+ def qualifiers
225
+ result = ''
226
+ result += 'abstract.' if abstract
227
+ result += 'implementation.' if implementation
228
+ result += 'override.' if override
229
+ result += 'overridable.' if overridable
230
+ result
139
231
  end
140
232
  end
141
233
  end
142
- end
234
+ end
@@ -1,18 +1,29 @@
1
1
  # typed: true
2
2
  module Parlour
3
3
  class RbiGenerator
4
+ # Represents a module definition.
4
5
  class ModuleNamespace < Namespace
5
6
  extend T::Sig
6
7
 
7
8
  sig do
8
9
  params(
10
+ generator: RbiGenerator,
9
11
  name: String,
10
12
  interface: T::Boolean,
11
13
  block: T.nilable(T.proc.params(x: ClassNamespace).void)
12
14
  ).void
13
15
  end
14
- def initialize(name, interface, &block)
15
- super(&block)
16
+ # Creates a new module definition.
17
+ # @note You should use {Namespace#create_module} rather than this directly.
18
+ #
19
+ # @param generator [RbiGenerator] The current RbiGenerator.
20
+ # @param name [String] The name of this module.
21
+ # @param interface [Boolean] A boolean indicating whether this module is an
22
+ # interface.
23
+ # @param block A block which the new instance yields itself to.
24
+ # @return [void]
25
+ def initialize(generator, name, interface, &block)
26
+ super(generator, name, &block)
16
27
  @name = name
17
28
  @interface = interface
18
29
  end
@@ -23,18 +34,22 @@ module Parlour
23
34
  options: Options
24
35
  ).returns(T::Array[String])
25
36
  end
37
+ # Generates the RBI lines for this module.
38
+ #
39
+ # @param indent_level [Integer] The indentation level to generate the lines at.
40
+ # @param options [Options] The formatting options to use.
41
+ # @return [Array<String>] The RBI lines, formatted as specified.
26
42
  def generate_rbi(indent_level, options)
27
- lines = []
43
+ lines = generate_comments(indent_level, options)
28
44
  lines << options.indented(indent_level, "module #{name}")
29
45
  lines += [options.indented(indent_level + 1, "interface!"), ""] if interface
30
- lines += super(indent_level + 1, options)
46
+ lines += generate_body(indent_level + 1, options)
31
47
  lines << options.indented(indent_level, "end")
32
48
  end
33
49
 
34
- sig { returns(String) }
35
- attr_reader :name
36
-
37
50
  sig { returns(T::Boolean) }
51
+ # A boolean indicating whether this module is an interface or not.
52
+ # @return [Boolean]
38
53
  attr_reader :interface
39
54
 
40
55
  sig do
@@ -42,6 +57,13 @@ module Parlour
42
57
  others: T::Array[RbiGenerator::RbiObject]
43
58
  ).returns(T::Boolean)
44
59
  end
60
+ # Given an array of {ModuleNamespace} instances, returns true if they may
61
+ # be merged into this instance using {merge_into_self}. For instances to
62
+ # be mergeable, they must either all be interfaces or all not be
63
+ # interfaces.
64
+ #
65
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {ModuleNamespace} instances.
66
+ # @return [Boolean] Whether this instance may be merged with them.
45
67
  def mergeable?(others)
46
68
  others = T.cast(others, T::Array[RbiGenerator::ModuleNamespace]) rescue (return false)
47
69
  all = others + [self]
@@ -54,15 +76,22 @@ module Parlour
54
76
  others: T::Array[RbiGenerator::RbiObject]
55
77
  ).void
56
78
  end
79
+ # Given an array of {ModuleNamespace} instances, merges them into this one.
80
+ # You MUST ensure that {mergeable?} is true for those instances.
81
+ #
82
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {ModuleNamespace} instances.
83
+ # @return [void]
57
84
  def merge_into_self(others)
58
- others.each do |other|
59
- other = T.cast(other, ModuleNamespace)
85
+ super
86
+ end
60
87
 
61
- other.children.each { |c| children << c }
62
- other.extends.each { |e| extends << e }
63
- other.includes.each { |i| includes << i }
64
- end
88
+ sig { override.returns(String) }
89
+ # Returns a human-readable brief string description of this module.
90
+ # @return [String]
91
+ def describe
92
+ "Module #{name} - #{"interface, " if interface}#{children.length} " +
93
+ "children, #{includes.length} includes, #{extends.length} extends"
65
94
  end
66
95
  end
67
96
  end
68
- end
97
+ end