tapioca 0.11.8 → 0.11.9
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/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
|