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.
- checksums.yaml +4 -4
- data/Gemfile +25 -1
- data/README.md +23 -2
- data/Rakefile +15 -4
- data/lib/tapioca.rb +8 -2
- data/lib/tapioca/cli.rb +32 -3
- 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 +267 -0
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +393 -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 +92 -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 +171 -26
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +1 -20
- data/lib/tapioca/compilers/todos_compiler.rb +32 -0
- data/lib/tapioca/config.rb +14 -6
- data/lib/tapioca/config_builder.rb +22 -9
- data/lib/tapioca/constant_locator.rb +1 -0
- data/lib/tapioca/core_ext/class.rb +23 -0
- data/lib/tapioca/gemfile.rb +32 -9
- data/lib/tapioca/generator.rb +231 -23
- data/lib/tapioca/loader.rb +30 -9
- data/lib/tapioca/version.rb +1 -1
- 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
|