tapioca 0.10.1 → 0.10.3

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -3
  3. data/lib/tapioca/cli.rb +24 -12
  4. data/lib/tapioca/commands/annotations.rb +2 -2
  5. data/lib/tapioca/commands/check_shims.rb +13 -21
  6. data/lib/tapioca/commands/command.rb +2 -2
  7. data/lib/tapioca/commands/command_without_tracker.rb +18 -0
  8. data/lib/tapioca/commands/configure.rb +3 -3
  9. data/lib/tapioca/commands/dsl.rb +16 -11
  10. data/lib/tapioca/commands/gem.rb +5 -5
  11. data/lib/tapioca/commands/require.rb +2 -2
  12. data/lib/tapioca/commands/todo.rb +2 -2
  13. data/lib/tapioca/commands.rb +1 -0
  14. data/lib/tapioca/dsl/compiler.rb +3 -3
  15. data/lib/tapioca/dsl/compilers/aasm.rb +4 -4
  16. data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +1 -1
  17. data/lib/tapioca/dsl/compilers/action_mailer.rb +1 -1
  18. data/lib/tapioca/dsl/compilers/active_job.rb +2 -2
  19. data/lib/tapioca/dsl/compilers/active_model_attributes.rb +1 -1
  20. data/lib/tapioca/dsl/compilers/active_record_associations.rb +48 -13
  21. data/lib/tapioca/dsl/compilers/active_record_columns.rb +22 -22
  22. data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +1 -1
  23. data/lib/tapioca/dsl/compilers/active_record_relations.rb +52 -48
  24. data/lib/tapioca/dsl/compilers/active_record_scope.rb +3 -3
  25. data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +2 -2
  26. data/lib/tapioca/dsl/compilers/active_storage.rb +2 -2
  27. data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +1 -1
  28. data/lib/tapioca/dsl/compilers/config.rb +2 -2
  29. data/lib/tapioca/dsl/compilers/graphql_input_object.rb +1 -0
  30. data/lib/tapioca/dsl/compilers/graphql_mutation.rb +1 -0
  31. data/lib/tapioca/dsl/compilers/identity_cache.rb +12 -12
  32. data/lib/tapioca/dsl/compilers/protobuf.rb +21 -10
  33. data/lib/tapioca/dsl/compilers/rails_generators.rb +2 -2
  34. data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +1 -1
  35. data/lib/tapioca/dsl/compilers/smart_properties.rb +2 -2
  36. data/lib/tapioca/dsl/compilers/state_machines.rb +24 -24
  37. data/lib/tapioca/dsl/compilers/url_helpers.rb +1 -1
  38. data/lib/tapioca/dsl/compilers.rb +1 -1
  39. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +19 -0
  40. data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +1 -1
  41. data/lib/tapioca/dsl/pipeline.rb +5 -4
  42. data/lib/tapioca/executor.rb +2 -2
  43. data/lib/tapioca/gem/events.rb +1 -1
  44. data/lib/tapioca/gem/listeners/foreign_constants.rb +3 -2
  45. data/lib/tapioca/gem/listeners/methods.rb +3 -3
  46. data/lib/tapioca/gem/listeners/mixins.rb +3 -7
  47. data/lib/tapioca/gem/listeners/source_location.rb +1 -1
  48. data/lib/tapioca/gem/listeners/subconstants.rb +1 -1
  49. data/lib/tapioca/gem/listeners/yard_doc.rb +1 -1
  50. data/lib/tapioca/gem/pipeline.rb +11 -9
  51. data/lib/tapioca/gemfile.rb +4 -4
  52. data/lib/tapioca/helpers/config_helper.rb +4 -4
  53. data/lib/tapioca/helpers/env_helper.rb +1 -0
  54. data/lib/tapioca/helpers/gem_helper.rb +17 -5
  55. data/lib/tapioca/helpers/rbi_files_helper.rb +3 -3
  56. data/lib/tapioca/helpers/rbi_helper.rb +5 -13
  57. data/lib/tapioca/helpers/sorbet_helper.rb +3 -5
  58. data/lib/tapioca/helpers/source_uri.rb +1 -1
  59. data/lib/tapioca/helpers/test/dsl_compiler.rb +1 -1
  60. data/lib/tapioca/loaders/dsl.rb +9 -7
  61. data/lib/tapioca/loaders/gem.rb +2 -2
  62. data/lib/tapioca/loaders/loader.rb +6 -6
  63. data/lib/tapioca/rbi_ext/model.rb +8 -5
  64. data/lib/tapioca/rbi_formatter.rb +2 -2
  65. data/lib/tapioca/runtime/generic_type_registry.rb +22 -2
  66. data/lib/tapioca/runtime/reflection.rb +63 -2
  67. data/lib/tapioca/runtime/trackers/autoload.rb +3 -0
  68. data/lib/tapioca/runtime/trackers/constant_definition.rb +13 -5
  69. data/lib/tapioca/runtime/trackers/mixin.rb +37 -36
  70. data/lib/tapioca/runtime/trackers/required_ancestor.rb +17 -4
  71. data/lib/tapioca/runtime/trackers/tracker.rb +45 -0
  72. data/lib/tapioca/runtime/trackers.rb +27 -1
  73. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +17 -6
  74. data/lib/tapioca/static/symbol_loader.rb +3 -3
  75. data/lib/tapioca/static/symbol_table_parser.rb +6 -0
  76. data/lib/tapioca/version.rb +1 -1
  77. data/lib/tapioca.rb +0 -10
  78. metadata +8 -20
