tapioca 0.11.8 → 0.11.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +116 -49
  3. data/lib/tapioca/cli.rb +76 -67
  4. data/lib/tapioca/commands/{dsl.rb → abstract_dsl.rb} +32 -78
  5. data/lib/tapioca/commands/{gem.rb → abstract_gem.rb} +26 -93
  6. data/lib/tapioca/commands/annotations.rb +9 -7
  7. data/lib/tapioca/commands/check_shims.rb +2 -0
  8. data/lib/tapioca/commands/command.rb +9 -2
  9. data/lib/tapioca/commands/configure.rb +2 -2
  10. data/lib/tapioca/commands/dsl_compiler_list.rb +31 -0
  11. data/lib/tapioca/commands/dsl_generate.rb +40 -0
  12. data/lib/tapioca/commands/dsl_verify.rb +25 -0
  13. data/lib/tapioca/commands/gem_generate.rb +51 -0
  14. data/lib/tapioca/commands/gem_sync.rb +37 -0
  15. data/lib/tapioca/commands/gem_verify.rb +36 -0
  16. data/lib/tapioca/commands/require.rb +2 -0
  17. data/lib/tapioca/commands/todo.rb +21 -2
  18. data/lib/tapioca/commands.rb +8 -2
  19. data/lib/tapioca/dsl/compiler.rb +8 -4
  20. data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +3 -1
  21. data/lib/tapioca/dsl/compilers/active_model_validations_confirmation.rb +94 -0
  22. data/lib/tapioca/dsl/compilers/active_record_columns.rb +19 -9
  23. data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +11 -14
  24. data/lib/tapioca/dsl/compilers/active_record_store.rb +149 -0
  25. data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +3 -2
  26. data/lib/tapioca/dsl/compilers/active_support_concern.rb +1 -1
  27. data/lib/tapioca/dsl/compilers/graphql_input_object.rb +1 -1
  28. data/lib/tapioca/dsl/compilers/graphql_mutation.rb +9 -2
  29. data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +208 -0
  30. data/lib/tapioca/dsl/extensions/active_record.rb +9 -0
  31. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +23 -4
  32. data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +1 -0
  33. data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +18 -3
  34. data/lib/tapioca/dsl/pipeline.rb +6 -4
  35. data/lib/tapioca/gem/pipeline.rb +103 -36
  36. data/lib/tapioca/gemfile.rb +13 -7
  37. data/lib/tapioca/helpers/git_attributes.rb +34 -0
  38. data/lib/tapioca/helpers/test/template.rb +4 -4
  39. data/lib/tapioca/internal.rb +1 -0
  40. data/lib/tapioca/loaders/dsl.rb +11 -1
  41. data/lib/tapioca/loaders/loader.rb +17 -2
  42. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +0 -27
  43. data/lib/tapioca/static/symbol_loader.rb +10 -9
  44. data/lib/tapioca/version.rb +1 -1
  45. metadata +20 -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
@@ -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 :Dsl, "tapioca/commands/dsl"
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 :Gem, "tapioca/commands/gem"
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
@@ -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 = T.let(@all_classes, T.nilable(T::Enumerable[T::Class[T.anything]]))
49
- @all_classes ||= T.cast(ObjectSpace.each_object(Class), T::Enumerable[T::Class[T.anything]]).each
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 = T.let(@all_modules, T.nilable(T::Enumerable[Module]))
55
- @all_modules ||= T.cast(ObjectSpace.each_object(Module), T::Enumerable[Module]).each
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).reject(&:abstract?).select(&:name)
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 |column_name|
114
- add_methods_for_attribute(mod, column_name)
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, column_name, attribute_name, methods_to_add)
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[[String, String]],
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.map { |param, type| create_param(param, type: type) },
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, column_name, attribute_name = column_name, methods_to_add = nil)
175
- getter_type, setter_type = Helpers::ActiveRecordColumnTypeHelper.new(constant).type_for(column_name)
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: [["value", setter_type]],
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,
@@ -73,20 +73,17 @@ module Tapioca
73
73
  sig { returns(T::Class[ActiveRecord::TestFixtures]) }
74
74
  def fixture_loader
75
75
  @fixture_loader ||= T.let(
76
- T.cast(
77
- Class.new do
78
- T.unsafe(self).include(ActiveRecord::TestFixtures)
79
-
80
- T.unsafe(self).fixture_path = Rails.root.join("test", "fixtures")
81
- # https://github.com/rails/rails/blob/7c70791470fc517deb7c640bead9f1b47efb5539/activerecord/lib/active_record/test_fixtures.rb#L46
82
- singleton_class.define_method(:file_fixture_path) do
83
- Rails.root.join("test", "fixtures", "files")
84
- end
85
-
86
- T.unsafe(self).fixtures(:all)
87
- end,
88
- T::Class[ActiveRecord::TestFixtures],
89
- ),
76
+ Class.new do
77
+ T.unsafe(self).include(ActiveRecord::TestFixtures)
78
+
79
+ T.unsafe(self).fixture_path = Rails.root.join("test", "fixtures")
80
+ # https://github.com/rails/rails/blob/7c70791470fc517deb7c640bead9f1b47efb5539/activerecord/lib/active_record/test_fixtures.rb#L46
81
+ singleton_class.define_method(:file_fixture_path) do
82
+ Rails.root.join("test", "fixtures", "files")
83
+ end
84
+
85
+ T.unsafe(self).fixtures(:all)
86
+ end,
90
87
  T.nilable(T::Class[ActiveRecord::TestFixtures]),
91
88
  )
92
89
  end
