tapioca 0.11.8 → 0.11.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +116 -49
- data/lib/tapioca/cli.rb +76 -67
- data/lib/tapioca/commands/{dsl.rb → abstract_dsl.rb} +32 -78
- data/lib/tapioca/commands/{gem.rb → abstract_gem.rb} +26 -93
- data/lib/tapioca/commands/annotations.rb +9 -7
- data/lib/tapioca/commands/check_shims.rb +2 -0
- data/lib/tapioca/commands/command.rb +9 -2
- data/lib/tapioca/commands/configure.rb +2 -2
- data/lib/tapioca/commands/dsl_compiler_list.rb +31 -0
- data/lib/tapioca/commands/dsl_generate.rb +40 -0
- data/lib/tapioca/commands/dsl_verify.rb +25 -0
- data/lib/tapioca/commands/gem_generate.rb +51 -0
- data/lib/tapioca/commands/gem_sync.rb +37 -0
- data/lib/tapioca/commands/gem_verify.rb +36 -0
- data/lib/tapioca/commands/require.rb +2 -0
- data/lib/tapioca/commands/todo.rb +21 -2
- data/lib/tapioca/commands.rb +8 -2
- data/lib/tapioca/dsl/compiler.rb +8 -4
- data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +3 -1
- data/lib/tapioca/dsl/compilers/active_model_validations_confirmation.rb +94 -0
- data/lib/tapioca/dsl/compilers/active_record_columns.rb +19 -9
- data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +3 -2
- data/lib/tapioca/dsl/compilers/active_support_concern.rb +1 -1
- data/lib/tapioca/dsl/compilers/graphql_input_object.rb +1 -1
- data/lib/tapioca/dsl/compilers/graphql_mutation.rb +9 -2
- data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +208 -0
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +20 -4
- data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +20 -3
- data/lib/tapioca/dsl/pipeline.rb +6 -4
- data/lib/tapioca/gem/pipeline.rb +103 -36
- data/lib/tapioca/gemfile.rb +13 -7
- data/lib/tapioca/helpers/git_attributes.rb +34 -0
- data/lib/tapioca/helpers/test/template.rb +4 -4
- data/lib/tapioca/internal.rb +1 -0
- data/lib/tapioca/loaders/dsl.rb +11 -1
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +0 -27
- data/lib/tapioca/static/symbol_loader.rb +9 -8
- data/lib/tapioca/version.rb +1 -1
- metadata +19 -10
@@ -6,6 +6,17 @@ module Tapioca
|
|
6
6
|
class Todo < CommandWithoutTracker
|
7
7
|
include SorbetHelper
|
8
8
|
|
9
|
+
DEPRECATION_MESSAGE = T.let(<<~DEPRECATION, String)
|
10
|
+
The `todo` command is deprecated and will be removed in a future release.
|
11
|
+
|
12
|
+
If your project is still missing type definitions for constants, try the following:
|
13
|
+
1. Regenerate gem RBIs by running `bin/tapioca gem --all` and `bin/tapioca annotations`
|
14
|
+
2. Generate RBIs for DSLs by running `bin/tapioca dsl`
|
15
|
+
3. If the missing constants are defined in files that a gem does not load by default,
|
16
|
+
manually require those files in `sorbet/tapioca/require.rb` and regenerate gem RBIs
|
17
|
+
4. Manually create an RBI shim defining the missing constants
|
18
|
+
DEPRECATION
|
19
|
+
|
9
20
|
sig do
|
10
21
|
params(
|
11
22
|
todo_file: String,
|
@@ -19,6 +30,16 @@ module Tapioca
|
|
19
30
|
super()
|
20
31
|
end
|
21
32
|
|
33
|
+
sig { void }
|
34
|
+
def run_with_deprecation
|
35
|
+
say(DEPRECATION_MESSAGE, :red)
|
36
|
+
say("")
|
37
|
+
|
38
|
+
run
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
22
43
|
sig { override.void }
|
23
44
|
def execute
|
24
45
|
say("Finding all unresolved constants, this may take a few seconds... ")
|
@@ -43,8 +64,6 @@ module Tapioca
|
|
43
64
|
say("Please review changes and commit them.", [:green, :bold])
|
44
65
|
end
|
45
66
|
|
46
|
-
private
|
47
|
-
|
48
67
|
sig { params(constants: T::Array[String], command: String).returns(RBI::File) }
|
49
68
|
def rbi(constants, command:)
|
50
69
|
file = RBI::File.new
|
data/lib/tapioca/commands.rb
CHANGED
@@ -7,9 +7,15 @@ module Tapioca
|
|
7
7
|
autoload :CommandWithoutTracker, "tapioca/commands/command_without_tracker"
|
8
8
|
autoload :Annotations, "tapioca/commands/annotations"
|
9
9
|
autoload :CheckShims, "tapioca/commands/check_shims"
|
10
|
-
autoload :
|
10
|
+
autoload :AbstractDsl, "tapioca/commands/abstract_dsl"
|
11
|
+
autoload :DslCompilerList, "tapioca/commands/dsl_compiler_list"
|
12
|
+
autoload :DslGenerate, "tapioca/commands/dsl_generate"
|
13
|
+
autoload :DslVerify, "tapioca/commands/dsl_verify"
|
11
14
|
autoload :Configure, "tapioca/commands/configure"
|
12
|
-
autoload :
|
15
|
+
autoload :AbstractGem, "tapioca/commands/abstract_gem"
|
16
|
+
autoload :GemGenerate, "tapioca/commands/gem_generate"
|
17
|
+
autoload :GemSync, "tapioca/commands/gem_sync"
|
18
|
+
autoload :GemVerify, "tapioca/commands/gem_verify"
|
13
19
|
autoload :Require, "tapioca/commands/require"
|
14
20
|
autoload :Todo, "tapioca/commands/todo"
|
15
21
|
end
|
data/lib/tapioca/dsl/compiler.rb
CHANGED
@@ -45,14 +45,18 @@ module Tapioca
|
|
45
45
|
|
46
46
|
sig { returns(T::Enumerable[T::Class[T.anything]]) }
|
47
47
|
def all_classes
|
48
|
-
@all_classes
|
49
|
-
|
48
|
+
@all_classes ||= T.let(
|
49
|
+
T.cast(ObjectSpace.each_object(Class), T::Enumerable[T::Class[T.anything]]).each,
|
50
|
+
T.nilable(T::Enumerable[T::Class[T.anything]]),
|
51
|
+
)
|
50
52
|
end
|
51
53
|
|
52
54
|
sig { returns(T::Enumerable[Module]) }
|
53
55
|
def all_modules
|
54
|
-
@all_modules
|
55
|
-
|
56
|
+
@all_modules ||= T.let(
|
57
|
+
T.cast(ObjectSpace.each_object(Module), T::Enumerable[Module]).each,
|
58
|
+
T.nilable(T::Enumerable[Module]),
|
59
|
+
)
|
56
60
|
end
|
57
61
|
end
|
58
62
|
|
@@ -126,7 +126,9 @@ module Tapioca
|
|
126
126
|
|
127
127
|
sig { override.returns(T::Enumerable[Module]) }
|
128
128
|
def gather_constants
|
129
|
-
descendants_of(::ActionController::Base).
|
129
|
+
descendants_of(::ActionController::Base).select(&:name).select do |klass|
|
130
|
+
klass.const_defined?(:HelperMethods, false)
|
131
|
+
end
|
130
132
|
end
|
131
133
|
end
|
132
134
|
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "active_model"
|
6
|
+
rescue LoadError
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
module Tapioca
|
11
|
+
module Dsl
|
12
|
+
module Compilers
|
13
|
+
# `Tapioca::Dsl::Compilers::ActiveModelValidationsConfirmation` decorates RBI files for all
|
14
|
+
# classes that use [`ActiveModel::Validates::Confirmation`](https://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_confirmation_of).
|
15
|
+
#
|
16
|
+
# For example, with the following class:
|
17
|
+
#
|
18
|
+
# ~~~rb
|
19
|
+
# class User
|
20
|
+
# include ActiveModel::Validations
|
21
|
+
#
|
22
|
+
# validates_confirmation_of :password
|
23
|
+
#
|
24
|
+
# validates :email, confirmation: true
|
25
|
+
# end
|
26
|
+
# ~~~
|
27
|
+
#
|
28
|
+
# this compiler will produce an RBI file with the following content:
|
29
|
+
# ~~~rbi
|
30
|
+
# # typed: true
|
31
|
+
#
|
32
|
+
# class User
|
33
|
+
#
|
34
|
+
# sig { returns(T.untyped) }
|
35
|
+
# def email_confirmation; end
|
36
|
+
#
|
37
|
+
# sig { params(email_confirmation=: T.untyped).returns(T.untyped) }
|
38
|
+
# def email_confirmation=(email_confirmation); end
|
39
|
+
#
|
40
|
+
# sig { returns(T.untyped) }
|
41
|
+
# def password_confirmation; end
|
42
|
+
#
|
43
|
+
# sig { params(password_confirmation=: T.untyped).returns(T.untyped) }
|
44
|
+
# def password_confirmation=(password_confirmation); end
|
45
|
+
# end
|
46
|
+
# ~~~
|
47
|
+
class ActiveModelValidationsConfirmation < Compiler
|
48
|
+
extend T::Sig
|
49
|
+
|
50
|
+
ConstantType = type_member do
|
51
|
+
{
|
52
|
+
fixed: T.all(
|
53
|
+
T::Class[ActiveModel::Validations],
|
54
|
+
ActiveModel::Validations::HelperMethods,
|
55
|
+
ActiveModel::Validations::ClassMethods,
|
56
|
+
),
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
sig { override.returns(T::Enumerable[Module]) }
|
62
|
+
def gather_constants
|
63
|
+
# Collect all the classes that include ActiveModel::Validations
|
64
|
+
all_classes.select do |c|
|
65
|
+
c < ActiveModel::Validations
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
sig { override.void }
|
71
|
+
def decorate
|
72
|
+
confirmation_validators = constant.validators.grep(ActiveModel::Validations::ConfirmationValidator)
|
73
|
+
|
74
|
+
return if confirmation_validators.empty?
|
75
|
+
|
76
|
+
# Create a RBI definition for each class that includes Active::Model::Validations
|
77
|
+
root.create_path(constant) do |klass|
|
78
|
+
# Create RBI definitions for all the attributes that use confirmation validation
|
79
|
+
confirmation_validators.each do |validator|
|
80
|
+
validator.attributes.each do |attr_name|
|
81
|
+
klass.create_method("#{attr_name}_confirmation", return_type: "T.untyped")
|
82
|
+
klass.create_method(
|
83
|
+
"#{attr_name}_confirmation=",
|
84
|
+
parameters: [create_param("#{attr_name}_confirmation", type: "T.untyped")],
|
85
|
+
return_type: "T.untyped",
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -110,8 +110,8 @@ module Tapioca
|
|
110
110
|
|
111
111
|
root.create_path(constant) do |model|
|
112
112
|
model.create_module(AttributeMethodsModuleName) do |mod|
|
113
|
-
constant.attribute_names.each do |
|
114
|
-
add_methods_for_attribute(mod,
|
113
|
+
(constant.attribute_names + ["id"]).uniq.each do |attribute_name|
|
114
|
+
add_methods_for_attribute(mod, attribute_name)
|
115
115
|
end
|
116
116
|
|
117
117
|
constant.attribute_aliases.each do |attribute_name, column_name|
|
@@ -127,7 +127,7 @@ module Tapioca
|
|
127
127
|
old_method_names = patterns.map { |m| m.method_name(column_name) }
|
128
128
|
methods_to_add = new_method_names - old_method_names
|
129
129
|
|
130
|
-
add_methods_for_attribute(mod,
|
130
|
+
add_methods_for_attribute(mod, attribute_name, column_name, methods_to_add)
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
@@ -152,13 +152,13 @@ module Tapioca
|
|
152
152
|
name: String,
|
153
153
|
methods_to_add: T.nilable(T::Array[String]),
|
154
154
|
return_type: String,
|
155
|
-
parameters: T::Array[
|
155
|
+
parameters: T::Array[RBI::TypedParam],
|
156
156
|
).void
|
157
157
|
end
|
158
158
|
def add_method(klass, name, methods_to_add, return_type: "void", parameters: [])
|
159
159
|
klass.create_method(
|
160
160
|
name,
|
161
|
-
parameters: parameters
|
161
|
+
parameters: parameters,
|
162
162
|
return_type: return_type,
|
163
163
|
) if methods_to_add.nil? || methods_to_add.include?(name)
|
164
164
|
end
|
@@ -166,13 +166,15 @@ module Tapioca
|
|
166
166
|
sig do
|
167
167
|
params(
|
168
168
|
klass: RBI::Scope,
|
169
|
-
column_name: String,
|
170
169
|
attribute_name: String,
|
170
|
+
column_name: String,
|
171
171
|
methods_to_add: T.nilable(T::Array[String]),
|
172
172
|
).void
|
173
173
|
end
|
174
|
-
def add_methods_for_attribute(klass,
|
175
|
-
getter_type, setter_type = Helpers::ActiveRecordColumnTypeHelper
|
174
|
+
def add_methods_for_attribute(klass, attribute_name, column_name = attribute_name, methods_to_add = nil)
|
175
|
+
getter_type, setter_type = Helpers::ActiveRecordColumnTypeHelper
|
176
|
+
.new(constant)
|
177
|
+
.type_for(attribute_name, column_name)
|
176
178
|
|
177
179
|
# Added by ActiveRecord::AttributeMethods::Read
|
178
180
|
#
|
@@ -189,7 +191,7 @@ module Tapioca
|
|
189
191
|
klass,
|
190
192
|
"#{attribute_name}=",
|
191
193
|
methods_to_add,
|
192
|
-
parameters: [
|
194
|
+
parameters: [create_param("value", type: setter_type)],
|
193
195
|
return_type: setter_type,
|
194
196
|
)
|
195
197
|
|
@@ -254,6 +256,10 @@ module Tapioca
|
|
254
256
|
"#{attribute_name}_changed?",
|
255
257
|
methods_to_add,
|
256
258
|
return_type: "T::Boolean",
|
259
|
+
parameters: [
|
260
|
+
create_kw_opt_param("from", type: setter_type, default: "T.unsafe(nil)"),
|
261
|
+
create_kw_opt_param("to", type: setter_type, default: "T.unsafe(nil)"),
|
262
|
+
],
|
257
263
|
)
|
258
264
|
add_method(
|
259
265
|
klass,
|
@@ -277,6 +283,10 @@ module Tapioca
|
|
277
283
|
"#{attribute_name}_previously_changed?",
|
278
284
|
methods_to_add,
|
279
285
|
return_type: "T::Boolean",
|
286
|
+
parameters: [
|
287
|
+
create_kw_opt_param("from", type: setter_type, default: "T.unsafe(nil)"),
|
288
|
+
create_kw_opt_param("to", type: setter_type, default: "T.unsafe(nil)"),
|
289
|
+
],
|
280
290
|
)
|
281
291
|
add_method(
|
282
292
|
klass,
|
@@ -98,6 +98,9 @@ module Tapioca
|
|
98
98
|
return if stores.values.all? { |store| store.accessors.empty? }
|
99
99
|
|
100
100
|
root.create_path(constant) do |model|
|
101
|
+
store_accessors_module = model.create_module("StoreAccessors")
|
102
|
+
model.create_include("StoreAccessors")
|
103
|
+
|
101
104
|
stores.values.each do |store_data|
|
102
105
|
store_data.accessors.each do |accessor, name|
|
103
106
|
field = store_data.fields.fetch(accessor)
|
@@ -105,9 +108,7 @@ module Tapioca
|
|
105
108
|
type = as_nilable_type(type) if field.null
|
106
109
|
name ||= field.name # support < 1.5.0
|
107
110
|
|
108
|
-
store_accessors_module = model.create_module("StoreAccessors")
|
109
111
|
generate_methods(store_accessors_module, name.to_s, type)
|
110
|
-
model.create_include("StoreAccessors")
|
111
112
|
end
|
112
113
|
end
|
113
114
|
end
|
@@ -55,7 +55,7 @@ module Tapioca
|
|
55
55
|
root.create_path(constant) do |input_object|
|
56
56
|
arguments.each do |argument|
|
57
57
|
name = argument.keyword.to_s
|
58
|
-
input_object.create_method(name, return_type: Helpers::GraphqlTypeHelper.type_for(argument
|
58
|
+
input_object.create_method(name, return_type: Helpers::GraphqlTypeHelper.type_for(argument))
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -42,7 +42,7 @@ module Tapioca
|
|
42
42
|
class GraphqlMutation < Compiler
|
43
43
|
extend T::Sig
|
44
44
|
|
45
|
-
ConstantType = type_member { { fixed: T.class_of(GraphQL::Schema::
|
45
|
+
ConstantType = type_member { { fixed: T.class_of(GraphQL::Schema::Mutation) } }
|
46
46
|
|
47
47
|
sig { override.void }
|
48
48
|
def decorate
|
@@ -59,7 +59,7 @@ module Tapioca
|
|
59
59
|
params = compile_method_parameters_to_rbi(method_def).map do |param|
|
60
60
|
name = param.param.name
|
61
61
|
argument = arguments_by_name.fetch(name, nil)
|
62
|
-
create_typed_param(param.param,
|
62
|
+
create_typed_param(param.param, argument_type(argument))
|
63
63
|
end
|
64
64
|
|
65
65
|
root.create_path(constant) do |mutation|
|
@@ -67,6 +67,13 @@ module Tapioca
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
+
sig { params(argument: T.nilable(GraphQL::Schema::Argument)).returns(String) }
|
71
|
+
def argument_type(argument)
|
72
|
+
return "T.untyped" unless argument
|
73
|
+
|
74
|
+
Helpers::GraphqlTypeHelper.type_for(argument)
|
75
|
+
end
|
76
|
+
|
70
77
|
class << self
|
71
78
|
extend T::Sig
|
72
79
|
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "json_api_client"
|
6
|
+
rescue LoadError
|
7
|
+
# means JsonApiClient is not installed,
|
8
|
+
# so let's not even define the compiler.
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
module Tapioca
|
13
|
+
module Dsl
|
14
|
+
module Compilers
|
15
|
+
# `Tapioca::Dsl::Compilers::JsonApiClientResource` generates RBI files for classes that inherit
|
16
|
+
# [`JsonApiClient::Resource`](https://github.com/JsonApiClient/json_api_client).
|
17
|
+
#
|
18
|
+
# For example, with the following classes that inherits `JsonApiClient::Resource`:
|
19
|
+
#
|
20
|
+
# ~~~rb
|
21
|
+
# # user.rb
|
22
|
+
# class User < JsonApiClient::Resource
|
23
|
+
# has_many :posts
|
24
|
+
#
|
25
|
+
# property :name, type: :string
|
26
|
+
# property :is_admin, type: :boolean, default: false
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # post.rb
|
30
|
+
# class Post < JsonApiClient::Resource
|
31
|
+
# belongs_to :user
|
32
|
+
#
|
33
|
+
# property :title, type: :string
|
34
|
+
# end
|
35
|
+
# ~~~
|
36
|
+
#
|
37
|
+
# this compiler will produce RBI files with the following content:
|
38
|
+
#
|
39
|
+
# ~~~rbi
|
40
|
+
# # user.rbi
|
41
|
+
# # typed: strong
|
42
|
+
#
|
43
|
+
# class User
|
44
|
+
# include JsonApiClientResourceGeneratedMethods
|
45
|
+
#
|
46
|
+
# module JsonApiClientResourceGeneratedMethods
|
47
|
+
# sig { returns(T::Boolean) }
|
48
|
+
# def is_admin; end
|
49
|
+
#
|
50
|
+
# sig { params(is_admin: T::Boolean).returns(T::Boolean) }
|
51
|
+
# def is_admin=(is_admin); end
|
52
|
+
#
|
53
|
+
# sig { returns(T.nilable(::String)) }
|
54
|
+
# def name; end
|
55
|
+
#
|
56
|
+
# sig { params(name: T.nilable(::String)).returns(T.nilable(::String)) }
|
57
|
+
# def name=(name); end
|
58
|
+
#
|
59
|
+
# sig { returns(T.nilable(T::Array[Post])) }
|
60
|
+
# def posts; end
|
61
|
+
#
|
62
|
+
# sig { params(posts: T.nilable(T::Array[Post])).returns(T.nilable(T::Array[Post])) }
|
63
|
+
# def posts=(posts); end
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# # post.rbi
|
68
|
+
# # typed: strong
|
69
|
+
#
|
70
|
+
# class Post
|
71
|
+
# include JsonApiClientResourceGeneratedMethods
|
72
|
+
#
|
73
|
+
# module JsonApiClientResourceGeneratedMethods
|
74
|
+
# sig { returns(T.nilable(::String)) }
|
75
|
+
# def title; end
|
76
|
+
#
|
77
|
+
# sig { params(title: T.nilable(::String)).returns(T.nilable(::String)) }
|
78
|
+
# def title=(title); end
|
79
|
+
#
|
80
|
+
# sig { returns(T.nilable(::String)) }
|
81
|
+
# def user_id; end
|
82
|
+
#
|
83
|
+
# sig { params(user_id: T.nilable(::String)).returns(T.nilable(::String)) }
|
84
|
+
# def user_id=(user_id); end
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
# ~~~
|
88
|
+
class JsonApiClientResource < Compiler
|
89
|
+
extend T::Sig
|
90
|
+
|
91
|
+
ConstantType = type_member { { fixed: T.class_of(::JsonApiClient::Resource) } }
|
92
|
+
|
93
|
+
sig { override.void }
|
94
|
+
def decorate
|
95
|
+
schema = resource_schema
|
96
|
+
return if schema.nil? && constant.associations.empty?
|
97
|
+
|
98
|
+
root.create_path(constant) do |k|
|
99
|
+
module_name = "JsonApiClientResourceGeneratedMethods"
|
100
|
+
k.create_module(module_name) do |mod|
|
101
|
+
schema&.each_property do |property|
|
102
|
+
generate_methods_for_property(mod, property)
|
103
|
+
end
|
104
|
+
|
105
|
+
constant.associations.each do |association|
|
106
|
+
generate_methods_for_association(mod, association)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
k.create_include(module_name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class << self
|
115
|
+
extend T::Sig
|
116
|
+
|
117
|
+
sig { override.returns(T::Enumerable[Module]) }
|
118
|
+
def gather_constants
|
119
|
+
all_modules.select do |c|
|
120
|
+
name_of(c) && c < ::JsonApiClient::Resource
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
sig { returns(T.nilable(::JsonApiClient::Schema)) }
|
128
|
+
def resource_schema
|
129
|
+
schema = constant.schema
|
130
|
+
|
131
|
+
# empty? does not exist on JsonApiClient::Schema
|
132
|
+
schema if schema.size > 0 # rubocop:disable Style/ZeroLengthPredicate
|
133
|
+
end
|
134
|
+
|
135
|
+
sig do
|
136
|
+
params(
|
137
|
+
mod: RBI::Scope,
|
138
|
+
property: ::JsonApiClient::Schema::Property,
|
139
|
+
).void
|
140
|
+
end
|
141
|
+
def generate_methods_for_property(mod, property)
|
142
|
+
type = type_for(property)
|
143
|
+
|
144
|
+
name = property.name.to_s
|
145
|
+
|
146
|
+
mod.create_method(name, return_type: type)
|
147
|
+
mod.create_method("#{name}=", parameters: [create_param(name, type: type)], return_type: type)
|
148
|
+
end
|
149
|
+
|
150
|
+
sig { params(property: ::JsonApiClient::Schema::Property).returns(String) }
|
151
|
+
def type_for(property)
|
152
|
+
type = ::JsonApiClient::Schema::TypeFactory.type_for(property.type)
|
153
|
+
return "T.untyped" if type.nil?
|
154
|
+
|
155
|
+
sorbet_type = if type.respond_to?(:sorbet_type)
|
156
|
+
type.sorbet_type
|
157
|
+
elsif type == ::JsonApiClient::Schema::Types::Integer
|
158
|
+
"::Integer"
|
159
|
+
elsif type == ::JsonApiClient::Schema::Types::String
|
160
|
+
"::String"
|
161
|
+
elsif type == ::JsonApiClient::Schema::Types::Float
|
162
|
+
"::Float"
|
163
|
+
elsif type == ::JsonApiClient::Schema::Types::Time
|
164
|
+
"::Time"
|
165
|
+
elsif type == ::JsonApiClient::Schema::Types::Decimal
|
166
|
+
"::BigDecimal"
|
167
|
+
elsif type == ::JsonApiClient::Schema::Types::Boolean
|
168
|
+
"T::Boolean"
|
169
|
+
else
|
170
|
+
"T.untyped"
|
171
|
+
end
|
172
|
+
|
173
|
+
if property.default.nil?
|
174
|
+
as_nilable_type(sorbet_type)
|
175
|
+
else
|
176
|
+
sorbet_type
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
sig do
|
181
|
+
params(
|
182
|
+
mod: RBI::Scope,
|
183
|
+
association: JsonApiClient::Associations::BaseAssociation,
|
184
|
+
).void
|
185
|
+
end
|
186
|
+
def generate_methods_for_association(mod, association)
|
187
|
+
# If the association is broken, it will raise a NameError when trying to access the association_class
|
188
|
+
klass = association.association_class
|
189
|
+
|
190
|
+
name, type = case association
|
191
|
+
when ::JsonApiClient::Associations::BelongsTo::Association
|
192
|
+
# id must be a string: # https://jsonapi.org/format/#document-resource-object-identification
|
193
|
+
[association.param.to_s, "T.nilable(::String)"]
|
194
|
+
when ::JsonApiClient::Associations::HasOne::Association
|
195
|
+
[association.attr_name.to_s, "T.nilable(#{klass})"]
|
196
|
+
when ::JsonApiClient::Associations::HasMany::Association
|
197
|
+
[association.attr_name.to_s, "T.nilable(T::Array[#{klass}])"]
|
198
|
+
else
|
199
|
+
return # Unsupported association type
|
200
|
+
end
|
201
|
+
|
202
|
+
mod.create_method(name, return_type: type)
|
203
|
+
mod.create_method("#{name}=", parameters: [create_param(name, type: type)], return_type: type)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -13,8 +13,26 @@ module Tapioca
|
|
13
13
|
@constant = constant
|
14
14
|
end
|
15
15
|
|
16
|
+
sig { params(attribute_name: String, column_name: String).returns([String, String]) }
|
17
|
+
def type_for(attribute_name, column_name = attribute_name)
|
18
|
+
return id_type if attribute_name == "id"
|
19
|
+
|
20
|
+
column_type_for(column_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
sig { returns([String, String]) }
|
26
|
+
def id_type
|
27
|
+
if @constant.respond_to?(:composite_primary_key?) && T.unsafe(@constant).composite_primary_key?
|
28
|
+
@constant.primary_key.map(&method(:column_type_for)).map { |tuple| "[#{tuple.join(", ")}]" }
|
29
|
+
else
|
30
|
+
column_type_for(@constant.primary_key)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
16
34
|
sig { params(column_name: String).returns([String, String]) }
|
17
|
-
def
|
35
|
+
def column_type_for(column_name)
|
18
36
|
return ["T.untyped", "T.untyped"] if do_not_generate_strong_types?(@constant)
|
19
37
|
|
20
38
|
column = @constant.columns_hash[column_name]
|
@@ -33,7 +51,7 @@ module Tapioca
|
|
33
51
|
return [getter_type, as_nilable_type(setter_type)]
|
34
52
|
end
|
35
53
|
|
36
|
-
if
|
54
|
+
if Array(@constant.primary_key).include?(column_name) ||
|
37
55
|
column_name == "created_at" ||
|
38
56
|
column_name == "updated_at"
|
39
57
|
getter_type = as_nilable_type(getter_type)
|
@@ -42,8 +60,6 @@ module Tapioca
|
|
42
60
|
[getter_type, setter_type]
|
43
61
|
end
|
44
62
|
|
45
|
-
private
|
46
|
-
|
47
63
|
sig { params(column_type: T.untyped).returns(String) }
|
48
64
|
def type_for_activerecord_value(column_type)
|
49
65
|
case column_type
|
@@ -9,8 +9,16 @@ module Tapioca
|
|
9
9
|
|
10
10
|
extend T::Sig
|
11
11
|
|
12
|
-
sig { params(
|
13
|
-
def type_for(
|
12
|
+
sig { params(argument: GraphQL::Schema::Argument).returns(String) }
|
13
|
+
def type_for(argument)
|
14
|
+
type = if argument.loads
|
15
|
+
loads_type = ::GraphQL::Schema::Wrapper.new(argument.loads)
|
16
|
+
loads_type = loads_type.to_list_type if argument.type.list?
|
17
|
+
loads_type = loads_type.to_non_null_type if argument.type.non_null?
|
18
|
+
loads_type
|
19
|
+
else
|
20
|
+
argument.type
|
21
|
+
end
|
14
22
|
unwrapped_type = type.unwrap
|
15
23
|
|
16
24
|
parsed_type = case unwrapped_type
|
@@ -39,6 +47,10 @@ module Tapioca
|
|
39
47
|
end
|
40
48
|
when GraphQL::Schema::InputObject.singleton_class
|
41
49
|
type_for_constant(unwrapped_type)
|
50
|
+
when GraphQL::Schema::NonNull.singleton_class
|
51
|
+
type_for(unwrapped_type.of_type)
|
52
|
+
when Module
|
53
|
+
Runtime::Reflection.qualified_name_of(unwrapped_type) || "T.untyped"
|
42
54
|
else
|
43
55
|
"T.untyped"
|
44
56
|
end
|
@@ -47,7 +59,7 @@ module Tapioca
|
|
47
59
|
parsed_type = "T::Array[#{parsed_type}]"
|
48
60
|
end
|
49
61
|
|
50
|
-
unless type.non_null?
|
62
|
+
unless type.non_null? || has_replaceable_default?(argument)
|
51
63
|
parsed_type = RBIHelper.as_nilable_type(parsed_type)
|
52
64
|
end
|
53
65
|
|
@@ -60,6 +72,11 @@ module Tapioca
|
|
60
72
|
def type_for_constant(constant)
|
61
73
|
Runtime::Reflection.qualified_name_of(constant) || "T.untyped"
|
62
74
|
end
|
75
|
+
|
76
|
+
sig { params(argument: GraphQL::Schema::Argument).returns(T::Boolean) }
|
77
|
+
def has_replaceable_default?(argument)
|
78
|
+
!!argument.replace_null_with_default? && !argument.default_value.nil?
|
79
|
+
end
|
63
80
|
end
|
64
81
|
end
|
65
82
|
end
|
data/lib/tapioca/dsl/pipeline.rb
CHANGED
@@ -105,10 +105,12 @@ module Tapioca
|
|
105
105
|
|
106
106
|
sig { returns(T::Array[T.class_of(Compiler)]) }
|
107
107
|
def compilers
|
108
|
-
@compilers
|
109
|
-
|
110
|
-
|
111
|
-
|
108
|
+
@compilers ||= T.let(
|
109
|
+
Runtime::Reflection.descendants_of(Compiler).sort_by do |compiler|
|
110
|
+
T.must(compiler.name)
|
111
|
+
end,
|
112
|
+
T.nilable(T::Array[T.class_of(Compiler)]),
|
113
|
+
)
|
112
114
|
end
|
113
115
|
|
114
116
|
private
|