tapioca 0.9.4 → 0.10.0
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/LICENSE.txt +21 -0
- data/README.md +48 -22
- data/lib/tapioca/cli.rb +17 -23
- data/lib/tapioca/commands/annotations.rb +2 -2
- data/lib/tapioca/commands/dsl.rb +43 -65
- data/lib/tapioca/commands/gem.rb +18 -50
- data/lib/tapioca/commands/todo.rb +1 -2
- data/lib/tapioca/dsl/compiler.rb +34 -37
- data/lib/tapioca/dsl/compilers/aasm.rb +7 -3
- data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +7 -3
- data/lib/tapioca/dsl/compilers/action_mailer.rb +7 -3
- data/lib/tapioca/dsl/compilers/active_job.rb +7 -3
- data/lib/tapioca/dsl/compilers/active_model_attributes.rb +9 -5
- data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +11 -7
- data/lib/tapioca/dsl/compilers/active_record_associations.rb +7 -3
- data/lib/tapioca/dsl/compilers/active_record_columns.rb +7 -3
- data/lib/tapioca/dsl/compilers/active_record_enum.rb +7 -3
- data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +8 -4
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +7 -3
- data/lib/tapioca/dsl/compilers/active_record_scope.rb +5 -3
- data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +8 -4
- data/lib/tapioca/dsl/compilers/active_resource.rb +7 -3
- data/lib/tapioca/dsl/compilers/active_storage.rb +9 -5
- data/lib/tapioca/dsl/compilers/active_support_concern.rb +21 -18
- data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +7 -3
- data/lib/tapioca/dsl/compilers/config.rb +12 -8
- data/lib/tapioca/dsl/compilers/frozen_record.rb +7 -3
- data/lib/tapioca/dsl/compilers/graphql_input_object.rb +71 -0
- data/lib/tapioca/dsl/compilers/graphql_mutation.rb +81 -0
- data/lib/tapioca/dsl/compilers/identity_cache.rb +8 -4
- data/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +9 -5
- data/lib/tapioca/dsl/compilers/protobuf.rb +69 -25
- data/lib/tapioca/dsl/compilers/rails_generators.rb +12 -8
- data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +7 -3
- data/lib/tapioca/dsl/compilers/smart_properties.rb +10 -6
- data/lib/tapioca/dsl/compilers/state_machines.rb +7 -3
- data/lib/tapioca/dsl/compilers/url_helpers.rb +29 -26
- data/lib/tapioca/dsl/compilers.rb +0 -5
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +1 -9
- data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +62 -0
- data/lib/tapioca/dsl/pipeline.rb +19 -11
- data/lib/tapioca/gem/listeners/source_location.rb +16 -9
- data/lib/tapioca/gemfile.rb +30 -0
- data/lib/tapioca/helpers/config_helper.rb +2 -2
- data/lib/tapioca/helpers/rbi_helper.rb +43 -30
- data/lib/tapioca/helpers/source_uri.rb +77 -0
- data/lib/tapioca/helpers/test/dsl_compiler.rb +1 -1
- data/lib/tapioca/helpers/test/isolation.rb +7 -3
- data/lib/tapioca/internal.rb +5 -1
- data/lib/tapioca/loaders/dsl.rb +84 -0
- data/lib/tapioca/loaders/gem.rb +85 -0
- data/lib/tapioca/{runtime → loaders}/loader.rb +39 -31
- data/lib/tapioca/repo_index.rb +12 -8
- data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +2 -2
- data/lib/tapioca/runtime/trackers/constant_definition.rb +15 -13
- data/lib/tapioca/runtime/trackers/mixin.rb +35 -31
- data/lib/tapioca/runtime/trackers/required_ancestor.rb +21 -19
- data/lib/tapioca/static/requires_compiler.rb +2 -3
- data/lib/tapioca/static/symbol_table_parser.rb +19 -17
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +24 -20
- metadata +10 -5
- data/Gemfile +0 -53
- data/Rakefile +0 -18
@@ -0,0 +1,81 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "graphql"
|
6
|
+
rescue LoadError
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
require "tapioca/dsl/helpers/graphql_type_helper"
|
11
|
+
|
12
|
+
module Tapioca
|
13
|
+
module Dsl
|
14
|
+
module Compilers
|
15
|
+
# `Tapioca::Dsl::Compilers::GraphqlMutation` generates RBI files for subclasses of
|
16
|
+
# [`GraphQL::Schema::Mutation`](https://graphql-ruby.org/api-doc/2.0.11/GraphQL/Schema/Mutation).
|
17
|
+
#
|
18
|
+
# For example, with the following `GraphQL::Schema::Mutation` subclass:
|
19
|
+
#
|
20
|
+
# ~~~rb
|
21
|
+
# class CreateComment < GraphQL::Schema::Mutation
|
22
|
+
# argument :body, String, required: true
|
23
|
+
# argument :post_id, ID, required: true
|
24
|
+
#
|
25
|
+
# def resolve(body:, post_id:)
|
26
|
+
# # ...
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# ~~~
|
30
|
+
#
|
31
|
+
# this compiler will produce the RBI file `notify_user_job.rbi` with the following content:
|
32
|
+
#
|
33
|
+
# ~~~rbi
|
34
|
+
# # create_comment.rbi
|
35
|
+
# # typed: true
|
36
|
+
# class CreateComment
|
37
|
+
# sig { params(body: String, post_id: String).returns(T.untyped) }
|
38
|
+
# def resolve(body:, post_id:); end
|
39
|
+
# end
|
40
|
+
# ~~~
|
41
|
+
class GraphqlMutation < Compiler
|
42
|
+
extend T::Sig
|
43
|
+
|
44
|
+
ConstantType = type_member { { fixed: T.class_of(GraphQL::Schema::InputObject) } }
|
45
|
+
|
46
|
+
sig { override.void }
|
47
|
+
def decorate
|
48
|
+
return unless constant.method_defined?(:resolve)
|
49
|
+
|
50
|
+
method_def = constant.instance_method(:resolve)
|
51
|
+
return if signature_of(method_def) # Skip if the mutation already has an inline sig
|
52
|
+
|
53
|
+
arguments = constant.all_argument_definitions
|
54
|
+
return if arguments.empty?
|
55
|
+
|
56
|
+
arguments_by_name = arguments.to_h { |a| [a.keyword.to_s, a] }
|
57
|
+
graphql_type_helper = Helpers::GraphqlTypeHelper.new
|
58
|
+
|
59
|
+
params = compile_method_parameters_to_rbi(method_def).map do |param|
|
60
|
+
name = param.param.name
|
61
|
+
argument = arguments_by_name.fetch(name, nil)
|
62
|
+
create_typed_param(param.param, argument ? graphql_type_helper.type_for(argument.type) : "T.untyped")
|
63
|
+
end
|
64
|
+
|
65
|
+
root.create_path(constant) do |mutation|
|
66
|
+
mutation.create_method("resolve", parameters: params, return_type: "T.untyped")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class << self
|
71
|
+
extend T::Sig
|
72
|
+
|
73
|
+
sig { override.returns(T::Enumerable[Module]) }
|
74
|
+
def gather_constants
|
75
|
+
all_classes.select { |c| c < GraphQL::Schema::Mutation && c != GraphQL::Schema::RelayClassicMutation }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -100,10 +100,14 @@ module Tapioca
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
103
|
+
class << self
|
104
|
+
extend T::Sig
|
105
|
+
|
106
|
+
sig { override.returns(T::Enumerable[Module]) }
|
107
|
+
def gather_constants
|
108
|
+
descendants_of(::ActiveRecord::Base).select do |klass|
|
109
|
+
klass < ::IdentityCache::WithoutPrimaryIndex
|
110
|
+
end
|
107
111
|
end
|
108
112
|
end
|
109
113
|
|
@@ -63,11 +63,15 @@ module Tapioca
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
class << self
|
67
|
+
extend T::Sig
|
68
|
+
|
69
|
+
sig { override.returns(T::Enumerable[Module]) }
|
70
|
+
def gather_constants
|
71
|
+
# Select all non-anonymous modules that have overridden Module.included
|
72
|
+
all_modules.select do |mod|
|
73
|
+
!mod.is_a?(Class) && name_of(mod) && Runtime::Reflection.method_of(mod, :included).owner != Module
|
74
|
+
end
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
@@ -90,33 +90,70 @@ module Tapioca
|
|
90
90
|
elsif constant == Google::Protobuf::Map
|
91
91
|
create_type_members(klass, "Key", "Value")
|
92
92
|
else
|
93
|
-
descriptor = T.
|
94
|
-
descriptor.each_oneof { |oneof| create_oneof_method(klass, oneof) }
|
95
|
-
fields = descriptor.map { |desc| create_descriptor_method(klass, desc) }
|
96
|
-
fields.sort_by!(&:name)
|
93
|
+
descriptor = T.unsafe(constant).descriptor
|
97
94
|
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
case descriptor
|
96
|
+
when Google::Protobuf::EnumDescriptor
|
97
|
+
descriptor.to_h.each do |sym, val|
|
98
|
+
klass.create_constant(sym.to_s, value: val.to_s)
|
99
|
+
end
|
100
|
+
|
101
|
+
klass.create_method(
|
102
|
+
"lookup",
|
103
|
+
parameters: [create_param("number", type: "Integer")],
|
104
|
+
return_type: "T.nilable(Symbol)",
|
105
|
+
class_method: true,
|
106
|
+
)
|
107
|
+
klass.create_method(
|
108
|
+
"resolve",
|
109
|
+
parameters: [create_param("symbol", type: "Symbol")],
|
110
|
+
return_type: "T.nilable(Integer)",
|
111
|
+
class_method: true,
|
112
|
+
)
|
113
|
+
klass.create_method(
|
114
|
+
"descriptor",
|
115
|
+
return_type: "Google::Protobuf::EnumDescriptor",
|
116
|
+
class_method: true,
|
117
|
+
)
|
118
|
+
when Google::Protobuf::Descriptor
|
119
|
+
descriptor.each_oneof { |oneof| create_oneof_method(klass, oneof) }
|
120
|
+
fields = descriptor.map { |desc| create_descriptor_method(klass, desc) }
|
121
|
+
fields.sort_by!(&:name)
|
122
|
+
|
123
|
+
parameters = fields.map do |field|
|
124
|
+
create_kw_opt_param(field.name, type: field.init_type, default: field.default)
|
125
|
+
end
|
101
126
|
|
102
|
-
|
103
|
-
|
127
|
+
if fields.all? { |field| FIELD_RE.match?(field.name) }
|
128
|
+
klass.create_method("initialize", parameters: parameters, return_type: "void")
|
129
|
+
else
|
130
|
+
# One of the fields has an incorrect name for a named parameter so creating the default initialize for
|
131
|
+
# it would create a RBI with a syntax error.
|
132
|
+
# The workaround is to create an initialize that takes a **kwargs instead.
|
133
|
+
kwargs_parameter = create_kw_rest_param("fields", type: "T.untyped")
|
134
|
+
klass.create_method("initialize", parameters: [kwargs_parameter], return_type: "void")
|
135
|
+
end
|
104
136
|
else
|
105
|
-
|
106
|
-
# it would create a RBI with a syntax error.
|
107
|
-
# The workaround is to create an initialize that takes a **kwargs instead.
|
108
|
-
kwargs_parameter = create_kw_rest_param("fields", type: "T.untyped")
|
109
|
-
klass.create_method("initialize", parameters: [kwargs_parameter], return_type: "void")
|
137
|
+
raise TypeError, "Unexpected descriptor class: #{descriptor.class.name}"
|
110
138
|
end
|
111
139
|
end
|
112
140
|
end
|
113
141
|
end
|
114
142
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
143
|
+
class << self
|
144
|
+
extend T::Sig
|
145
|
+
|
146
|
+
sig { override.returns(T::Enumerable[Module]) }
|
147
|
+
def gather_constants
|
148
|
+
marker = Google::Protobuf::MessageExts::ClassMethods
|
149
|
+
|
150
|
+
enum_modules = ObjectSpace.each_object(Google::Protobuf::EnumDescriptor).map do |desc|
|
151
|
+
T.cast(desc, Google::Protobuf::EnumDescriptor).enummodule
|
152
|
+
end
|
153
|
+
|
154
|
+
results = T.cast(ObjectSpace.each_object(marker).to_a, T::Array[Module]).concat(enum_modules)
|
155
|
+
results.any? ? results + [Google::Protobuf::RepeatedField, Google::Protobuf::Map] : []
|
156
|
+
end
|
120
157
|
end
|
121
158
|
|
122
159
|
private
|
@@ -138,7 +175,13 @@ module Tapioca
|
|
138
175
|
def type_of(descriptor)
|
139
176
|
case descriptor.type
|
140
177
|
when :enum
|
141
|
-
|
178
|
+
# According to https://developers.google.com/protocol-buffers/docs/reference/ruby-generated#enum
|
179
|
+
# > You may assign either a number or a symbol to an enum field.
|
180
|
+
# > When reading the value back, it will be a symbol if the enum
|
181
|
+
# > value is known, or a number if it is unknown. Since proto3 uses
|
182
|
+
# > open enum semantics, any number may be assigned to an enum
|
183
|
+
# > field, even if it was not defined in the enum.
|
184
|
+
"T.any(Symbol, Integer)"
|
142
185
|
when :message
|
143
186
|
descriptor.subtype.msgclass.name
|
144
187
|
when :int32, :int64, :uint32, :uint64
|
@@ -179,7 +222,7 @@ module Tapioca
|
|
179
222
|
Field.new(
|
180
223
|
name: descriptor.name,
|
181
224
|
type: type,
|
182
|
-
init_type: "T.any(#{type}, T::Hash[#{key_type}, #{value_type}])",
|
225
|
+
init_type: "T.nilable(T.any(#{type}, T::Hash[#{key_type}, #{value_type}]))",
|
183
226
|
default: "Google::Protobuf::Map.new(#{default_args.join(", ")})"
|
184
227
|
)
|
185
228
|
else
|
@@ -192,18 +235,19 @@ module Tapioca
|
|
192
235
|
Field.new(
|
193
236
|
name: descriptor.name,
|
194
237
|
type: type,
|
195
|
-
init_type: "T.any(#{type}, T::Array[#{elem_type}])",
|
238
|
+
init_type: "T.nilable(T.any(#{type}, T::Array[#{elem_type}]))",
|
196
239
|
default: "Google::Protobuf::RepeatedField.new(#{default_args.join(", ")})"
|
197
240
|
)
|
198
241
|
end
|
199
242
|
else
|
200
243
|
type = type_of(descriptor)
|
201
|
-
|
244
|
+
nilable_type = as_nilable_type(type)
|
245
|
+
type = nilable_type if nilable_descriptor?(descriptor)
|
202
246
|
|
203
247
|
Field.new(
|
204
248
|
name: descriptor.name,
|
205
249
|
type: type,
|
206
|
-
init_type:
|
250
|
+
init_type: nilable_type,
|
207
251
|
default: "nil"
|
208
252
|
)
|
209
253
|
end
|
@@ -226,7 +270,7 @@ module Tapioca
|
|
226
270
|
klass.create_method(
|
227
271
|
"#{field.name}=",
|
228
272
|
parameters: [create_param("value", type: field.type)],
|
229
|
-
return_type:
|
273
|
+
return_type: "void"
|
230
274
|
)
|
231
275
|
|
232
276
|
field
|
@@ -64,14 +64,18 @@ module Tapioca
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
67
|
+
class << self
|
68
|
+
extend T::Sig
|
69
|
+
|
70
|
+
sig { override.returns(T::Enumerable[Module]) }
|
71
|
+
def gather_constants
|
72
|
+
all_classes.select do |const|
|
73
|
+
name = qualified_name_of(const)
|
74
|
+
|
75
|
+
name &&
|
76
|
+
!name.match?(BUILT_IN_MATCHER) &&
|
77
|
+
::Rails::Generators::Base > const
|
78
|
+
end
|
75
79
|
end
|
76
80
|
end
|
77
81
|
|
@@ -72,9 +72,13 @@ module Tapioca
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
class << self
|
76
|
+
extend T::Sig
|
77
|
+
|
78
|
+
sig { override.returns(T::Enumerable[Module]) }
|
79
|
+
def gather_constants
|
80
|
+
all_classes.select { |c| c < Sidekiq::Worker }
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
private
|
@@ -85,12 +85,16 @@ module Tapioca
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
88
|
+
class << self
|
89
|
+
extend T::Sig
|
90
|
+
|
91
|
+
sig { override.returns(T::Enumerable[Module]) }
|
92
|
+
def gather_constants
|
93
|
+
all_modules.select do |c|
|
94
|
+
name_of(c) &&
|
95
|
+
c != ::SmartProperties::Validations::Ancestor &&
|
96
|
+
c < ::SmartProperties && ::SmartProperties::ClassMethods === c
|
97
|
+
end
|
94
98
|
end
|
95
99
|
end
|
96
100
|
|
@@ -160,9 +160,13 @@ module Tapioca
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
class << self
|
164
|
+
extend T::Sig
|
165
|
+
|
166
|
+
sig { override.returns(T::Enumerable[Module]) }
|
167
|
+
def gather_constants
|
168
|
+
all_classes.select { |mod| mod < ::StateMachines::InstanceMethods }
|
169
|
+
end
|
166
170
|
end
|
167
171
|
|
168
172
|
private
|
@@ -107,23 +107,39 @@ module Tapioca
|
|
107
107
|
ActionView::Helpers,
|
108
108
|
], T::Array[Module])
|
109
109
|
|
110
|
-
|
111
|
-
|
112
|
-
|
110
|
+
class << self
|
111
|
+
extend T::Sig
|
112
|
+
sig { override.returns(T::Enumerable[Module]) }
|
113
|
+
def gather_constants
|
114
|
+
return [] unless Rails.application
|
115
|
+
|
116
|
+
Object.const_set(:GeneratedUrlHelpersModule, Rails.application.routes.named_routes.url_helpers_module)
|
117
|
+
Object.const_set(:GeneratedPathHelpersModule, Rails.application.routes.named_routes.path_helpers_module)
|
118
|
+
|
119
|
+
constants = all_modules.select do |mod|
|
120
|
+
next unless name_of(mod)
|
121
|
+
|
122
|
+
includes_helper?(mod, GeneratedUrlHelpersModule) ||
|
123
|
+
includes_helper?(mod, GeneratedPathHelpersModule) ||
|
124
|
+
includes_helper?(mod.singleton_class, GeneratedUrlHelpersModule) ||
|
125
|
+
includes_helper?(mod.singleton_class, GeneratedPathHelpersModule)
|
126
|
+
end
|
113
127
|
|
114
|
-
|
115
|
-
|
128
|
+
constants.concat(NON_DISCOVERABLE_INCLUDERS)
|
129
|
+
end
|
116
130
|
|
117
|
-
|
118
|
-
|
131
|
+
sig { params(mod: Module, helper: Module).returns(T::Boolean) }
|
132
|
+
private def includes_helper?(mod, helper)
|
133
|
+
superclass_ancestors = []
|
119
134
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
135
|
+
if Class === mod
|
136
|
+
superclass = superclass_of(mod)
|
137
|
+
superclass_ancestors = ancestors_of(superclass) if superclass
|
138
|
+
end
|
125
139
|
|
126
|
-
|
140
|
+
ancestors = Set.new.compare_by_identity.merge(ancestors_of(mod)).subtract(superclass_ancestors)
|
141
|
+
ancestors.any? { |ancestor| helper == ancestor }
|
142
|
+
end
|
127
143
|
end
|
128
144
|
|
129
145
|
private
|
@@ -152,19 +168,6 @@ module Tapioca
|
|
152
168
|
mod.create_include(T.must(helper_module.name)) if include_helper
|
153
169
|
mod.create_extend(T.must(helper_module.name)) if extend_helper
|
154
170
|
end
|
155
|
-
|
156
|
-
sig { params(mod: Module, helper: Module).returns(T::Boolean) }
|
157
|
-
private_class_method def self.includes_helper?(mod, helper)
|
158
|
-
superclass_ancestors = []
|
159
|
-
|
160
|
-
if Class === mod
|
161
|
-
superclass = superclass_of(mod)
|
162
|
-
superclass_ancestors = ancestors_of(superclass) if superclass
|
163
|
-
end
|
164
|
-
|
165
|
-
ancestors = Set.new.compare_by_identity.merge(ancestors_of(mod)).subtract(superclass_ancestors)
|
166
|
-
ancestors.any? { |ancestor| helper == ancestor }
|
167
|
-
end
|
168
171
|
end
|
169
172
|
end
|
170
173
|
end
|
@@ -4,11 +4,6 @@
|
|
4
4
|
module Tapioca
|
5
5
|
module Dsl
|
6
6
|
module Compilers
|
7
|
-
DIRECTORY = T.let(
|
8
|
-
File.expand_path("compilers", __dir__),
|
9
|
-
String
|
10
|
-
)
|
11
|
-
|
12
7
|
# DSL compilers are either built-in to Tapioca and live under the
|
13
8
|
# `Tapioca::Dsl::Compilers` namespace (i.e. this namespace), and
|
14
9
|
# can be referred to by just using the class name, or they live in
|
@@ -6,6 +6,7 @@ module Tapioca
|
|
6
6
|
module Helpers
|
7
7
|
class ActiveRecordColumnTypeHelper
|
8
8
|
extend T::Sig
|
9
|
+
include RBIHelper
|
9
10
|
|
10
11
|
sig { params(constant: T.class_of(ActiveRecord::Base)).void }
|
11
12
|
def initialize(constant)
|
@@ -74,15 +75,6 @@ module Tapioca
|
|
74
75
|
!(constant.singleton_class < Object.const_get(:StrongTypeGeneration))
|
75
76
|
end
|
76
77
|
|
77
|
-
sig { params(type: String).returns(String) }
|
78
|
-
def as_nilable_type(type)
|
79
|
-
if type.start_with?("T.nilable(") || type == "T.untyped"
|
80
|
-
type
|
81
|
-
else
|
82
|
-
"T.nilable(#{type})"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
78
|
sig { params(column_type: BasicObject).returns(String) }
|
87
79
|
def handle_unknown_type(column_type)
|
88
80
|
return "T.untyped" unless ActiveModel::Type::Value === column_type
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
module Dsl
|
6
|
+
module Helpers
|
7
|
+
class GraphqlTypeHelper
|
8
|
+
extend T::Sig
|
9
|
+
include RBIHelper
|
10
|
+
include Runtime::Reflection
|
11
|
+
|
12
|
+
sig { params(type: GraphQL::Schema::Wrapper).returns(String) }
|
13
|
+
def type_for(type)
|
14
|
+
unwrapped_type = type.unwrap
|
15
|
+
|
16
|
+
parsed_type = case unwrapped_type
|
17
|
+
when GraphQL::Types::Boolean.singleton_class
|
18
|
+
"T::Boolean"
|
19
|
+
when GraphQL::Types::Float.singleton_class
|
20
|
+
qualified_name_of(Float)
|
21
|
+
when GraphQL::Types::ID.singleton_class
|
22
|
+
qualified_name_of(String)
|
23
|
+
when GraphQL::Types::Int.singleton_class
|
24
|
+
qualified_name_of(Integer)
|
25
|
+
when GraphQL::Types::ISO8601Date.singleton_class
|
26
|
+
qualified_name_of(Date)
|
27
|
+
when GraphQL::Types::ISO8601DateTime.singleton_class
|
28
|
+
qualified_name_of(DateTime)
|
29
|
+
when GraphQL::Types::JSON.singleton_class
|
30
|
+
"T::Hash[::String, T.untyped]"
|
31
|
+
when GraphQL::Types::String.singleton_class
|
32
|
+
qualified_name_of(String)
|
33
|
+
when GraphQL::Schema::Enum.singleton_class
|
34
|
+
enum_values = T.cast(unwrapped_type.enum_values, T::Array[GraphQL::Schema::EnumValue])
|
35
|
+
value_types = enum_values.map { |v| qualified_name_of(v.value.class) }.uniq
|
36
|
+
if value_types.size == 1
|
37
|
+
value_types.first
|
38
|
+
else
|
39
|
+
"T.any(#{value_types.join(", ")})"
|
40
|
+
end
|
41
|
+
when GraphQL::Schema::InputObject.singleton_class
|
42
|
+
qualified_name_of(unwrapped_type)
|
43
|
+
else
|
44
|
+
"T.untyped"
|
45
|
+
end
|
46
|
+
|
47
|
+
parsed_type = T.must(parsed_type)
|
48
|
+
|
49
|
+
if type.list?
|
50
|
+
parsed_type = "T::Array[#{parsed_type}]"
|
51
|
+
end
|
52
|
+
|
53
|
+
unless type.non_null?
|
54
|
+
parsed_type = as_nilable_type(parsed_type)
|
55
|
+
end
|
56
|
+
|
57
|
+
parsed_type
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/tapioca/dsl/pipeline.rb
CHANGED
@@ -7,7 +7,7 @@ module Tapioca
|
|
7
7
|
extend T::Sig
|
8
8
|
|
9
9
|
sig { returns(T::Enumerable[T.class_of(Compiler)]) }
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :active_compilers
|
11
11
|
|
12
12
|
sig { returns(T::Array[Module]) }
|
13
13
|
attr_reader :requested_constants
|
@@ -34,8 +34,8 @@ module Tapioca
|
|
34
34
|
error_handler: $stderr.method(:puts).to_proc,
|
35
35
|
number_of_workers: nil
|
36
36
|
)
|
37
|
-
@
|
38
|
-
|
37
|
+
@active_compilers = T.let(
|
38
|
+
gather_active_compilers(requested_compilers, excluded_compilers),
|
39
39
|
T::Enumerable[T.class_of(Compiler)]
|
40
40
|
)
|
41
41
|
@requested_constants = requested_constants
|
@@ -87,11 +87,19 @@ module Tapioca
|
|
87
87
|
def compiler_enabled?(compiler_name)
|
88
88
|
potential_names = Compilers::NAMESPACES.map { |namespace| namespace + compiler_name }
|
89
89
|
|
90
|
-
|
90
|
+
active_compilers.any? do |compiler|
|
91
91
|
potential_names.any?(compiler.name)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
sig { returns(T::Array[T.class_of(Compiler)]) }
|
96
|
+
def compilers
|
97
|
+
@compilers = T.let(@compilers, T.nilable(T::Array[T.class_of(Compiler)]))
|
98
|
+
@compilers ||= Runtime::Reflection.descendants_of(Compiler).sort_by do |compiler|
|
99
|
+
T.must(compiler.name)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
95
103
|
private
|
96
104
|
|
97
105
|
sig do
|
@@ -100,16 +108,16 @@ module Tapioca
|
|
100
108
|
excluded_compilers: T::Array[T.class_of(Compiler)]
|
101
109
|
).returns(T::Enumerable[T.class_of(Compiler)])
|
102
110
|
end
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
111
|
+
def gather_active_compilers(requested_compilers, excluded_compilers)
|
112
|
+
active_compilers = compilers
|
113
|
+
active_compilers -= excluded_compilers
|
114
|
+
active_compilers &= requested_compilers unless requested_compilers.empty?
|
115
|
+
active_compilers
|
108
116
|
end
|
109
117
|
|
110
118
|
sig { params(requested_constants: T::Array[Module]).returns(T::Set[Module]) }
|
111
119
|
def gather_constants(requested_constants)
|
112
|
-
constants =
|
120
|
+
constants = active_compilers.map(&:processable_constants).reduce(Set.new, :union)
|
113
121
|
constants = filter_anonymous_and_reloaded_constants(constants)
|
114
122
|
|
115
123
|
constants &= requested_constants unless requested_constants.empty?
|
@@ -145,7 +153,7 @@ module Tapioca
|
|
145
153
|
def rbi_for_constant(constant)
|
146
154
|
file = RBI::File.new(strictness: "true")
|
147
155
|
|
148
|
-
|
156
|
+
active_compilers.each do |compiler_class|
|
149
157
|
next unless compiler_class.handles?(constant)
|
150
158
|
|
151
159
|
compiler = compiler_class.new(self, file.root, constant)
|
@@ -41,7 +41,6 @@ module Tapioca
|
|
41
41
|
def add_source_location_comment(node, file, line)
|
42
42
|
return unless file && line
|
43
43
|
|
44
|
-
gem = @pipeline.gem
|
45
44
|
path = Pathname.new(file)
|
46
45
|
return unless File.exist?(path)
|
47
46
|
|
@@ -49,17 +48,25 @@ module Tapioca
|
|
49
48
|
# use for jump to definition. Only add source comments on Ruby files
|
50
49
|
return unless path.extname == ".rb"
|
51
50
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
path.sub("#{Bundler.bundle_path}/gems/", "").to_s
|
56
|
-
end
|
51
|
+
realpath = path.realpath.to_s
|
52
|
+
gem = Gemfile::GemSpec.spec_lookup_by_file_path[realpath]
|
53
|
+
return if gem.nil?
|
57
54
|
|
58
|
-
|
59
|
-
|
55
|
+
path = gem.relative_path_for(path)
|
56
|
+
version = gem.version
|
57
|
+
# we can clear the gem version if the gem is the same one we are processing
|
58
|
+
version = "" if gem == @pipeline.gem
|
60
59
|
|
60
|
+
uri = URI::Source.build(
|
61
|
+
gem_name: gem.name,
|
62
|
+
gem_version: version,
|
63
|
+
path: path.to_s,
|
64
|
+
line_number: line.to_s
|
65
|
+
)
|
61
66
|
node.comments << RBI::Comment.new("") if node.comments.any?
|
62
|
-
node.comments << RBI::Comment.new(
|
67
|
+
node.comments << RBI::Comment.new(uri.to_s)
|
68
|
+
rescue URI::InvalidComponentError, URI::InvalidURIError
|
69
|
+
# Bad uri
|
63
70
|
end
|
64
71
|
end
|
65
72
|
end
|