@@ -0,0 +1,149 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "active_record"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ require "tapioca/dsl/helpers/active_record_constants_helper"
11
+
12
+ module Tapioca
13
+ module Dsl
14
+ module Compilers
15
+ # `Tapioca::Dsl::Compilers::ActiveRecordStore` decorates RBI files for all
16
+ # classes that use [`ActiveRecord::Store`](https://api.rubyonrails.org/classes/ActiveRecord/Store.html).
17
+ #
18
+ # For example, with the following class:
19
+ #
20
+ # ~~~rb
21
+ # class User < ActiveRecord::Base
22
+ # store :settings, accessors: :theme
23
+ # store_accessor :settings, :power_source, prefix: :prefs
24
+ # end
25
+ # ~~~
26
+ #
27
+ # this compiler will produce an RBI file with the following content:
28
+ # ~~~rbi
29
+ # # typed: true
30
+ #
31
+ # class User
32
+ # include GeneratedStoredAttributesMethods
33
+ #
34
+ # module GeneratedStoredAttributesMethods
35
+ # sig { returns(T.untyped) }
36
+ # def prefs_power_source; end
37
+ #
38
+ # sig { params(value: T.untyped).returns(T.untyped) }
39
+ # def prefs_power_source=(value); end
40
+ #
41
+ # sig { returns(T.untyped) }
42
+ # def prefs_power_source_before_last_save; end
43
+ #
44
+ # sig { returns(T.untyped) }
45
+ # def prefs_power_source_change; end
46
+ #
47
+ # sig { returns(T::Boolean) }
48
+ # def prefs_power_source_changed?; end
49
+ #
50
+ # sig { returns(T.untyped) }
51
+ # def prefs_power_source_was; end
52
+ #
53
+ # sig { returns(T.untyped) }
54
+ # def saved_change_to_prefs_power_source; end
55
+ #
56
+ # sig { returns(T::Boolean) }
57
+ # def saved_change_to_prefs_power_source?; end
58
+ #
59
+ # sig { returns(T.untyped) }
60
+ # def saved_change_to_theme; end
61
+ #
62
+ # sig { returns(T::Boolean) }
63
+ # def saved_change_to_theme?; end
64
+ #
65
+ # sig { returns(T.untyped) }
66
+ # def theme; end
67
+ #
68
+ # sig { params(value: T.untyped).returns(T.untyped) }
69
+ # def theme=(value); end
70
+ #
71
+ # sig { returns(T.untyped) }
72
+ # def theme_before_last_save; end
73
+ #
74
+ # sig { returns(T.untyped) }
75
+ # def theme_change; end
76
+ #
77
+ # sig { returns(T::Boolean) }
78
+ # def theme_changed?; end
79
+ #
80
+ # sig { returns(T.untyped) }
81
+ # def theme_was; end
82
+ # end
83
+ # end
84
+ # ~~~
85
+ class ActiveRecordStore < Compiler
86
+ extend T::Sig
87
+ include Helpers::ActiveRecordConstantsHelper
88
+
89
+ ConstantType = type_member { { fixed: T.all(T.class_of(ActiveRecord::Base), Extensions::ActiveRecord) } }
90
+
91
+ sig { override.void }
92
+ def decorate
93
+ return if constant.__tapioca_stored_attributes.nil?
94
+
95
+ root.create_path(constant) do |klass|
96
+ klass.create_module(StoredAttributesModuleName) do |mod|
97
+ constant.__tapioca_stored_attributes.each do |store_attribute, keys, prefix, suffix|
98
+ accessor_prefix =
99
+ case prefix
100
+ when String, Symbol
101
+ "#{prefix}_"
102
+ when TrueClass
103
+ "#{store_attribute}_"
104
+ else
105
+ ""
106
+ end
107
+ accessor_suffix =
108
+ case suffix
109
+ when String, Symbol
110
+ "_#{suffix}"
111
+ when TrueClass
112
+ "_#{store_attribute}"
113
+ else
114
+ ""
115
+ end
116
+
117
+ keys.flatten.map { |key| "#{accessor_prefix}#{key}#{accessor_suffix}" }.each do |accessor_key|
118
+ mod.create_method(
119
+ "#{accessor_key}=",
120
+ parameters: [create_param("value", type: "T.untyped")],
121
+ return_type: "T.untyped",
122
+ )
123
+ mod.create_method(accessor_key, return_type: "T.untyped")
124
+ mod.create_method("#{accessor_key}_changed?", return_type: "T::Boolean")
125
+ mod.create_method("#{accessor_key}_change", return_type: "T.untyped")
126
+ mod.create_method("#{accessor_key}_was", return_type: "T.untyped")
127
+ mod.create_method("saved_change_to_#{accessor_key}?", return_type: "T::Boolean")
128
+ mod.create_method("saved_change_to_#{accessor_key}", return_type: "T.untyped")
129
+ mod.create_method("#{accessor_key}_before_last_save", return_type: "T.untyped")
130
+ end
131
+ end
132
+ end
133
+
134
+ klass.create_include(StoredAttributesModuleName)
135
+ end
136
+ end
137
+
138
+ class << self
139
+ extend T::Sig
140
+
141
+ sig { override.returns(T::Enumerable[Module]) }
142
+ def gather_constants
143
+ descendants_of(::ActiveRecord::Base).reject(&:abstract_class?)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -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
@@ -85,7 +85,7 @@ module Tapioca
85
85
 
86
86
  sig { params(concern: Module).returns(T::Array[Module]) }
87
87
  def dependencies_of(concern)
88
- concern.instance_variable_get(:@_dependencies)
88
+ concern.instance_variable_get(:@_dependencies) || []
89
89
  end
90
90
  end
91
91
 
@@ -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.type))
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::InputObject) } }
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, argument ? Helpers::GraphqlTypeHelper.type_for(argument.type) : "T.untyped")
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