@@ -24,7 +24,7 @@ module Tapioca
24
24
  # The way we identify these "foreign constants" is by asking the mixin tracker which
25
25
  # constants have mixed in the current module that we are handling. We add all the
26
26
  # constants that we discover to the pipeline to be processed.
27
- Runtime::Trackers::Mixin.constants_with_mixin(mixin).each_value do |location_info|
27
+ Runtime::Trackers::Mixin.constants_with_mixin(mixin).each do |mixin_type, location_info|
28
28
  location_info.each do |constant, location|
29
29
  next unless mixed_in_by_gem?(location)
30
30
 
@@ -35,10 +35,11 @@ module Tapioca
35
35
  # base constant. Then, generate RBIs as if the base constant is extending the mixin,
36
36
  # which is functionally equivalent to including or prepending to the singleton class.
37
37
  if !name && constant.singleton_class?
38
- attached_class = attached_class_of(constant)
38
+ attached_class = Runtime::Trackers::Mixin.resolve_to_attached_class(constant, mixin, mixin_type)
39
39
  next unless attached_class
40
40
 
41
41
  constant = attached_class
42
+ name = @pipeline.name_of(constant)
42
43
  end
43
44
 
44
45
  @pipeline.push_foreign_constant(name, constant) if name
@@ -28,7 +28,7 @@ module Tapioca
28
28
  tree: RBI::Tree,
29
29
  module_name: String,
30
30
  mod: Module,
31
- for_visibility: T::Array[Symbol]
31
+ for_visibility: T::Array[Symbol],
32
32
  ).void
33
33
  end
34
34
  def compile_directly_owned_methods(tree, module_name, mod, for_visibility = [:public, :protected, :private])
@@ -57,7 +57,7 @@ module Tapioca
57
57
  symbol_name: String,
58
58
  constant: Module,
59
59
  method: T.nilable(UnboundMethod),
60
- visibility: RBI::Visibility
60
+ visibility: RBI::Visibility,
61
61
  ).void
62
62
  end
63
63
  def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public.new)
@@ -112,7 +112,7 @@ module Tapioca
112
112
  rbi_method = RBI::Method.new(
113
113
  method_name,
114
114
  is_singleton: constant.singleton_class?,
115
- visibility: visibility
115
+ visibility: visibility,
116
116
  )
117
117
 
118
118
  sanitized_parameters.each do |type, name|
@@ -36,7 +36,7 @@ module Tapioca
36
36
  tree: RBI::Tree,
37
37
  constant: Module,
