tapioca 0.11.8 → 0.11.10

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.
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