tapioca 0.4.27 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +14 -14
- data/README.md +2 -2
- data/Rakefile +5 -7
- data/exe/tapioca +2 -2
- data/lib/tapioca/cli.rb +256 -2
- data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
- data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
- data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
- data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
- data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
- data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
- data/lib/tapioca/compilers/dsl/active_support_concern.rb +108 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
- data/lib/tapioca/compilers/dsl/base.rb +96 -82
- data/lib/tapioca/compilers/dsl/config.rb +111 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
- data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
- data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
- data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
- data/lib/tapioca/compilers/dsl/smart_properties.rb +19 -31
- data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
- data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
- data/lib/tapioca/compilers/dsl_compiler.rb +22 -38
- data/lib/tapioca/compilers/requires_compiler.rb +2 -2
- data/lib/tapioca/compilers/sorbet.rb +26 -5
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +138 -153
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
- data/lib/tapioca/compilers/todos_compiler.rb +1 -1
- data/lib/tapioca/config.rb +2 -0
- data/lib/tapioca/config_builder.rb +4 -2
- data/lib/tapioca/constant_locator.rb +6 -8
- data/lib/tapioca/gemfile.rb +2 -4
- data/lib/tapioca/generator.rb +124 -40
- data/lib/tapioca/generic_type_registry.rb +25 -98
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
- data/lib/tapioca/internal.rb +2 -9
- data/lib/tapioca/loader.rb +13 -33
- data/lib/tapioca/rbi_ext/model.rb +122 -0
- data/lib/tapioca/reflection.rb +131 -0
- data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
- data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +2 -1
- metadata +34 -22
- data/lib/tapioca/cli/main.rb +0 -146
- data/lib/tapioca/core_ext/class.rb +0 -28
- data/lib/tapioca/core_ext/string.rb +0 -18
- data/lib/tapioca/rbi/model.rb +0 -405
- data/lib/tapioca/rbi/printer.rb +0 -410
- data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
- data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
- data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
- data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
- data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -1,8 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
|
6
4
|
begin
|
7
5
|
require "frozen_record"
|
8
6
|
rescue LoadError
|
@@ -67,18 +65,18 @@ module Tapioca
|
|
67
65
|
class FrozenRecord < Base
|
68
66
|
extend T::Sig
|
69
67
|
|
70
|
-
sig { override.params(root:
|
68
|
+
sig { override.params(root: RBI::Tree, constant: T.class_of(::FrozenRecord::Base)).void }
|
71
69
|
def decorate(root, constant)
|
72
70
|
attributes = constant.attributes
|
73
71
|
return if attributes.empty?
|
74
72
|
|
75
|
-
root.
|
73
|
+
root.create_path(constant) do |record|
|
76
74
|
module_name = "FrozenRecordAttributeMethods"
|
77
75
|
|
78
76
|
record.create_module(module_name) do |mod|
|
79
77
|
attributes.each do |attribute|
|
80
|
-
create_method(
|
81
|
-
create_method(
|
78
|
+
mod.create_method("#{attribute}?", return_type: "T::Boolean")
|
79
|
+
mod.create_method(attribute.to_s, return_type: "T.untyped")
|
82
80
|
end
|
83
81
|
end
|
84
82
|
|
@@ -88,7 +86,7 @@ module Tapioca
|
|
88
86
|
|
89
87
|
sig { override.returns(T::Enumerable[Module]) }
|
90
88
|
def gather_constants
|
91
|
-
::FrozenRecord::Base.
|
89
|
+
descendants_of(::FrozenRecord::Base).reject(&:abstract_class?)
|
92
90
|
end
|
93
91
|
end
|
94
92
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
|
6
4
|
begin
|
7
5
|
require "rails/railtie"
|
8
6
|
require "identity_cache"
|
@@ -67,23 +65,16 @@ module Tapioca
|
|
67
65
|
|
68
66
|
COLLECTION_TYPE = T.let(
|
69
67
|
->(type) { "T::Array[::#{type}]" },
|
70
|
-
T.proc.params(type: Module).returns(String)
|
68
|
+
T.proc.params(type: T.any(Module, String)).returns(String)
|
71
69
|
)
|
72
70
|
|
73
|
-
sig
|
74
|
-
override
|
75
|
-
.params(
|
76
|
-
root: Parlour::RbiGenerator::Namespace,
|
77
|
-
constant: T.class_of(::ActiveRecord::Base)
|
78
|
-
)
|
79
|
-
.void
|
80
|
-
end
|
71
|
+
sig { override.params(root: RBI::Tree, constant: T.class_of(::ActiveRecord::Base)).void }
|
81
72
|
def decorate(root, constant)
|
82
73
|
caches = constant.send(:all_cached_associations)
|
83
74
|
cache_indexes = constant.send(:cache_indexes)
|
84
75
|
return if caches.empty? && cache_indexes.empty?
|
85
76
|
|
86
|
-
root.
|
77
|
+
root.create_path(constant) do |model|
|
87
78
|
cache_manys = constant.send(:cached_has_manys)
|
88
79
|
cache_ones = constant.send(:cached_has_ones)
|
89
80
|
cache_belongs = constant.send(:cached_belongs_tos)
|
@@ -108,7 +99,7 @@ module Tapioca
|
|
108
99
|
|
109
100
|
sig { override.returns(T::Enumerable[Module]) }
|
110
101
|
def gather_constants
|
111
|
-
::ActiveRecord::Base.
|
102
|
+
descendants_of(::ActiveRecord::Base).select do |klass|
|
112
103
|
klass < ::IdentityCache::WithoutPrimaryIndex
|
113
104
|
end
|
114
105
|
end
|
@@ -119,8 +110,7 @@ module Tapioca
|
|
119
110
|
params(
|
120
111
|
field: T.untyped,
|
121
112
|
returns_collection: T::Boolean
|
122
|
-
)
|
123
|
-
.returns(String)
|
113
|
+
).returns(String)
|
124
114
|
end
|
125
115
|
def type_for_field(field, returns_collection:)
|
126
116
|
cache_type = field.reflection.compute_class(field.reflection.class_name)
|
@@ -136,10 +126,9 @@ module Tapioca
|
|
136
126
|
sig do
|
137
127
|
params(
|
138
128
|
field: T.untyped,
|
139
|
-
klass:
|
129
|
+
klass: RBI::Scope,
|
140
130
|
returns_collection: T::Boolean
|
141
|
-
)
|
142
|
-
.void
|
131
|
+
).void
|
143
132
|
end
|
144
133
|
def create_fetch_field_methods(field, klass, returns_collection:)
|
145
134
|
name = field.cached_accessor_name.to_s
|
@@ -156,21 +145,36 @@ module Tapioca
|
|
156
145
|
sig do
|
157
146
|
params(
|
158
147
|
field: T.untyped,
|
159
|
-
klass:
|
148
|
+
klass: RBI::Scope,
|
160
149
|
constant: T.class_of(::ActiveRecord::Base),
|
161
|
-
)
|
162
|
-
.void
|
150
|
+
).void
|
163
151
|
end
|
164
152
|
def create_fetch_by_methods(field, klass, constant)
|
153
|
+
is_cache_index = field.instance_variable_defined?(:@attribute_proc)
|
154
|
+
|
155
|
+
# Both `cache_index` and `cache_attribute` generate aliased methods
|
156
|
+
create_aliased_fetch_by_methods(field, klass, constant)
|
157
|
+
|
158
|
+
# If the method used was `cache_index` a few extra methods are created
|
159
|
+
create_index_fetch_by_methods(field, klass, constant) if is_cache_index
|
160
|
+
end
|
161
|
+
|
162
|
+
sig do
|
163
|
+
params(
|
164
|
+
field: T.untyped,
|
165
|
+
klass: RBI::Scope,
|
166
|
+
constant: T.class_of(::ActiveRecord::Base),
|
167
|
+
).void
|
168
|
+
end
|
169
|
+
def create_index_fetch_by_methods(field, klass, constant)
|
165
170
|
field_length = field.key_fields.length
|
166
171
|
fields_name = field.key_fields.join("_and_")
|
167
|
-
|
172
|
+
name = "fetch_by_#{fields_name}"
|
168
173
|
parameters = field.key_fields.map do |arg|
|
169
|
-
|
174
|
+
create_param(arg.to_s, type: "T.untyped")
|
170
175
|
end
|
171
|
-
parameters <<
|
176
|
+
parameters << create_kw_opt_param("includes", default: "nil", type: "T.untyped")
|
172
177
|
|
173
|
-
name = "fetch_by_#{fields_name}"
|
174
178
|
if field.unique
|
175
179
|
klass.create_method(
|
176
180
|
"#{name}!",
|
@@ -195,18 +199,51 @@ module Tapioca
|
|
195
199
|
end
|
196
200
|
|
197
201
|
if field_length == 1
|
198
|
-
name = "fetch_multi_by_#{fields_name}"
|
199
202
|
klass.create_method(
|
200
|
-
|
203
|
+
"fetch_multi_by_#{fields_name}",
|
201
204
|
class_method: true,
|
202
205
|
parameters: [
|
203
|
-
|
204
|
-
|
206
|
+
create_param("index_values", type: "T::Enumerable[T.untyped]"),
|
207
|
+
create_kw_opt_param("includes", default: "nil", type: "T.untyped"),
|
205
208
|
],
|
206
209
|
return_type: COLLECTION_TYPE.call(constant)
|
207
210
|
)
|
208
211
|
end
|
209
212
|
end
|
213
|
+
|
214
|
+
sig do
|
215
|
+
params(
|
216
|
+
field: T.untyped,
|
217
|
+
klass: RBI::Scope,
|
218
|
+
constant: T.class_of(::ActiveRecord::Base),
|
219
|
+
).void
|
220
|
+
end
|
221
|
+
def create_aliased_fetch_by_methods(field, klass, constant)
|
222
|
+
type, _ = ActiveRecordColumnTypeHelper.new(constant).type_for(field.alias_name.to_s)
|
223
|
+
multi_type = type.delete_prefix("T.nilable(").delete_suffix(")").delete_prefix("::")
|
224
|
+
length = field.key_fields.length
|
225
|
+
suffix = field.send(:fetch_method_suffix)
|
226
|
+
|
227
|
+
parameters = field.key_fields.map do |arg|
|
228
|
+
create_param(arg.to_s, type: "T.untyped")
|
229
|
+
end
|
230
|
+
|
231
|
+
klass.create_method(
|
232
|
+
"fetch_#{suffix}",
|
233
|
+
class_method: true,
|
234
|
+
parameters: parameters,
|
235
|
+
return_type: type
|
236
|
+
)
|
237
|
+
|
238
|
+
if length == 1
|
239
|
+
klass.create_method(
|
240
|
+
"fetch_multi_#{suffix}",
|
241
|
+
class_method: true,
|
242
|
+
parameters: [create_param("keys", type: "T::Enumerable[T.untyped]")],
|
243
|
+
return_type: COLLECTION_TYPE.call(multi_type)
|
244
|
+
)
|
245
|
+
end
|
246
|
+
end
|
210
247
|
end
|
211
248
|
end
|
212
249
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
|
-
require "parlour"
|
4
3
|
|
5
4
|
begin
|
6
5
|
require "google/protobuf"
|
@@ -62,67 +61,18 @@ module Tapioca
|
|
62
61
|
# end
|
63
62
|
# ~~~
|
64
63
|
class Protobuf < Base
|
65
|
-
# Parlour doesn't support type members out of the box, so adding the
|
66
|
-
# ability to do that here. This should be upstreamed.
|
67
|
-
class TypeMember < Parlour::RbiGenerator::RbiObject
|
68
|
-
extend T::Sig
|
69
|
-
|
70
|
-
sig { params(other: Object).returns(T::Boolean) }
|
71
|
-
def ==(other)
|
72
|
-
TypeMember === other && name == other.name
|
73
|
-
end
|
74
|
-
|
75
|
-
sig do
|
76
|
-
override
|
77
|
-
.params(indent_level: Integer, options: Parlour::RbiGenerator::Options)
|
78
|
-
.returns(T::Array[String])
|
79
|
-
end
|
80
|
-
def generate_rbi(indent_level, options)
|
81
|
-
[options.indented(indent_level, "#{name} = type_member")]
|
82
|
-
end
|
83
|
-
|
84
|
-
sig do
|
85
|
-
override
|
86
|
-
.params(others: T::Array[Parlour::RbiGenerator::RbiObject])
|
87
|
-
.returns(T::Boolean)
|
88
|
-
end
|
89
|
-
def mergeable?(others)
|
90
|
-
others.all? { |other| self == other }
|
91
|
-
end
|
92
|
-
|
93
|
-
sig { override.params(others: T::Array[Parlour::RbiGenerator::RbiObject]).void }
|
94
|
-
def merge_into_self(others); end
|
95
|
-
|
96
|
-
sig { override.returns(String) }
|
97
|
-
def describe
|
98
|
-
"Type Member (#{name})"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
64
|
class Field < T::Struct
|
103
65
|
prop :name, String
|
104
66
|
prop :type, String
|
105
67
|
prop :init_type, String
|
106
68
|
prop :default, String
|
107
|
-
|
108
|
-
extend T::Sig
|
109
|
-
|
110
|
-
sig { returns(Parlour::RbiGenerator::Parameter) }
|
111
|
-
def to_init
|
112
|
-
Parlour::RbiGenerator::Parameter.new("#{name}:", type: init_type, default: default)
|
113
|
-
end
|
114
69
|
end
|
115
70
|
|
116
71
|
extend T::Sig
|
117
72
|
|
118
|
-
sig
|
119
|
-
override.params(
|
120
|
-
root: Parlour::RbiGenerator::Namespace,
|
121
|
-
constant: Module
|
122
|
-
).void
|
123
|
-
end
|
73
|
+
sig { override.params(root: RBI::Tree, constant: Module).void }
|
124
74
|
def decorate(root, constant)
|
125
|
-
root.
|
75
|
+
root.create_path(constant) do |klass|
|
126
76
|
if constant == Google::Protobuf::RepeatedField
|
127
77
|
create_type_members(klass, "Elem")
|
128
78
|
elsif constant == Google::Protobuf::Map
|
@@ -132,7 +82,11 @@ module Tapioca
|
|
132
82
|
fields = descriptor.map { |desc| create_descriptor_method(klass, desc) }
|
133
83
|
fields.sort_by!(&:name)
|
134
84
|
|
135
|
-
|
85
|
+
parameters = fields.map do |field|
|
86
|
+
create_kw_opt_param(field.name, type: field.init_type, default: field.default)
|
87
|
+
end
|
88
|
+
|
89
|
+
klass.create_method("initialize", parameters: parameters, return_type: "void")
|
136
90
|
end
|
137
91
|
end
|
138
92
|
end
|
@@ -146,12 +100,12 @@ module Tapioca
|
|
146
100
|
|
147
101
|
private
|
148
102
|
|
149
|
-
sig { params(klass:
|
103
|
+
sig { params(klass: RBI::Scope, names: String).void }
|
150
104
|
def create_type_members(klass, *names)
|
151
105
|
klass.create_extend("T::Generic")
|
152
106
|
|
153
107
|
names.each do |name|
|
154
|
-
klass.
|
108
|
+
klass.create_type_member(name)
|
155
109
|
end
|
156
110
|
end
|
157
111
|
|
@@ -186,34 +140,34 @@ module Tapioca
|
|
186
140
|
# how Google names map entries.
|
187
141
|
# https://github.com/protocolbuffers/protobuf/blob/f82e26/ruby/ext/google/protobuf_c/defs.c#L1963-L1966
|
188
142
|
if descriptor.submsg_name.to_s.end_with?("_MapEntry_#{descriptor.name}")
|
189
|
-
key = descriptor.subtype.lookup(
|
190
|
-
value = descriptor.subtype.lookup(
|
143
|
+
key = descriptor.subtype.lookup("key")
|
144
|
+
value = descriptor.subtype.lookup("value")
|
191
145
|
|
192
146
|
key_type = type_of(key)
|
193
147
|
value_type = type_of(value)
|
194
148
|
type = "Google::Protobuf::Map[#{key_type}, #{value_type}]"
|
195
149
|
|
196
150
|
default_args = [key.type.inspect, value.type.inspect]
|
197
|
-
default_args << value_type if
|
151
|
+
default_args << value_type if [:enum, :message].include?(value.type)
|
198
152
|
|
199
153
|
Field.new(
|
200
154
|
name: descriptor.name,
|
201
155
|
type: type,
|
202
156
|
init_type: "T.any(#{type}, T::Hash[#{key_type}, #{value_type}])",
|
203
|
-
default: "Google::Protobuf::Map.new(#{default_args.join(
|
157
|
+
default: "Google::Protobuf::Map.new(#{default_args.join(", ")})"
|
204
158
|
)
|
205
159
|
else
|
206
160
|
elem_type = type_of(descriptor)
|
207
161
|
type = "Google::Protobuf::RepeatedField[#{elem_type}]"
|
208
162
|
|
209
163
|
default_args = [descriptor.type.inspect]
|
210
|
-
default_args << elem_type if
|
164
|
+
default_args << elem_type if [:enum, :message].include?(descriptor.type)
|
211
165
|
|
212
166
|
Field.new(
|
213
167
|
name: descriptor.name,
|
214
168
|
type: type,
|
215
169
|
init_type: "T.any(#{type}, T::Array[#{elem_type}])",
|
216
|
-
default: "Google::Protobuf::RepeatedField.new(#{default_args.join(
|
170
|
+
default: "Google::Protobuf::RepeatedField.new(#{default_args.join(", ")})"
|
217
171
|
)
|
218
172
|
end
|
219
173
|
else
|
@@ -230,25 +184,21 @@ module Tapioca
|
|
230
184
|
|
231
185
|
sig do
|
232
186
|
params(
|
233
|
-
klass:
|
187
|
+
klass: RBI::Scope,
|
234
188
|
desc: Google::Protobuf::FieldDescriptor,
|
235
189
|
).returns(Field)
|
236
190
|
end
|
237
191
|
def create_descriptor_method(klass, desc)
|
238
192
|
field = field_of(desc)
|
239
193
|
|
240
|
-
create_method(
|
241
|
-
klass,
|
194
|
+
klass.create_method(
|
242
195
|
field.name,
|
243
196
|
return_type: field.type
|
244
197
|
)
|
245
198
|
|
246
|
-
create_method(
|
247
|
-
klass,
|
199
|
+
klass.create_method(
|
248
200
|
"#{field.name}=",
|
249
|
-
parameters: [
|
250
|
-
Parlour::RbiGenerator::Parameter.new("value", type: field.type),
|
251
|
-
],
|
201
|
+
parameters: [create_param("value", type: field.type)],
|
252
202
|
return_type: field.type
|
253
203
|
)
|
254
204
|
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
|
6
4
|
begin
|
7
5
|
require "sidekiq"
|
8
6
|
rescue LoadError
|
@@ -45,37 +43,52 @@ module Tapioca
|
|
45
43
|
class SidekiqWorker < Base
|
46
44
|
extend T::Sig
|
47
45
|
|
48
|
-
sig { override.params(root:
|
46
|
+
sig { override.params(root: RBI::Tree, constant: T.class_of(::Sidekiq::Worker)).void }
|
49
47
|
def decorate(root, constant)
|
50
48
|
return unless constant.instance_methods.include?(:perform)
|
51
49
|
|
52
|
-
root.
|
50
|
+
root.create_path(constant) do |worker|
|
53
51
|
method_def = constant.instance_method(:perform)
|
54
52
|
|
55
|
-
async_params =
|
53
|
+
async_params = compile_method_parameters_to_rbi(method_def)
|
56
54
|
|
57
55
|
# `perform_at` and is just an alias for `perform_in` so both methods technically
|
58
56
|
# accept a datetime, time, or numeric but we're typing them differently so they
|
59
57
|
# semantically make sense.
|
60
58
|
at_params = [
|
61
|
-
|
59
|
+
create_param("interval", type: "T.any(DateTime, Time)"),
|
62
60
|
*async_params,
|
63
61
|
]
|
64
62
|
in_params = [
|
65
|
-
|
63
|
+
create_param("interval", type: "Numeric"),
|
66
64
|
*async_params,
|
67
65
|
]
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
generate_perform_method(constant, worker, "perform_async", async_params)
|
68
|
+
generate_perform_method(constant, worker, "perform_at", at_params)
|
69
|
+
generate_perform_method(constant, worker, "perform_in", in_params)
|
72
70
|
end
|
73
71
|
end
|
74
72
|
|
75
73
|
sig { override.returns(T::Enumerable[Module]) }
|
76
74
|
def gather_constants
|
77
|
-
|
78
|
-
|
75
|
+
all_classes.select { |c| c < Sidekiq::Worker }
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
sig do
|
81
|
+
params(
|
82
|
+
constant: T.class_of(::Sidekiq::Worker),
|
83
|
+
worker: RBI::Scope,
|
84
|
+
method_name: String,
|
85
|
+
parameters: T::Array[RBI::TypedParam]
|
86
|
+
).void
|
87
|
+
end
|
88
|
+
def generate_perform_method(constant, worker, method_name, parameters)
|
89
|
+
if constant.method(method_name.to_sym).owner == Sidekiq::Worker::ClassMethods
|
90
|
+
worker.create_method(method_name, parameters: parameters, return_type: "String", class_method: true)
|
91
|
+
end
|
79
92
|
end
|
80
93
|
end
|
81
94
|
end
|