38
38
  mods: T::Array[Module],
39
- mixin_type: Runtime::Trackers::Mixin::Type
39
+ mixin_type: Runtime::Trackers::Mixin::Type,
40
40
  ).void
41
41
  end
42
42
  def add_mixins(tree, constant, mods, mixin_type)
@@ -69,15 +69,11 @@ module Tapioca
69
69
  params(
70
70
  constant: Module,
71
71
  mixin: Module,
72
- mixin_type: Runtime::Trackers::Mixin::Type
72
+ mixin_type: Runtime::Trackers::Mixin::Type,
73
73
  ).returns(T::Boolean)
74
74
  end
75
75
  def mixed_in_by_gem?(constant, mixin, mixin_type)
76
- mixin_location =
77
- T.cast(
78
- Runtime::Trackers::Mixin.constants_with_mixin(mixin).dig(mixin_type, constant),
79
- T.nilable(String)
80
- )
76
+ mixin_location = Runtime::Trackers::Mixin.mixin_location(mixin, mixin_type, constant)
81
77
 
82
78
  return true if mixin_location.nil?
83
79
 
@@ -61,7 +61,7 @@ module Tapioca
61
61
  gem_name: gem.name,
62
62
  gem_version: version,
63
63
  path: path.to_s,
64
- line_number: line.to_s
64
+ line_number: line.to_s,
65
65
  )
66
66
  node.comments << RBI::Comment.new("") if node.comments.any?
67
67
  node.comments << RBI::Comment.new(uri.to_s)
@@ -26,7 +26,7 @@ module Tapioca
26
26
  # Don't compile modules of Object because Object::Foo == Foo
27
27
  # Don't compile modules of BasicObject because BasicObject::BasicObject == BasicObject
28
28
  next if (Object == constant || BasicObject == constant) && Module === subconstant
29
- next unless subconstant
29
+ next unless Runtime::Reflection.constant_defined?(subconstant)
30
30
 
31
31
  @pipeline.push_constant(name, subconstant)
32
32
  end
@@ -43,7 +43,7 @@ module Tapioca
43
43
  separator = event.constant.singleton_class? ? "." : "#"
44
44
  event.node.comments = documentation_comments(
45
45
  "#{event.symbol}#{separator}#{event.node.name}",
46
- sigs: event.node.sigs
46
+ sigs: event.node.sigs,
47
47
  )
48
48
  end
49
49
 
@@ -91,7 +91,7 @@ module Tapioca
91
91
  method: UnboundMethod,
92
92
  node: RBI::Method,
93
93
  signature: T.untyped,
94
- parameters: T::Array[[Symbol, String]]
94
+ parameters: T::Array[[Symbol, String]],
95
95
  ).void.checked(:never)
96
96
  end
97
97
  def push_method(symbol, constant, method, node, signature, parameters) # rubocop:disable Metrics/ParameterLists
@@ -162,7 +162,7 @@ module Tapioca
162
162
  return if symbol_in_payload?(symbol) && !@bootstrap_symbols.include?(symbol)
163
163
 
164
164
  constant = constantize(symbol)
165
- push_constant(symbol, constant) if constant
165
+ push_constant(symbol, constant) if Runtime::Reflection.constant_defined?(constant)
166
166
  end
167
167
 
168
168
  sig { params(event: Gem::ConstantFound).void.checked(:never) }
@@ -260,6 +260,7 @@ module Tapioca
260
260
  return if klass_name&.start_with?("T::Types::", "T::Private::")
261
261
 
262
262
  type_name = klass_name || "T.untyped"
263
+ type_name = "T.untyped" if type_name == "NilClass"
263
264
  node = RBI::Const.new(name, "T.let(T.unsafe(nil), #{type_name})")
264
265
  push_const(name, klass, node)
265
266
  @root << node
@@ -321,7 +322,7 @@ module Tapioca
321
322
  next unless superclass_name
322
323
 
323
324
  resolved_superclass = constantize(superclass_name)
