tapioca 0.10.1 → 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
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
  #