tapioca 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|