324
- next unless Module === resolved_superclass
325
+ next unless Module === resolved_superclass && Runtime::Reflection.constant_defined?(resolved_superclass)
325
326
  next if name_of(resolved_superclass) == constant_name
326
327
 
327
328
  # We found a suitable superclass
@@ -341,7 +342,7 @@ module Tapioca
341
342
 
342
343
  sig { params(constant: Module, strict: T::Boolean).returns(T::Boolean) }
343
344
  def defined_in_gem?(constant, strict: true)
344
- files = Set.new(get_file_candidates(constant))
345
+ files = get_file_candidates(constant)
345
346
  .merge(Runtime::Trackers::ConstantDefinition.files_for(constant))
346
347
 
347
348
  return !strict if files.empty?
@@ -351,13 +352,11 @@ module Tapioca
351
352
  end
352
353
  end
353
354
 
354
- sig { params(constant: Module).returns(T::Array[String]) }
355
+ sig { params(constant: Module).returns(T::Set[String]) }
355
356
  def get_file_candidates(constant)
356
- wrapped_module = Pry::WrappedModule.new(constant)
357
-
358
- wrapped_module.send(:method_candidates).flatten.filter_map(&:source_file).uniq
357
+ file_candidates_for(constant)
359
358
  rescue ArgumentError, NameError
360
- []
359
+ Set.new
361
360
  end
362
361
 
363
362
  sig { params(name: String).void }
@@ -390,6 +389,9 @@ module Tapioca
390
389
  type_variables = Runtime::GenericTypeRegistry.lookup_type_variables(constant)
391
390
  return type_name unless type_variables
392
391
 
392
+ type_variables = type_variables.reject(&:fixed?)
393
+ return type_name if type_variables.empty?
394
+
393
395
  type_variable_names = type_variables.map { "T.untyped" }.join(", ")
394
396
 
395
397
  "#{type_name}[#{type_variable_names}]"
@@ -8,7 +8,7 @@ module Tapioca
8
8
  Spec = T.type_alias do
9
9
  T.any(
10
10
  ::Bundler::StubSpecification,
11
- ::Gem::Specification
11
+ ::Gem::Specification,
12
12
  )
13
13
  end
14
14
 
@@ -147,7 +147,7 @@ module Tapioca
147
147
  .flat_map do |spec|
148
148
  spec.files.filter_map { |file| [file.realpath.to_s, spec] if file.exist? }
149
149
  end.to_h,
150
- T.nilable(T::Hash[String, Gemfile::GemSpec])
150
+ T.nilable(T::Hash[String, Gemfile::GemSpec]),
151
151
  )
152
152
  end
153
153
  end
@@ -157,7 +157,7 @@ module Tapioca
157
157
  "sorbet", "sorbet-static", "sorbet-runtime", "sorbet-static-and-runtime",
158
158
  "debug", "fakefs",
159
159
  ].freeze,
160
- T::Array[String]
160
+ T::Array[String],
161
161
  )
162
162
 
163
163
  sig { returns(String) }
@@ -201,7 +201,7 @@ module Tapioca
201
201
  if default_gem?
202
202
  files.any? { |file| file.to_s == to_realpath(path) }
203
203
  else
204
- to_realpath(path).start_with?(full_gem_path) || has_parent_gemspec?(path)
204
+ path_in_dir?(to_realpath(path), full_gem_path) || has_parent_gemspec?(path)
205
205
  end
206
206
  end
207
207
 
@@ -96,7 +96,7 @@ module Tapioca
96
96
  params(
97
97
  command_options: T::Hash[Symbol, Thor::Option],
98
98
  config_key: String,
99
- config_options: T::Hash[T.untyped, T.untyped]
99
+ config_options: T::Hash[T.untyped, T.untyped],
100
100
  ).returns(T::Array[ConfigError])
101
101
  end
102
102
  def validate_config_options(command_options, config_key, config_options)
@@ -157,18 +157,18 @@ module Tapioca
157
157
  if match
