tapioca 0.10.5 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,6 +21,15 @@ module Tapioca
21
21
  super
22
22
  end
23
23
 
24
+ attr_reader :__tapioca_secure_tokens
25
+
26
+ def has_secure_token(attribute = :token, length: ::ActiveRecord::SecureToken::MINIMUM_TOKEN_LENGTH)
27
+ @__tapioca_secure_tokens ||= []
28
+ @__tapioca_secure_tokens << attribute
29
+
30
+ super
31
+ end
32
+
24
33
  ::ActiveRecord::Base.singleton_class.prepend(self)
25
34
  end
26
35
  end
@@ -0,0 +1,114 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "kredis"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ module Tapioca
11
+ module Dsl
12
+ module Compilers
13
+ module Extensions
14
+ module Kredis
15
+ attr_reader :__tapioca_kredis_types
16
+
17
+ def kredis_proxy(name, key: nil, config: :shared, after_change: nil)
18
+ collect_kredis_type(name, "Kredis::Types::Proxy")
19
+ super
20
+ end
21
+
22
+ def kredis_string(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
23
+ collect_kredis_type(name, "Kredis::Types::Scalar")
24
+ super
25
+ end
26
+
27
+ def kredis_integer(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
28
+ collect_kredis_type(name, "Kredis::Types::Scalar")
29
+ super
30
+ end
31
+
32
+ def kredis_decimal(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
33
+ collect_kredis_type(name, "Kredis::Types::Scalar")
34
+ super
35
+ end
36
+
37
+ def kredis_datetime(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
38
+ collect_kredis_type(name, "Kredis::Types::Scalar")
39
+ super
40
+ end
41
+
42
+ def kredis_flag(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
43
+ collect_kredis_type(name, "Kredis::Types::Flag")
44
+ super
45
+ end
46
+
47
+ def kredis_float(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
48
+ collect_kredis_type(name, "Kredis::Types::Scalar")
49
+ super
50
+ end
51
+
52
+ def kredis_enum(name, key: nil, values:, default:, config: :shared, after_change: nil)
53
+ collect_kredis_type(name, "Kredis::Types::Enum", values: values)
54
+ super
55
+ end
56
+
57
+ def kredis_json(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
58
+ collect_kredis_type(name, "Kredis::Types::Scalar")
59
+ super
60
+ end
61
+
62
+ def kredis_list(name, key: nil, typed: :string, config: :shared, after_change: nil)
63
+ collect_kredis_type(name, "Kredis::Types::List")
64
+ super
65
+ end
66
+
67
+ def kredis_unique_list(name, limit: nil, key: nil, typed: :string, config: :shared, after_change: nil)
68
+ collect_kredis_type(name, "Kredis::Types::UniqueList")
69
+ super
70
+ end
71
+
72
+ def kredis_set(name, key: nil, typed: :string, config: :shared, after_change: nil)
73
+ collect_kredis_type(name, "Kredis::Types::Set")
74
+ super
75
+ end
76
+
77
+ def kredis_slot(name, key: nil, config: :shared, after_change: nil)
78
+ collect_kredis_type(name, "Kredis::Types::Slots")
79
+ super
80
+ end
81
+
82
+ def kredis_slots(name, available:, key: nil, config: :shared, after_change: nil)
83
+ collect_kredis_type(name, "Kredis::Types::Slots")
84
+ super
85
+ end
86
+
87
+ def kredis_counter(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
88
+ collect_kredis_type(name, "Kredis::Types::Counter")
89
+ super
90
+ end
91
+
92
+ def kredis_hash(name, key: nil, typed: :string, config: :shared, after_change: nil)
93
+ collect_kredis_type(name, "Kredis::Types::Hash")
94
+ super
95
+ end
96
+
97
+ def kredis_boolean(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
98
+ collect_kredis_type(name, "Kredis::Types::Scalar")
99
+ super
100
+ end
101
+
102
+ private
103
+
104
+ def collect_kredis_type(method, type, values: nil)
105
+ @__tapioca_kredis_types ||= {}
106
+ @__tapioca_kredis_types[method.to_s] = { type: type, values: values }
107
+ end
108
+
109
+ ::Kredis::Attributes::ClassMethods.prepend(self)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -14,6 +14,7 @@ module Tapioca
14
14
  AttributeMethodsModuleName = T.let("GeneratedAttributeMethods", String)
15
15
  AssociationMethodsModuleName = T.let("GeneratedAssociationMethods", String)
16
16
  DelegatedTypesModuleName = T.let("GeneratedDelegatedTypeMethods", String)
17
+ SecureTokensModuleName = T.let("GeneratedSecureTokenMethods", String)
17
18
 
18
19
  RelationMethodsModuleName = T.let("GeneratedRelationMethods", String)
19
20
  AssociationRelationMethodsModuleName = T.let("GeneratedAssociationRelationMethods", String)
@@ -12,6 +12,9 @@ module Tapioca
12
12
  sig { returns(T::Array[Module]) }
13
13
  attr_reader :requested_constants
14
14
 
15
+ sig { returns(T::Array[Pathname]) }
16
+ attr_reader :requested_paths
17
+
15
18
  sig { returns(T.proc.params(error: String).void) }
16
19
  attr_reader :error_handler
17
20
 
@@ -21,6 +24,7 @@ module Tapioca
21
24
  sig do
22
25
  params(
23
26
  requested_constants: T::Array[Module],
27
+ requested_paths: T::Array[Pathname],
24
28
  requested_compilers: T::Array[T.class_of(Compiler)],
25
29
  excluded_compilers: T::Array[T.class_of(Compiler)],
26
30
  error_handler: T.proc.params(error: String).void,
@@ -29,6 +33,7 @@ module Tapioca
29
33
  end
30
34
  def initialize(
31
35
  requested_constants:,
36
+ requested_paths: [],
32
37
  requested_compilers: [],
33
38
  excluded_compilers: [],
34
39
  error_handler: $stderr.method(:puts).to_proc,
@@ -39,6 +44,7 @@ module Tapioca
39
44
  T::Enumerable[T.class_of(Compiler)],
40
45
  )
41
46
  @requested_constants = requested_constants
47
+ @requested_paths = requested_paths
42
48
  @error_handler = error_handler
43
49
  @number_of_workers = number_of_workers
44
50
  @errors = T.let([], T::Array[String])
@@ -50,11 +56,12 @@ module Tapioca
50
56
  ).returns(T::Array[T.type_parameter(:T)])
51
57
  end
52
58
  def run(&blk)
53
- constants_to_process = gather_constants(requested_constants)
59
+ constants_to_process = gather_constants(requested_constants, requested_paths)
54
60
  .select { |c| Module === c } # Filter value constants out
55
61
  .sort_by! { |c| T.must(Runtime::Reflection.name_of(c)) }
56
62
 
57
- if constants_to_process.empty?
63
+ # It's OK if there are no constants to process if we received a valid file/path.
64
+ if constants_to_process.empty? && requested_paths.select { |p| File.exist?(p) }.empty?
58
65
  report_error(<<~ERROR)
59
66
  No classes/modules can be matched for RBI generation.
60
67
  Please check that the requested classes/modules include processable DSL methods.
@@ -115,12 +122,12 @@ module Tapioca
115
122
  active_compilers
116
123
  end
117
124
 
118
- sig { params(requested_constants: T::Array[Module]).returns(T::Set[Module]) }
119
- def gather_constants(requested_constants)
125
+ sig { params(requested_constants: T::Array[Module], requested_paths: T::Array[Pathname]).returns(T::Set[Module]) }
126
+ def gather_constants(requested_constants, requested_paths)
120
127
  constants = active_compilers.map(&:processable_constants).reduce(Set.new, :union)
121
128
  constants = filter_anonymous_and_reloaded_constants(constants)
122
129
 
123
- constants &= requested_constants unless requested_constants.empty?
130
+ constants &= requested_constants unless requested_constants.empty? && requested_paths.empty?
124
131
  constants
125
132
  end
126
133
 
@@ -12,7 +12,7 @@ module Tapioca
12
12
  sig { override.params(event: ScopeNodeAdded).void }
13
13
  def on_scope(event)
14
14
  constant = event.constant
15
- return unless T::Enum > event.constant
15
+ return unless T::Enum > event.constant # rubocop:disable Style/InvertibleUnlessCondition
16
16
 
17
17
  enums = T.unsafe(constant).values.map do |enum_type|
18
18
  enum_type.instance_variable_get(:@const_name).to_s
@@ -106,6 +106,18 @@ module Tapioca
106
106
  @payload_symbols.include?(symbol_name)
107
107
  end
108
108
 
109
+ sig { params(name: T.any(String, Symbol)).returns(T::Boolean) }
110
+ def constant_in_gem?(name)
111
+ return true unless Object.respond_to?(:const_source_location)
112
+
113
+ source_location, _ = Object.const_source_location(name)
114
+ return true unless source_location
115
+ # If the source location of the constant is "(eval)", all bets are off.
116
+ return true if source_location == "(eval)"
117
+
118
+ gem.contains_path?(source_location)
119
+ end
120
+
109
121
  sig { params(method: UnboundMethod).returns(T::Boolean) }
110
122
  def method_in_gem?(method)
111
123
  source_location = method.source_location&.first
@@ -216,6 +228,7 @@ module Tapioca
216
228
  mark_seen(name)
217
229
 
218
230
  return if symbol_in_payload?(name)
231
+ return unless constant_in_gem?(name)
219
232
 
220
233
  target = name_of(constant)
221
234
  # If target has no name, let's make it an anonymous class or module with `Class.new` or `Module.new`
@@ -237,6 +250,7 @@ module Tapioca
237
250
  mark_seen(name)
238
251
 
239
252
  return if symbol_in_payload?(name)
253
+ return unless constant_in_gem?(name)
240
254
 
241
255
  klass = class_of(value)
242
256
 
@@ -58,36 +58,102 @@ module Tapioca
58
58
 
59
59
  sig { void }
60
60
  def load_rails_engines
61
- rails_engines.each do |engine|
62
- errored_files = []
61
+ return if engines.empty?
63
62
 
63
+ with_rails_application do
64
+ run_initializers
65
+
66
+ if zeitwerk_mode?
67
+ load_engines_in_zeitwerk_mode
68
+ else
69
+ load_engines_in_classic_mode
70
+ end
71
+ end
72
+ end
73
+
74
+ def run_initializers
75
+ engines.each do |engine|
76
+ engine.instance.initializers.tsort_each do |initializer|
77
+ initializer.run(Rails.application)
78
+ rescue ScriptError, StandardError
79
+ nil
80
+ end
81
+ end
82
+ end
83
+
84
+ sig { void }
85
+ def load_engines_in_zeitwerk_mode
86
+ # Collect all the directories that are already managed by all existing Zeitwerk loaders.
87
+ managed_dirs = Zeitwerk::Registry.loaders.flat_map(&:dirs).to_set
88
+ # We use a fresh loader to load the engine directories, so that we don't interfere with
89
+ # any of the existing loaders.
90
+ autoloader = Zeitwerk::Loader.new
91
+
92
+ engines.each do |engine|
93
+ engine.config.eager_load_paths.each do |path|
94
+ # Zeitwerk only accepts existing directories in `push_dir`.
95
+ next unless File.directory?(path)
96
+ # We should not add directories that are already managed by a Zeitwerk loader.
97
+ next if managed_dirs.member?(path)
98
+
99
+ autoloader.push_dir(path)
100
+ end
101
+ end
102
+
103
+ autoloader.setup
104
+ end
105
+
106
+ sig { void }
107
+ def load_engines_in_classic_mode
108
+ # This is code adapted from `Rails::Engine#eager_load!` in
109
+ # https://github.com/rails/rails/blob/d9e188dbab81b412f73dfb7763318d52f360af49/railties/lib/rails/engine.rb#L489-L495
110
+ #
111
+ # We can't use `Rails::Engine#eager_load!` directly because it will raise as soon as it encounters
112
+ # an error, which is not what we want. We want to try to load as much as we can.
113
+ engines.each do |engine|
64
114
  engine.config.eager_load_paths.each do |load_path|
65
115
  Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
66
- require(file)
67
- rescue LoadError, StandardError
68
- errored_files << file
116
+ require_dependency file
69
117
  end
70
- end
71
-
72
- # Try files that have errored one more time
73
- # It might have been a load order problem
74
- errored_files.each do |file|
75
- require(file)
76
- rescue LoadError, StandardError
118
+ rescue ScriptError, StandardError
77
119
  nil
78
120
  end
79
121
  end
80
122
  end
81
123
 
82
- sig { returns(T::Array[T.untyped]) }
83
- def rails_engines
84
- return [] unless Object.const_defined?("Rails::Engine")
124
+ sig { returns(T::Boolean) }
125
+ def zeitwerk_mode?
126
+ Rails.respond_to?(:autoloaders) &&
127
+ Rails.autoloaders.respond_to?(:zeitwerk_enabled?) &&
128
+ Rails.autoloaders.zeitwerk_enabled?
129
+ end
130
+
131
+ sig { params(blk: T.proc.void).void }
132
+ def with_rails_application(&blk)
133
+ # Store the current Rails.application object so that we can restore it
134
+ rails_application = T.unsafe(Rails.application)
135
+
136
+ # Create a new Rails::Application object, so that we can load the engines.
137
+ # Some engines and the `Rails.autoloaders` call might expect `Rails.application`
138
+ # to be set, so we need to create one here.
139
+ unless rails_application
140
+ Rails.application = Class.new(Rails::Application)
141
+ end
142
+
143
+ blk.call
144
+ ensure
145
+ Rails.app_class = Rails.application = rails_application
146
+ end
147
+
148
+ T::Sig::WithoutRuntime.sig { returns(T::Array[T.class_of(Rails::Engine)]) }
149
+ def engines
150
+ return [] unless defined?(Rails::Engine)
85
151
 
86
152
  safe_require("active_support/core_ext/class/subclasses")
87
153
 
88
154
  project_path = Bundler.default_gemfile.parent.expand_path
89
155
  # We can use `Class#descendants` here, since we know Rails is loaded
90
- Object.const_get("Rails::Engine")
156
+ Rails::Engine
91
157
  .descendants
92
158
  .reject(&:abstract_railtie?)
93
159
  .reject { |engine| gem_in_app_dir?(project_path, engine.config.root.to_path) }
@@ -103,30 +169,25 @@ module Tapioca
103
169
  sig { void }
104
170
  def silence_deprecations
105
171
  # Stop any ActiveSupport Deprecations from being reported
106
- Object.const_get("ActiveSupport::Deprecation").silenced = true
107
- rescue NameError
108
- nil
172
+ if defined?(ActiveSupport::Deprecation)
173
+ ActiveSupport::Deprecation.silenced = true
174
+ end
109
175
  end
110
176
 
111
177
  sig { void }
112
178
  def eager_load_rails_app
113
- rails = Object.const_get("Rails")
114
- application = rails.application
115
-
116
- if Object.const_defined?("ActiveSupport")
117
- Object.const_get("ActiveSupport").run_load_hooks(
118
- :before_eager_load,
119
- application,
120
- )
179
+ application = Rails.application
180
+
181
+ if defined?(ActiveSupport)
182
+ ActiveSupport.run_load_hooks(:before_eager_load, application)
121
183
  end
122
184
 
123
- if Object.const_defined?("Zeitwerk::Loader")
124
- zeitwerk_loader = Object.const_get("Zeitwerk::Loader")
125
- zeitwerk_loader.eager_load_all
185
+ if defined?(Zeitwerk::Loader)
186
+ Zeitwerk::Loader.eager_load_all
126
187
  end
127
188
 
128
- if rails.respond_to?(:autoloaders) && rails.autoloaders.zeitwerk_enabled?
129
- rails.autoloaders.each(&:eager_load)
189
+ if Rails.respond_to?(:autoloaders)
190
+ Rails.autoloaders.each(&:eager_load)
130
191
  end
131
192
 
132
193
  if application.config.respond_to?(:eager_load_namespaces)
@@ -5,7 +5,7 @@ module RBI
5
5
  class Tree
6
6
  extend T::Sig
7
7
 
8
- sig { params(constant: ::Module, block: T.nilable(T.proc.params(scope: Scope).void)).void }
8
+ sig { params(constant: ::Module, block: T.nilable(T.proc.params(scope: Scope).void)).returns(Scope) }
9
9
  def create_path(constant, &block)
10
10
  constant_name = Tapioca::Runtime::Reflection.name_of(constant)
11
11
  raise "given constant does not have a name" unless constant_name
@@ -0,0 +1,20 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Tapioca
5
+ module Runtime
6
+ # This module should only be included when running Ruby version 3.2
7
+ # or newer. It relies on the Class#attached_object method, which was
8
+ # added in Ruby 3.2 and fetches the attached object of a singleton
9
+ # class without having to iterate through all of ObjectSpace.
10
+ module AttachedClassOf
11
+ extend T::Sig
12
+
13
+ sig { params(singleton_class: Class).returns(T.nilable(Module)) }
14
+ def attached_class_of(singleton_class)
15
+ result = singleton_class.attached_object
16
+ Module === result ? result : nil
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Tapioca
5
+ module Runtime
6
+ # This module should only be included when running versions of Ruby
7
+ # older than 3.2. Because the Class#attached_object method is not
8
+ # available, it implements finding the attached class of a singleton
9
+ # class by iterating through ObjectSpace.
10
+ module AttachedClassOf
11
+ extend T::Sig
12
+ extend T::Helpers
13
+
14
+ requires_ancestor { Tapioca::Runtime::Reflection }
15
+
16
+ sig { params(singleton_class: Class).returns(T.nilable(Module)) }
17
+ def attached_class_of(singleton_class)
18
+ # https://stackoverflow.com/a/36622320/98634
19
+ result = ObjectSpace.each_object(singleton_class).find do |klass|
20
+ singleton_class_of(T.cast(klass, Module)) == singleton_class
21
+ end
22
+
23
+ T.cast(result, T.nilable(Module))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,9 +1,20 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ # On Ruby 3.2 or newer, Class defines an attached_object method that returns the
5
+ # attached class of a singleton class without iterating ObjectSpace. On older
6
+ # versions of Ruby, we fall back to iterating ObjectSpace.
7
+ if Class.method_defined?(:attached_object)
8
+ require "tapioca/runtime/attached_class_of_32"
9
+ else
10
+ require "tapioca/runtime/attached_class_of_legacy"
11
+ end
12
+
4
13
  module Tapioca
5
14
  module Runtime
6
15
  module Reflection
16
+ include AttachedClassOf
17
+
7
18
  extend T::Sig
8
19
  extend self
9
20
 
@@ -174,16 +185,6 @@ module Tapioca
174
185
  resolved_loc.absolute_path || ""
175
186
  end
176
187
 
177
- sig { params(singleton_class: Module).returns(T.nilable(Module)) }
178
- def attached_class_of(singleton_class)
179
- # https://stackoverflow.com/a/36622320/98634
180
- result = ObjectSpace.each_object(singleton_class).find do |klass|
181
- singleton_class_of(T.cast(klass, Module)) == singleton_class
182
- end
183
-
184
- T.cast(result, Module)
185
- end
186
-
187
188
  sig { params(constant: Module).returns(T::Set[String]) }
188
189
  def file_candidates_for(constant)
189
190
  relevant_methods_for(constant).filter_map do |method|
@@ -41,6 +41,20 @@ module Tapioca
41
41
  symbols_from_paths(gem.files)
42
42
  end
43
43
 
44
+ sig { params(paths: T::Array[Pathname]).returns(T::Set[String]) }
45
+ def symbols_from_paths(paths)
46
+ output = Tempfile.create("sorbet") do |file|
47
+ file.write(Array(paths).join("\n"))
48
+ file.flush
49
+
50
+ symbol_table_json_from("@#{file.path.shellescape}")
51
+ end
52
+
53
+ return Set.new if output.empty?
54
+
55
+ SymbolTableParser.parse_json(output)
56
+ end
57
+
44
58
  private
45
59
 
46
60
  sig { returns(T::Array[T.class_of(Rails::Engine)]) }
@@ -59,20 +73,6 @@ module Tapioca
59
73
  def symbol_table_json_from(input, table_type: "symbol-table-json")
60
74
  sorbet("--no-config", "--quiet", "--print=#{table_type}", input).out
61
75
  end
62
-
63
- sig { params(paths: T::Array[Pathname]).returns(T::Set[String]) }
64
- def symbols_from_paths(paths)
65
- output = Tempfile.create("sorbet") do |file|
66
- file.write(Array(paths).join("\n"))
67
- file.flush
68
-
69
- symbol_table_json_from("@#{file.path.shellescape}")
70
- end
71
-
72
- return Set.new if output.empty?
73
-
74
- SymbolTableParser.parse_json(output)
75
- end
76
76
  end
77
77
  end
78
78
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.10.5"
5
+ VERSION = "0.11.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapioca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.5
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ufuk Kayserilioglu
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2023-01-04 00:00:00.000000000 Z
14
+ date: 2023-02-21 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -176,6 +176,7 @@ files:
176
176
  - lib/tapioca/dsl/compilers/active_record_fixtures.rb
177
177
  - lib/tapioca/dsl/compilers/active_record_relations.rb
178
178
  - lib/tapioca/dsl/compilers/active_record_scope.rb
179
+ - lib/tapioca/dsl/compilers/active_record_secure_token.rb
179
180
  - lib/tapioca/dsl/compilers/active_record_typed_store.rb
180
181
  - lib/tapioca/dsl/compilers/active_resource.rb
181
182
  - lib/tapioca/dsl/compilers/active_storage.rb
@@ -186,6 +187,7 @@ files:
186
187
  - lib/tapioca/dsl/compilers/graphql_input_object.rb
187
188
  - lib/tapioca/dsl/compilers/graphql_mutation.rb
188
189
  - lib/tapioca/dsl/compilers/identity_cache.rb
190
+ - lib/tapioca/dsl/compilers/kredis.rb
189
191
  - lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb
190
192
  - lib/tapioca/dsl/compilers/protobuf.rb
191
193
  - lib/tapioca/dsl/compilers/rails_generators.rb
@@ -195,6 +197,7 @@ files:
195
197
  - lib/tapioca/dsl/compilers/url_helpers.rb
196
198
  - lib/tapioca/dsl/extensions/active_record.rb
197
199
  - lib/tapioca/dsl/extensions/frozen_record.rb
200
+ - lib/tapioca/dsl/extensions/kredis.rb
198
201
  - lib/tapioca/dsl/helpers/active_record_column_type_helper.rb
199
202
  - lib/tapioca/dsl/helpers/active_record_constants_helper.rb
200
203
  - lib/tapioca/dsl/helpers/graphql_type_helper.rb
@@ -239,6 +242,8 @@ files:
239
242
  - lib/tapioca/rbi_ext/model.rb
240
243
  - lib/tapioca/rbi_formatter.rb
241
244
  - lib/tapioca/repo_index.rb
245
+ - lib/tapioca/runtime/attached_class_of_32.rb
246
+ - lib/tapioca/runtime/attached_class_of_legacy.rb
242
247
  - lib/tapioca/runtime/dynamic_mixin_compiler.rb
243
248
  - lib/tapioca/runtime/generic_type_registry.rb
244
249
  - lib/tapioca/runtime/reflection.rb