tapioca 0.3.1 → 0.4.4

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +25 -1
  3. data/README.md +23 -2
  4. data/Rakefile +15 -4
  5. data/lib/tapioca.rb +8 -2
  6. data/lib/tapioca/cli.rb +32 -3
  7. data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +129 -0
  8. data/lib/tapioca/compilers/dsl/action_mailer.rb +65 -0
  9. data/lib/tapioca/compilers/dsl/active_record_associations.rb +267 -0
  10. data/lib/tapioca/compilers/dsl/active_record_columns.rb +393 -0
  11. data/lib/tapioca/compilers/dsl/active_record_enum.rb +112 -0
  12. data/lib/tapioca/compilers/dsl/active_record_identity_cache.rb +213 -0
  13. data/lib/tapioca/compilers/dsl/active_record_scope.rb +100 -0
  14. data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +170 -0
  15. data/lib/tapioca/compilers/dsl/active_resource.rb +140 -0
  16. data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +126 -0
  17. data/lib/tapioca/compilers/dsl/base.rb +165 -0
  18. data/lib/tapioca/compilers/dsl/frozen_record.rb +96 -0
  19. data/lib/tapioca/compilers/dsl/protobuf.rb +144 -0
  20. data/lib/tapioca/compilers/dsl/smart_properties.rb +173 -0
  21. data/lib/tapioca/compilers/dsl/state_machines.rb +378 -0
  22. data/lib/tapioca/compilers/dsl/url_helpers.rb +92 -0
  23. data/lib/tapioca/compilers/dsl_compiler.rb +121 -0
  24. data/lib/tapioca/compilers/requires_compiler.rb +67 -0
  25. data/lib/tapioca/compilers/sorbet.rb +34 -0
  26. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +171 -26
  27. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +1 -20
  28. data/lib/tapioca/compilers/todos_compiler.rb +32 -0
  29. data/lib/tapioca/config.rb +14 -6
  30. data/lib/tapioca/config_builder.rb +22 -9
  31. data/lib/tapioca/constant_locator.rb +1 -0
  32. data/lib/tapioca/core_ext/class.rb +23 -0
  33. data/lib/tapioca/gemfile.rb +32 -9
  34. data/lib/tapioca/generator.rb +231 -23
  35. data/lib/tapioca/loader.rb +30 -9
  36. data/lib/tapioca/version.rb +1 -1
  37. metadata +32 -39