158
158
  ConfigErrorMessagePart.new(
159
159
  message: "#{match[1]}#{match[2]}",
160
- colors: [:bold, :blue]
160
+ colors: [:bold, :blue],
161
161
  )
162
162
  else
163
163
  ConfigErrorMessagePart.new(
164
164
  message: part,
165
- colors: [:yellow]
165
+ colors: [:yellow],
166
166
  )
167
167
  end
168
168
  end
169
169
 
170
170
  ConfigError.new(
171
- message_parts: message_parts
171
+ message_parts: message_parts,
172
172
  )
173
173
  end
174
174
 
@@ -11,6 +11,7 @@ module Tapioca
11
11
  sig { params(options: T::Hash[Symbol, T.untyped]).void }
12
12
  def set_environment(options) # rubocop:disable Naming/AccessorMethodName
13
13
  ENV["RAILS_ENV"] = ENV["RACK_ENV"] = options[:environment]
14
+ ENV["RUBY_DEBUG_ENABLE"] = "0"
14
15
  end
15
16
  end
16
17
  end
@@ -5,15 +5,17 @@ module Tapioca
5
5
  module GemHelper
6
6
  extend T::Sig
7
7
 
8
- sig { params(gemfile_dir: String, full_gem_path: String).returns(T::Boolean) }
9
- def gem_in_app_dir?(gemfile_dir, full_gem_path)
10
- !gem_in_bundle_path?(to_realpath(full_gem_path)) &&
11
- full_gem_path.start_with?(to_realpath(gemfile_dir))
8
+ sig { params(app_dir: String, full_gem_path: String).returns(T::Boolean) }
9
+ def gem_in_app_dir?(app_dir, full_gem_path)
10
+ app_dir = to_realpath(app_dir)
11
+ full_gem_path = to_realpath(full_gem_path)
12
+
13
+ !gem_in_bundle_path?(full_gem_path) && path_in_dir?(full_gem_path, app_dir)
12
14
  end
13
15
 
14
16
  sig { params(full_gem_path: String).returns(T::Boolean) }
15
17
  def gem_in_bundle_path?(full_gem_path)
16
- full_gem_path.start_with?(Bundler.bundle_path.to_s, Bundler.app_cache.to_s)
18
+ path_in_dir?(full_gem_path, Bundler.bundle_path) || path_in_dir?(full_gem_path, Bundler.app_cache)
17
19
  end
18
20
 
19
21
  sig { params(path: T.any(String, Pathname)).returns(String) }
@@ -22,5 +24,15 @@ module Tapioca
22
24
  path_string = File.realpath(path_string) if File.exist?(path_string)
23
25
  path_string
24
26
  end
27
+
28
+ private
29
+
30
+ sig { params(path: T.any(Pathname, String), dir: T.any(Pathname, String)).returns(T::Boolean) }
31
+ def path_in_dir?(path, dir)
32
+ dir = Pathname.new(dir)
33
+ path = Pathname.new(path)
34
+
35
+ path.ascend.any?(dir)
36
+ end
25
37
  end
26
38
  end
@@ -42,7 +42,7 @@ module Tapioca
42
42
  params(
43
43
  index: RBI::Index,
44
44
  shim_rbi_dir: String,
45
- todo_rbi_file: String
45
+ todo_rbi_file: String,
46
46
  ).returns(T::Hash[String, T::Array[RBI::Node]])
47
47
  end
48
48
  def duplicated_nodes_from_index(index, shim_rbi_dir:, todo_rbi_file:)
@@ -80,7 +80,7 @@ module Tapioca
80
80
  dsl_dir: String,
81
81
  auto_strictness: T::Boolean,
82
82
  gems: T::Array[Gemfile::GemSpec],
83
- compilers: T::Enumerable[Class]
83
+ compilers: T::Enumerable[Class],
84
84
  ).void
85
85
  end
86
86
  def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [], compilers: [])
@@ -92,7 +92,7 @@ module Tapioca
92
92
  "--error-url-base=#{error_url_base}",
93
93
  "--stop-after namer",
94
94
  dsl_dir,
95
- gem_dir
95
+ gem_dir,
96
96
  )
