tapioca 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +25 -1
  3. data/README.md +12 -0
  4. data/Rakefile +15 -4
  5. data/lib/tapioca.rb +2 -0
  6. data/lib/tapioca/cli.rb +24 -2
  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 +285 -0
  10. data/lib/tapioca/compilers/dsl/active_record_columns.rb +379 -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 +163 -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 +83 -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/symbol_table/symbol_generator.rb +141 -24
  26. data/lib/tapioca/config.rb +11 -6
  27. data/lib/tapioca/config_builder.rb +19 -9
  28. data/lib/tapioca/constant_locator.rb +1 -0
  29. data/lib/tapioca/core_ext/class.rb +23 -0
  30. data/lib/tapioca/generator.rb +187 -21
  31. data/lib/tapioca/loader.rb +20 -9
  32. data/lib/tapioca/sorbet_config_parser.rb +77 -0
  33. data/lib/tapioca/version.rb +1 -1
  34. metadata +29 -51
@@ -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