@@ -0,0 +1,144 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+ require "parlour"
4
+
5
+ begin
6
+ require "google/protobuf"
7
+ rescue LoadError
8
+ return
9
+ end
10
+
11
+ module Tapioca
12
+ module Compilers
13
+ module Dsl
14
+ # `Tapioca::Compilers::Dsl::Protobuf` decorates RBI files for subclasses of
15
+ # `Google::Protobuf::MessageExts`.
16
+ # (see https://github.com/coinbase/protoc-gen-rbi).
17
+ #
18
+ # For example, with the following "cart.rb" file:
19
+ #
20
+ # ~~~rb
21
+ # Google::Protobuf::DescriptorPool.generated_pool.build do
22
+ # add_file("cart.proto", :syntax => :proto3) do
23
+ # add_message "MyCart" do
24
+ # optional :shop_id, :int32, 1
25
+ # optional :customer_id, :int64, 2
26
+ # optional :number_value, :double, 3
27
+ # optional :string_value, :string, 4
28
+ # end
29
+ # end
30
+ # ~~~
31
+ #
32
+ # this generator will produce the RBI file `cart.rbi` with the following content:
33
+ #
34
+ # ~~~rbi
35
+ # # cart.rbi
36
+ # # typed: strong
37
+ # class Cart
38
+ # sig { returns(Integer) }
39
+ # def customer_id; end
40
+ #
41
+ # sig { params(month: Integer).returns(Integer) }
42
+ # def customer_id=(value); end
43
+ #
44
+ # sig { returns(Integer) }
45
+ # def shop_id; end
46
+ #
47
+ # sig { params(value: Integer).returns(Integer) }
48
+ # def shop_id=(value); end
49
+ #
50
+ # sig { returns(String) }
51
+ # def string_value; end
52
+ #
53
+ # sig { params(value: String).returns(String) }
54
+ # def string_value=(value); end
55
+ #
56
+ #
57
+ # sig { returns(Float) }
58
+ # def number_value; end
59
+ #
60
+ # sig { params(value: Float).returns(Float) }
61
+ # def number_value=(value); end
62
+ # end
63
+ # ~~~
64
+ class Protobuf < Base
65
+ extend T::Sig
66
+
67
+ sig do
68
+ override.params(
69
+ root: Parlour::RbiGenerator::Namespace,
70
+ constant: T.class_of(Google::Protobuf::MessageExts)
71
+ ).void
72
+ end
73
+ def decorate(root, constant)
74
+ descriptor = T.let(T.unsafe(constant).descriptor, Google::Protobuf::Descriptor)
75
+ return unless descriptor.any?
76
+
77
+ root.path(constant) do |klass|
78
+ descriptor.each do |desc|
79
+ create_descriptor_method(klass, desc)
80
+ end
81
+ end
82
+ end
83
+
84
+ sig { override.returns(T::Enumerable[Module]) }
85
+ def gather_constants
86
+ classes = T.cast(ObjectSpace.each_object(Class), T::Enumerable[Class])
87
+ classes.select { |c| c < Google::Protobuf::MessageExts && !c.singleton_class? }
88
+ end
89
+
90
+ private
91
+
92
+ sig do
93
+ params(
94
+ descriptor: Google::Protobuf::FieldDescriptor
95
+ ).returns(String)
96
+ end
97
+ def type_of(descriptor)
98
+ case descriptor.type
99
+ when :enum
100
+ descriptor.subtype.enummodule.name
101
+ when :message
102
+ descriptor.subtype.msgclass.name
103
+ when :int32, :int64, :uint32, :uint64
104
+ "Integer"
105
+ when :double, :float
106
+ "Float"
107
+ when :bool
108
+ "T::Boolean"
109
+ when :string, :bytes
110
+ "String"
111
+ else
112
+ "T.untyped"
113
+ end
114
+ end
115
+
116
+ sig do
117
+ params(
118
+ klass: Parlour::RbiGenerator::Namespace,
119
+ desc: Google::Protobuf::FieldDescriptor,
120
+ ).void
121
+ end
122
+ def create_descriptor_method(klass, desc)
123
+ name = desc.name
124
+ type = type_of(desc)
125
+
126
+ create_method(
127
+ klass,
128
+ name,
129
+ return_type: type
130
+ )
131
+
132
+ create_method(
133
+ klass,
134
+ "#{name}=",
135
+ parameters: [
136
+ Parlour::RbiGenerator::Parameter.new("value", type: type),
137
+ ],
138
+ return_type: type
139
+ )
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,173 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "parlour"
5
+
6
+ begin
7
+ require "smart_properties"
8
+ rescue LoadError
9
+ # means SmartProperties is not installed,
10
+ # so let's not even define the generator.
11
+ return
12
+ end
13
+
14
+ module Tapioca
15
+ module Compilers
16
+ module Dsl
17
+ # `Tapioca::Compilers::Dsl::SmartProperties` generates RBI files for classes that include
18
+ # `SmartProperties` (see https://github.com/t6d/smart_properties).
19
+ #
20
+ # For example, with the following class that includes `SmartProperties`:
21
+ #
22
+ # ~~~rb
23
+ # # post.rb
24
+ # class Post
25
+ # include(SmartProperties)
26
+ #
27
+ # property :title, accepts: String
28
+ # property! :description, accepts: String
29
+ # property :published, accepts: [true, false], reader: :published?
30
+ # property :enabled, accepts: [true, false], default: false
31
+ # end
32
+ # ~~~
33
+ #
34
+ # this generator will produce the RBI file `post.rbi` with the following content:
35
+ #
36
+ # ~~~rbi
37
+ # # post.rbi
38
+ # # typed: true
39
+ # class Post
40
+ # sig { returns(T.nilable(::String)) }
41
+ # def title; end
42
+ #
43
+ # sig { params(title: T.nilable(::String)).returns(T.nilable(::String)) }
44
+ # def title=(title); end
45
+ #
46
+ # sig { returns(::String) }
47
+ # def description; end
48
+ #
49
+ # sig { params(description: ::String).returns(::String) }
50
+ # def description=(description); end
51
+ #
52
+ # sig { returns(T.nilable(T::Boolean)) }
53
+ # def published?; end
54
+ #
55
+ # sig { params(published: T.nilable(T::Boolean)).returns(T.nilable(T::Boolean)) }
56
+ # def published=(published); end
57
+ #
58
+ # ssig { returns(T.nilable(T::Boolean)) }
59
+ # def enabled; end
60
+ #
61
+ # sig { params(enabled: T.nilable(T::Boolean)).returns(T.nilable(T::Boolean)) }
62
+ # def enabled=(enabled); end
63
+ # end
64
+ # ~~~
65
+ class SmartProperties < Base
66
+ extend T::Sig
67
+
68
+ sig do
69
+ override
70
+ .params(
71
+ root: Parlour::RbiGenerator::Namespace,
72
+ constant: T.class_of(::SmartProperties)
73
+ )
74
+ .void
75
+ end
76
+ def decorate(root, constant)
77
+ properties = T.let(
78
+ T.unsafe(constant).properties,
79
+ ::SmartProperties::PropertyCollection
80
+ )
81
+ return if properties.keys.empty?
82
+
83
+ instance_methods = constant.instance_methods(false).map(&:to_s).to_set
84
+
85
+ root.path(constant) do |k|
86
+ properties.values.each do |property|
87
+ generate_methods_for_property(k, property) do |method_name|
88
+ !instance_methods.include?(method_name.to_sym)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ sig { override.returns(T::Enumerable[Module]) }
95
+ def gather_constants
96
+ classes = T.cast(ObjectSpace.each_object(Class), T::Enumerable[Class])
97
+ classes.select do |c|
98
+ c < ::SmartProperties
99
+ end.reject do |c|
100
+ c.name.nil? || c == ::SmartProperties::Validations::Ancestor
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ sig do
107
+ params(
108
+ klass: Parlour::RbiGenerator::Namespace,
109
+ property: ::SmartProperties::Property,
110
+ block: T.proc.params(arg: String).returns(T::Boolean)
111
+ ).void
112
+ end
113
+ def generate_methods_for_property(klass, property, &block)
114
+ type = type_for(property)
115
+
116
+ if property.writable?
117
+ name = property.name.to_s
118
+ method_name = "#{name}="
119
+
120
+ klass.create_method(
121
+ method_name,
122
+ parameters: [Parlour::RbiGenerator::Parameter.new(name, type: type)],
123
+ return_type: type
124
+ ) if block.call(method_name)
125
+ end
126
+
127
+ klass.create_method(property.reader.to_s, return_type: type) if block.call(property.reader.to_s)
128
+ end
129
+
130
+ BOOLEANS = T.let([
131
+ [true, false],
132
+ [false, true],
133
+ ].freeze, T::Array[[T::Boolean, T::Boolean]])
134
+
135
+ sig { params(property: ::SmartProperties::Property).returns(String) }
136
+ def type_for(property)
137
+ converter = property.converter
138
+ return "T.untyped" if converter
139
+
140
+ accepter = property.accepter
141
+
142
+ type = if accepter.nil? || accepter.respond_to?(:to_proc)
143
+ "T.untyped"
144
+ elsif accepter == Array
145
+ "T::Array[T.untyped]"
146
+ elsif BOOLEANS.include?(accepter)
147
+ "T::Boolean"
148
+ elsif Array(accepter).all? { |a| a.is_a?(Module) }
149
+ accepters = Array(accepter)
150
+ types = accepters.map { |mod| name_of(mod) }.join(', ')
151
+ types = "T.any(#{types})" if accepters.size > 1
152
+ types
153
+ else
154
+ "T.untyped"
155
+ end
156
+
157
+ required_attr = property.instance_variable_get(:@required)
158
+ required = !required_attr.is_a?(Proc) && !!required_attr
159
+ property_required = type == "T.untyped" || required
160
+ type = "T.nilable(#{type})" unless property_required
161
+
162
+ type
163
+ end
164
+
165
+ sig { params(type: Module).returns(String) }
166
+ def name_of(type)
167
+ name = Module.instance_method(:name).bind(type).call
168
+ name.start_with?("::") ? name : "::#{name}"
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,378 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "parlour"
5
+ require "tapioca/core_ext/class"
6
+
7
+ begin
8
+ require "state_machines"
9
+ rescue LoadError
10
+ # means StateMachines is not installed,
11
+ # so let's not even define the generator.
12
+ return
13
+ end
14
+
15
+ module Tapioca
16
+ module Compilers
17
+ module Dsl
18
+ # `RbiGenerator::StateMachines` generates RBI files for classes that setup a `state_machine`
19
+ # (see https://github.com/state-machines/state_machines). The generator also processes the extra
20
+ # methods generated by [StateMachines Active Record](https://github.com/state-machines/state_machines-activerecord)
21
+ # and [StateMachines ActiveModel](https://github.com/state-machines/state_machines-activemodel) integrations.
22
+ #
23
+ # For example, with the following `Vehicle` class:
24
+ #
25
+ # ~~~rb
26
+ # class Vehicle
27
+ # state_machine :alarm_state, initial: :active, namespace: :'alarm' do
28
+ # event :enable do
29
+ # transition all => :active
30
+ # end
31
+ #
32
+ # event :disable do
33
+ # transition all => :off
34
+ # end
35
+ #
36
+ # state :active, :value => 1
37
+ # state :off, :value => 0
38
+ # end
39
+ # end
40
+ # ~~~
41
+ #
42
+ # this generator will produce the RBI file `vehicle.rbi` with the following content:
43
+ #
44
+ # ~~~rbi
45
+ # # vehicle.rbi
46
+ # # typed: true
47
+ # class Vehicle
48
+ # include Vehicle::StateMachineInstanceHelperModule
49
+ # extend Vehicle::StateMachineClassHelperModule
50
+ # end
51
+ #
52
+ # module Vehicle::StateMachineClassHelperModule
53
+ # sig { params(event: T.any(String, Symbol)).returns(String) }
54
+ # def human_alarm_state_event_name(event); end
55
+ #
56
+ # sig { params(state: T.any(String, Symbol)).returns(String) }
57
+ # def human_alarm_state_name(state); end
58
+ # end
59
+ #
60
+ # module Vehicle::StateMachineInstanceHelperModule
61
+ # sig { returns(T::Boolean) }
62
+ # def alarm_active?; end
63
+ #
64
+ # sig { returns(T::Boolean) }
65
+ # def alarm_off?; end
66
+ #
67
+ # sig { returns(Integer) }
68
+ # def alarm_state; end
69
+ #
70
+ # sig { params(value: Integer).returns(Integer) }
71
+ # def alarm_state=(value); end
72
+ #
73
+ # sig { params(state: T.any(String, Symbol)).returns(T::Boolean) }
74
+ # def alarm_state?(state); end
75
+ #
76
+ # sig { params(args: T.untyped).returns(T::Array[T.any(String, Symbol)]) }
77
+ # def alarm_state_events(*args); end
78
+ #
79
+ # sig { returns(T.any(String, Symbol)) }
80
+ # def alarm_state_name; end
81
+ #
82
+ # sig { params(args: T.untyped).returns(T::Array[::StateMachines::Transition]) }
83
+ # def alarm_state_paths(*args); end
84
+ #
85
+ # sig { params(args: T.untyped).returns(T::Array[::StateMachines::Transition]) }
86
+ # def alarm_state_transitions(*args); end
87
+ #
88
+ # sig { returns(T::Boolean) }
89
+ # def can_disable_alarm?; end
90
+ #
91
+ # sig { returns(T::Boolean) }
92
+ # def can_enable_alarm?; end
93
+ #
94
+ # sig { params(args: T.untyped).returns(T::Boolean) }
95
+ # def disable_alarm(*args); end
96
+ #
97
+ # sig { params(args: T.untyped).returns(T::Boolean) }
98
+ # def disable_alarm!(*args); end
99
+ #
100
+ # sig { params(args: T.untyped).returns(T.nilable(::StateMachines::Transition)) }
101
+ # def disable_alarm_transition(*args); end
102
+ #
103
+ # sig { params(args: T.untyped).returns(T::Boolean) }
104
+ # def enable_alarm(*args); end
105
+ #
106
+ # sig { params(args: T.untyped).returns(T::Boolean) }
107
+ # def enable_alarm!(*args); end
108
+ #
109
+ # sig { params(args: T.untyped).returns(T.nilable(::StateMachines::Transition)) }
110
+ # def enable_alarm_transition(*args); end
111
+ #
112
+ # sig { params(event: T.any(String, Symbol), args: T.untyped).returns(T::Boolean) }
113
+ # def fire_alarm_state_event(event, *args); end
114
+ #
115
+ # sig { returns(String) }
116
+ # def human_alarm_state_name; end
117
+ # end
118
+ # ~~~
119
+ class StateMachines < Base
120
+ extend T::Sig
121
+
122
+ sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: ::StateMachines::ClassMethods).void }
123
+ def decorate(root, constant)
124
+ return if constant.state_machines.empty?
125
+
126
+ instance_module_name = "#{constant}::StateMachineInstanceHelperModule"
127
+ class_module_name = "#{constant}::StateMachineClassHelperModule"
128
+
129
+ instance_module = root.create_module(instance_module_name)
130
+ class_module = root.create_module(class_module_name)
131
+
132
+ constant.state_machines.each_value do |machine|
133
+ state_type = state_type_for(machine)
134
+
135
+ define_state_accessor(instance_module, machine, state_type)
136
+ define_state_predicate(instance_module, machine)
137
+ define_event_helpers(instance_module, machine)
138
+ define_path_helpers(instance_module, machine)
139
+ define_name_helpers(instance_module, class_module, machine)
140
+ define_scopes(class_module, machine)
141
+
142
+ define_state_methods(instance_module, machine)
143
+ define_event_methods(instance_module, machine)
144
+ end
145
+
146
+ matching_integration_name = ::StateMachines::Integrations.match(constant)&.integration_name
147
+
148
+ case matching_integration_name
149
+ when :active_record
150
+ define_activerecord_methods(instance_module)
151
+ end
152
+
153
+ root.path(constant) do |klass|
154
+ klass.create_include(instance_module_name)
155
+ klass.create_extend(class_module_name)
156
+ end
157
+ end
158
+
159
+ sig { override.returns(T::Enumerable[Module]) }
160
+ def gather_constants
161
+ Object.descendants.select { |mod| mod < ::StateMachines::InstanceMethods }
162
+ end
163
+
164
+ private
165
+
166
+ sig { params(machine: ::StateMachines::Machine).returns(String) }
167
+ def state_type_for(machine)
168
+ value_types = machine.states.map { |state| state.value.class.name }.uniq
169
+
170
+ if value_types.size == 1
171
+ value_types.first
172
+ else
173
+ "T.any(#{value_types.join(', ')})"
174
+ end
175
+ end
176
+
177
+ sig { params(instance_module: Parlour::RbiGenerator::Namespace).void }
178
+ def define_activerecord_methods(instance_module)
179
+ create_method(
180
+ instance_module,
181
+ "changed_for_autosave?",
182
+ return_type: "T::Boolean"
183
+ )
184
+ end
185
+
186
+ sig { params(instance_module: Parlour::RbiGenerator::Namespace, machine: ::StateMachines::Machine).void }
187
+ def define_state_methods(instance_module, machine)
188
+ machine.states.each do |state|
189
+ create_method(
190
+ instance_module,
191
+ "#{state.qualified_name}?",
192
+ return_type: "T::Boolean"
193
+ )
194
+ end
195
+ end
196
+
197
+ sig { params(instance_module: Parlour::RbiGenerator::Namespace, machine: ::StateMachines::Machine).void }
198
+ def define_event_methods(instance_module, machine)
199
+ machine.events.each do |event|
200
+ create_method(
201
+ instance_module,
202
+ "can_#{event.qualified_name}?",
203
+ return_type: "T::Boolean"
204
+ )
205
+ create_method(
206
+ instance_module,
207
+ "#{event.qualified_name}_transition",
208
+ parameters: [Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped")],
209
+ return_type: "T.nilable(::StateMachines::Transition)"
210
+ )
211
+ create_method(
212
+ instance_module,
213
+ event.qualified_name.to_s,
214
+ parameters: [Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped")],
215
+ return_type: "T::Boolean"
216
+ )
217
+ create_method(
218
+ instance_module,
219
+ "#{event.qualified_name}!",
220
+ parameters: [Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped")],
221
+ return_type: "T::Boolean"
222
+ )
223
+ end
224
+ end
225
+
226
+ sig do
227
+ params(
228
+ instance_module: Parlour::RbiGenerator::Namespace,
229
+ machine: ::StateMachines::Machine,
230
+ state_type: String
231
+ ).void
232
+ end
233
+ def define_state_accessor(instance_module, machine, state_type)
234
+ attribute = machine.attribute.to_s
235
+ create_method(
236
+ instance_module,
237
+ attribute,
238
+ return_type: state_type
239
+ )
240
+ create_method(
241
+ instance_module,
242
+ "#{attribute}=",
243
+ parameters: [Parlour::RbiGenerator::Parameter.new("value", type: state_type)],
244
+ return_type: state_type
245
+ )
246
+ end
247
+
248
+ sig { params(instance_module: Parlour::RbiGenerator::Namespace, machine: ::StateMachines::Machine).void }
249
+ def define_state_predicate(instance_module, machine)
250
+ create_method(
251
+ instance_module,
252
+ "#{machine.name}?",
253
+ parameters: [Parlour::RbiGenerator::Parameter.new("state", type: "T.any(String, Symbol)")],
254
+ return_type: "T::Boolean"
255
+ )
256
+ end
257
+
258
+ sig { params(instance_module: Parlour::RbiGenerator::Namespace, machine: ::StateMachines::Machine).void }
259
+ def define_event_helpers(instance_module, machine)
260
+ events_attribute = machine.attribute(:events).to_s
261
+ transitions_attribute = machine.attribute(:transitions).to_s
262
+ event_attribute = machine.attribute(:event).to_s
263
+ event_transition_attribute = machine.attribute(:event_transition).to_s
264
+
265
+ create_method(
266
+ instance_module,
267
+ events_attribute,
268
+ parameters: [Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped")],
269
+ return_type: "T::Array[T.any(String, Symbol)]"
270
+ )
271
+ create_method(
272
+ instance_module,
273
+ transitions_attribute,
274
+ parameters: [Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped")],
275
+ return_type: "T::Array[::StateMachines::Transition]"
276
+ )
277
+ create_method(
278
+ instance_module,
279
+ "fire_#{event_attribute}",
280
+ parameters: [
281
+ Parlour::RbiGenerator::Parameter.new("event", type: "T.any(String, Symbol)"),
282
+ Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped"),
283
+ ],
284
+ return_type: "T::Boolean"
285
+ )
286
+ if machine.action
287
+ create_method(
288
+ instance_module,
289
+ event_attribute,
290
+ return_type: "T.nilable(Symbol)"
291
+ )
292
+ create_method(
293
+ instance_module,
294
+ "#{event_attribute}=",
295
+ parameters: [Parlour::RbiGenerator::Parameter.new("value", type: "T.any(String, Symbol)")],
296
+ return_type: "T.any(String, Symbol)"
297
+ )
298
+ create_method(
299
+ instance_module,
300
+ event_transition_attribute,
301
+ return_type: "T.nilable(::StateMachines::Transition)"
302
+ )
303
+ create_method(
304
+ instance_module,
305
+ "#{event_transition_attribute}=",
306
+ parameters: [Parlour::RbiGenerator::Parameter.new("value", type: "::StateMachines::Transition")],
307
+ return_type: "::StateMachines::Transition"
308
+ )
309
+ end
310
+ end
311
+
312
+ sig { params(instance_module: Parlour::RbiGenerator::Namespace, machine: ::StateMachines::Machine).void }
313
+ def define_path_helpers(instance_module, machine)
314
+ paths_attribute = machine.attribute(:paths).to_s
315
+
316
+ create_method(
317
+ instance_module,
318
+ paths_attribute,
319
+ parameters: [Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped")],
320
+ return_type: "T::Array[::StateMachines::Transition]"
321
+ )
322
+ end
323
+
324
+ sig do
325
+ params(
326
+ instance_module: Parlour::RbiGenerator::Namespace,
327
+ class_module: Parlour::RbiGenerator::Namespace,
328
+ machine: ::StateMachines::Machine
329
+ ).void
330
+ end
331
+ def define_name_helpers(instance_module, class_module, machine)
332
+ name_attribute = machine.attribute(:name).to_s
333
+ event_name_attribute = machine.attribute(:event_name).to_s
334
+
335
+ create_method(
336
+ class_module,
337
+ "human_#{name_attribute}",
338
+ parameters: [Parlour::RbiGenerator::Parameter.new("state", type: "T.any(String, Symbol)")],
339
+ return_type: "String"
340
+ )
341
+ create_method(
342
+ class_module,
343
+ "human_#{event_name_attribute}",
344
+ parameters: [Parlour::RbiGenerator::Parameter.new("event", type: "T.any(String, Symbol)")],
345
+ return_type: "String"
346
+ )
347
+ create_method(
348
+ instance_module,
349
+ name_attribute,
350
+ return_type: "T.any(String, Symbol)"
351
+ )
352
+ create_method(
353
+ instance_module,
354
+ "human_#{name_attribute}",
355
+ return_type: "String"
356
+ )
357
+ end
358
+
359
+ sig { params(class_module: Parlour::RbiGenerator::Namespace, machine: ::StateMachines::Machine).void }
360
+ def define_scopes(class_module, machine)
361
+ helper_modules = machine.instance_variable_get(:@helper_modules)
362
+ class_methods = helper_modules[:class].instance_methods(false)
363
+
364
+ class_methods
365
+ .select { |method| method.to_s.start_with?("with_", "without_") }
366
+ .each do |method|
367
+ create_method(
368
+ class_module,
369
+ method.to_s,
370
+ parameters: [Parlour::RbiGenerator::Parameter.new("*states", type: "T.any(String, Symbol)")],
371
+ return_type: "T.untyped"
372
+ )
373
+ end
374
+ end
375
+ end
376
+ end
377
+ end
378
+ end