97
97
  say(" Done", :green)
98
98
 
@@ -17,28 +17,20 @@ module Tapioca
17
17
  variance: Symbol,
18
18
  fixed: T.nilable(String),
19
19
  upper: T.nilable(String),
20
- lower: T.nilable(String)
20
+ lower: T.nilable(String),
21
21
  ).returns(String)
22
22
  end
23
23
  def serialize_type_variable(type, variance, fixed, upper, lower)
24
24
  variance = nil if variance == :invariant
25
25
 
26
- bounds = []
27
- bounds << "fixed: #{fixed}" if fixed
28
- bounds << "lower: #{lower}" if lower
29
- bounds << "upper: #{upper}" if upper
30
-
31
- parameters = []
32
26
  block = []
27
+ block << "fixed: #{fixed}" if fixed
28
+ block << "lower: #{lower}" if lower
29
+ block << "upper: #{upper}" if upper
33
30
 
31
+ parameters = []
34
32
  parameters << ":#{variance}" if variance
35
33
 
36
- if sorbet_supports?(:type_variable_block_syntax)
37
- block = bounds
38
- else
39
- parameters.concat(bounds)
40
- end
41
-
42
34
  serialized = type.dup
43
35
  serialized << "(#{parameters.join(", ")})" unless parameters.empty?
44
36
  serialized << " { { #{block.join(", ")} } }" unless block.empty?
@@ -7,12 +7,12 @@ module Tapioca
7
7
 
8
8
  SORBET_GEM_SPEC = T.let(
9
9
  ::Gem::Specification.find_by_name("sorbet-static"),
10
- ::Gem::Specification
10
+ ::Gem::Specification,
11
11
  )
12
12
 
13
13
  SORBET_BIN = T.let(
14
14
  Pathname.new(SORBET_GEM_SPEC.full_gem_path) / "libexec" / "sorbet",
15
- Pathname
15
+ Pathname,
16
16
  )
17
17
 
18
18
  SORBET_EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"
@@ -20,9 +20,7 @@ module Tapioca
20
20
  SORBET_PAYLOAD_URL = "https://github.com/sorbet/sorbet/tree/master/rbi"
21
21
 
22
22
  FEATURE_REQUIREMENTS = T.let({
23
- to_ary_nil_support: ::Gem::Requirement.new(">= 0.5.9220"), # https://github.com/sorbet/sorbet/pull/4706
24
- print_payload_sources: ::Gem::Requirement.new(">= 0.5.9818"), # https://github.com/sorbet/sorbet/pull/5504
25
- type_variable_block_syntax: ::Gem::Requirement.new(">= 0.5.9892"), # https://github.com/sorbet/sorbet/pull/5639
23
+ # feature_name: ::Gem::Requirement.new(">= ___"), # https://github.com/sorbet/sorbet/pull/___
26
24
  }.freeze, T::Hash[Symbol, ::Gem::Requirement])
27
25
 
28
26
  sig { params(sorbet_args: String).returns(Spoom::ExecResult) }
@@ -29,7 +29,7 @@ module URI
29
29
  gem_name: String,
30
30
  gem_version: T.nilable(String),
31
31
  path: String,
32
- line_number: T.nilable(String)
32
+ line_number: T.nilable(String),
33
33
  ).returns(URI::Source)
34
34
  end
35
35
  def build(gem_name:, gem_version:, path:, line_number:)
@@ -109,7 +109,7 @@ module Tapioca
109
109
  def pipeline
110
110
  @pipeline ||= Tapioca::Dsl::Pipeline.new(
111
111
  requested_constants: [],
112
- requested_compilers: activated_compiler_classes
112
+ requested_compilers: activated_compiler_classes,
113
113
  )
114
114
  end
115
115
  end
@@ -9,9 +9,9 @@ module Tapioca
9
9
  class << self
10
10
  extend T::Sig
11
11
 
