tapioca 0.2.8 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +25 -1
- data/README.md +23 -2
- data/Rakefile +15 -4
- data/lib/tapioca.rb +15 -9
- data/lib/tapioca/cli.rb +41 -12
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +129 -0
- data/lib/tapioca/compilers/dsl/action_mailer.rb +65 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +285 -0
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +387 -0
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +112 -0
- data/lib/tapioca/compilers/dsl/active_record_identity_cache.rb +213 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +100 -0
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +170 -0
- data/lib/tapioca/compilers/dsl/active_resource.rb +140 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +126 -0
- data/lib/tapioca/compilers/dsl/base.rb +165 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +96 -0
- data/lib/tapioca/compilers/dsl/protobuf.rb +144 -0
- data/lib/tapioca/compilers/dsl/smart_properties.rb +173 -0
- data/lib/tapioca/compilers/dsl/state_machines.rb +378 -0
- data/lib/tapioca/compilers/dsl/url_helpers.rb +83 -0
- data/lib/tapioca/compilers/dsl_compiler.rb +121 -0
- data/lib/tapioca/compilers/requires_compiler.rb +67 -0
- data/lib/tapioca/compilers/sorbet.rb +34 -0
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +210 -50
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +3 -17
- data/lib/tapioca/compilers/todos_compiler.rb +32 -0
- data/lib/tapioca/config.rb +42 -0
- data/lib/tapioca/config_builder.rb +75 -0
- data/lib/tapioca/constant_locator.rb +1 -0
- data/lib/tapioca/core_ext/class.rb +23 -0
- data/lib/tapioca/gemfile.rb +44 -9
- data/lib/tapioca/generator.rb +248 -70
- data/lib/tapioca/loader.rb +20 -9
- data/lib/tapioca/sorbet_config_parser.rb +77 -0
- data/lib/tapioca/version.rb +1 -1
- metadata +33 -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
|