parlour 0.1.1 → 0.2.0

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