12
- sig { params(tapioca_path: String, eager_load: T::Boolean).void }
13
- def load_application(tapioca_path:, eager_load: true)
14
- loader = new(tapioca_path: tapioca_path)
12
+ sig { params(tapioca_path: String, eager_load: T::Boolean, app_root: String).void }
13
+ def load_application(tapioca_path:, eager_load: true, app_root: ".")
14
+ loader = new(tapioca_path: tapioca_path, app_root: app_root)
15
15
  loader.load
16
16
  end
17
17
  end
@@ -26,12 +26,13 @@ module Tapioca
26
26
 
27
27
  protected
28
28
 
29
- sig { params(tapioca_path: String, eager_load: T::Boolean).void }
30
- def initialize(tapioca_path:, eager_load: true)
29
+ sig { params(tapioca_path: String, eager_load: T::Boolean, app_root: String).void }
30
+ def initialize(tapioca_path:, eager_load: true, app_root: ".")
31
31
  super()
32
32
 
33
33
  @tapioca_path = tapioca_path
34
34
  @eager_load = eager_load
35
+ @app_root = app_root
35
36
  end
36
37
 
37
38
  sig { void }
@@ -63,7 +64,8 @@ module Tapioca
63
64
 
64
65
  load_rails_application(
65
66
  environment_load: true,
66
- eager_load: @eager_load
67
+ eager_load: @eager_load,
68
+ app_root: @app_root,
67
69
  )
68
70
 
69
71
  say("Done", :green)
@@ -71,7 +73,7 @@ module Tapioca
71
73
 
72
74
  sig { void }
73
75
  def abort_if_pending_migrations!
74
- return unless File.exist?("config/application.rb")
76
+ return unless File.exist?("#{@app_root}/config/application.rb")
75
77
  return unless defined?(::Rake)
76
78
 
77
79
  Rails.application.load_tasks
@@ -14,7 +14,7 @@ module Tapioca
14
14
  bundle: Gemfile,
15
15
  prerequire: T.nilable(String),
16
16
  postrequire: String,
17
- default_command: String
17
+ default_command: String,
18
18
  ).void
19
19
  end
20
20
  def load_application(bundle:, prerequire:, postrequire:, default_command:)
@@ -38,7 +38,7 @@ module Tapioca
38
38
  bundle: Gemfile,
39
39
  prerequire: T.nilable(String),
40
40
  postrequire: String,
41
- default_command: String
41
+ default_command: String,
42
42
  ).void
43
43
  end
44
44
  def initialize(bundle:, prerequire:, postrequire:, default_command:)
@@ -33,16 +33,16 @@ module Tapioca
33
33
  load_rails_engines
34
34
  end
35
35
 
36
- sig { params(environment_load: T::Boolean, eager_load: T::Boolean).void }
37
- def load_rails_application(environment_load: false, eager_load: false)
38
- return unless File.exist?("config/application.rb")
36
+ sig { params(environment_load: T::Boolean, eager_load: T::Boolean, app_root: String).void }
37
+ def load_rails_application(environment_load: false, eager_load: false, app_root: ".")
38
+ return unless File.exist?("#{app_root}/config/application.rb")
39
39
 
40
40
  silence_deprecations
41
41
 
42
42
  if environment_load
43
- require "./config/environment"
43
+ require "./#{app_root}/config/environment"
44
44
  else
45
- require "./config/application"
45
+ require "./#{app_root}/config/application"
46
46
  end
47
47
 
48
48
  eager_load_rails_app if eager_load
@@ -113,7 +113,7 @@ module Tapioca
113
113
  if Object.const_defined?("ActiveSupport")
114
114
  Object.const_get("ActiveSupport").run_load_hooks(
115
115
  :before_eager_load,
116
- application
116
+ application,
117
117
  )
118
118
  end
119
119
 
@@ -32,7 +32,7 @@ module RBI
32
32
  params(
33
33
  name: String,
34
34
  superclass_name: T.nilable(String),
35
- block: T.nilable(T.proc.params(scope: RBI::Scope).void)
35
+ block: T.nilable(T.proc.params(scope: RBI::Scope).void),
36
36
  ).returns(Scope)
37
37
  end
38
38
  def create_class(name, superclass_name: nil, &block)
@@ -68,7 +68,7 @@ module RBI
68
68
  variance: Symbol,
69
69
  fixed: T.nilable(String),
70
70
  upper: T.nilable(String),
71
- lower: T.nilable(String)
71
+ lower: T.nilable(String),
72
72
  ).void
73
73
  end
74
74
  def create_type_variable(name, type:, variance: :invariant, fixed: nil, upper: nil, lower: nil)
@@ -82,14 +82,17 @@ module RBI
82
82
  parameters: T::Array[TypedParam],
83
83
  return_type: String,
84
84
  class_method: T::Boolean,
85
- visibility: RBI::Visibility
85
+ visibility: RBI::Visibility,
86
+ comments: T::Array[RBI::Comment],
86
87
  ).void
87
88
  end
88
- def create_method(name, parameters: [], return_type: "T.untyped", class_method: false, visibility: RBI::Public.new)
89
+ def create_method(name, parameters: [], return_type: "T.untyped", class_method: false, visibility: RBI::Public.new,
90
+ comments: [])
89
91
  return unless Tapioca::RBIHelper.valid_method_name?(name)
90
92
 
91
93
  sig = RBI::Sig.new(return_type: return_type)
92
- method = RBI::Method.new(name, sigs: [sig], is_singleton: class_method, visibility: visibility)
94
+ method = RBI::Method.new(name, sigs: [sig], is_singleton: class_method, visibility: visibility,
95
+ comments: comments)
93
96
  parameters.each do |param|
94
97
  method << param.param
95
98
  sig << RBI::SigParam.new(param.param.name, param.type)
@@ -9,7 +9,7 @@ module Tapioca
9
9
  params(
10
10
  file: RBI::File,
11
11
  command: String,
12
- reason: T.nilable(String)
12
+ reason: T.nilable(String),
13
13
  ).void
14
14
  end
15
15
  def write_header!(file, command, reason: nil)
@@ -32,6 +32,6 @@ module Tapioca
32
32
  max_line_length: nil,
33
33
  nest_singleton_methods: true,
34
34
  nest_non_public_methods: true,
35
- sort_nodes: true
35
+ sort_nodes: true,
36
36
  ), RBIFormatter)
37
37
  end
@@ -23,14 +23,30 @@ module Tapioca
23
23
  module GenericTypeRegistry
24
24
  @generic_instances = T.let(
25
25
  {},
26
- T::Hash[String, Module]
26
+ T::Hash[String, Module],
27
27
  )
28
28
 
29
29
  @type_variables = T.let(
30
30
  {}.compare_by_identity,
31
- T::Hash[Module, T::Array[TypeVariableModule]]
31
+ T::Hash[Module, T::Array[TypeVariableModule]],
32
32
  )
33
33
 
34
+ class GenericType < T::Types::Simple
35
+ extend T::Sig
36
+
37
+ sig { params(raw_type: Module, underlying_type: Module).void }
38
+ def initialize(raw_type, underlying_type)
39
+ super(raw_type)
40
+
41
+ @underlying_type = T.let(underlying_type, Module)
42
+ end
43
+
44
+ sig { params(obj: T.untyped).returns(T::Boolean) }
45
+ def valid?(obj)
46
+ obj.is_a?(@underlying_type)
47
+ end
48
+ end
49
+
34
50
  class << self
35
51
  extend T::Sig
36
52
 
@@ -116,6 +132,10 @@ module Tapioca
116
132
  generic_type.define_singleton_method(:name, name_proc)
117
133
  generic_type.define_singleton_method(:to_s, name_proc)
118
134
 
135
+ override_type = GenericType.new(generic_type, constant)
136
+ override_type_proc = -> { override_type }
137
+ generic_type.define_singleton_method(:__tapioca_override_type, override_type_proc)
138
+
119
139
  # We need to define a `<=` method on the cloned constant, so that Sorbet
120
140
  # can do covariance/contravariance checks on the type variables.
121
141
  #