tapioca 0.10.4 → 0.11.0
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.
- checksums.yaml +4 -4
- data/lib/tapioca/cli.rb +14 -5
- data/lib/tapioca/commands/annotations.rb +2 -0
- data/lib/tapioca/commands/configure.rb +1 -0
- data/lib/tapioca/commands/dsl.rb +17 -3
- data/lib/tapioca/commands/gem.rb +4 -2
- data/lib/tapioca/dsl/compilers/aasm.rb +78 -17
- data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_columns.rb +3 -3
- data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +8 -5
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +140 -83
- data/lib/tapioca/dsl/compilers/active_record_scope.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_secure_token.rb +74 -0
- data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +14 -11
- data/lib/tapioca/dsl/compilers/active_resource.rb +22 -15
- data/lib/tapioca/dsl/compilers/active_storage.rb +4 -2
- data/lib/tapioca/dsl/compilers/graphql_input_object.rb +21 -1
- data/lib/tapioca/dsl/compilers/kredis.rb +130 -0
- data/lib/tapioca/dsl/compilers/smart_properties.rb +7 -4
- data/lib/tapioca/dsl/compilers/url_helpers.rb +7 -4
- data/lib/tapioca/dsl/extensions/active_record.rb +9 -0
- data/lib/tapioca/dsl/extensions/kredis.rb +114 -0
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +37 -27
- data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +1 -0
- data/lib/tapioca/dsl/pipeline.rb +12 -5
- data/lib/tapioca/gem/listeners/sorbet_enums.rb +1 -1
- data/lib/tapioca/gem/listeners/yard_doc.rb +13 -10
- data/lib/tapioca/gem/pipeline.rb +14 -0
- data/lib/tapioca/gemfile.rb +6 -2
- data/lib/tapioca/helpers/rbi_files_helper.rb +12 -6
- data/lib/tapioca/helpers/sorbet_helper.rb +7 -4
- data/lib/tapioca/helpers/source_uri.rb +10 -7
- data/lib/tapioca/loaders/gem.rb +4 -2
- data/lib/tapioca/loaders/loader.rb +99 -35
- data/lib/tapioca/rbi_ext/model.rb +8 -3
- data/lib/tapioca/rbi_formatter.rb +11 -8
- data/lib/tapioca/runtime/attached_class_of_32.rb +20 -0
- data/lib/tapioca/runtime/attached_class_of_legacy.rb +27 -0
- data/lib/tapioca/runtime/reflection.rb +11 -10
- data/lib/tapioca/runtime/trackers.rb +17 -0
- data/lib/tapioca/static/symbol_loader.rb +14 -14
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +8 -5
- metadata +7 -2
@@ -47,44 +47,113 @@ module Tapioca
|
|
47
47
|
|
48
48
|
eager_load_rails_app if eager_load
|
49
49
|
rescue LoadError, StandardError => e
|
50
|
-
say(
|
51
|
-
"
|
52
|
-
|
50
|
+
say(
|
51
|
+
"Tapioca attempted to load the Rails application after encountering a `config/application.rb` file, " \
|
52
|
+
"but it failed. If your application uses Rails please ensure it can be loaded correctly before " \
|
53
|
+
"generating RBIs.\n#{e}",
|
54
|
+
:yellow,
|
55
|
+
)
|
53
56
|
say("Continuing RBI generation without loading the Rails application.")
|
54
57
|
end
|
55
58
|
|
56
59
|
sig { void }
|
57
60
|
def load_rails_engines
|
58
|
-
|
59
|
-
errored_files = []
|
61
|
+
return if engines.empty?
|
60
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|
|
61
114
|
engine.config.eager_load_paths.each do |load_path|
|
62
115
|
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
|
63
|
-
|
64
|
-
rescue LoadError, StandardError
|
65
|
-
errored_files << file
|
116
|
+
require_dependency file
|
66
117
|
end
|
67
|
-
|
68
|
-
|
69
|
-
# Try files that have errored one more time
|
70
|
-
# It might have been a load order problem
|
71
|
-
errored_files.each do |file|
|
72
|
-
require(file)
|
73
|
-
rescue LoadError, StandardError
|
118
|
+
rescue ScriptError, StandardError
|
74
119
|
nil
|
75
120
|
end
|
76
121
|
end
|
77
122
|
end
|
78
123
|
|
79
|
-
sig { returns(T::
|
80
|
-
def
|
81
|
-
|
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)
|
82
151
|
|
83
152
|
safe_require("active_support/core_ext/class/subclasses")
|
84
153
|
|
85
154
|
project_path = Bundler.default_gemfile.parent.expand_path
|
86
155
|
# We can use `Class#descendants` here, since we know Rails is loaded
|
87
|
-
|
156
|
+
Rails::Engine
|
88
157
|
.descendants
|
89
158
|
.reject(&:abstract_railtie?)
|
90
159
|
.reject { |engine| gem_in_app_dir?(project_path, engine.config.root.to_path) }
|
@@ -100,30 +169,25 @@ module Tapioca
|
|
100
169
|
sig { void }
|
101
170
|
def silence_deprecations
|
102
171
|
# Stop any ActiveSupport Deprecations from being reported
|
103
|
-
|
104
|
-
|
105
|
-
|
172
|
+
if defined?(ActiveSupport::Deprecation)
|
173
|
+
ActiveSupport::Deprecation.silenced = true
|
174
|
+
end
|
106
175
|
end
|
107
176
|
|
108
177
|
sig { void }
|
109
178
|
def eager_load_rails_app
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
Object.const_get("ActiveSupport").run_load_hooks(
|
115
|
-
:before_eager_load,
|
116
|
-
application,
|
117
|
-
)
|
179
|
+
application = Rails.application
|
180
|
+
|
181
|
+
if defined?(ActiveSupport)
|
182
|
+
ActiveSupport.run_load_hooks(:before_eager_load, application)
|
118
183
|
end
|
119
184
|
|
120
|
-
if
|
121
|
-
|
122
|
-
zeitwerk_loader.eager_load_all
|
185
|
+
if defined?(Zeitwerk::Loader)
|
186
|
+
Zeitwerk::Loader.eager_load_all
|
123
187
|
end
|
124
188
|
|
125
|
-
if
|
126
|
-
|
189
|
+
if Rails.respond_to?(:autoloaders)
|
190
|
+
Rails.autoloaders.each(&:eager_load)
|
127
191
|
end
|
128
192
|
|
129
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)).
|
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
|
@@ -91,8 +91,13 @@ module RBI
|
|
91
91
|
return unless Tapioca::RBIHelper.valid_method_name?(name)
|
92
92
|
|
93
93
|
sig = RBI::Sig.new(return_type: return_type)
|
94
|
-
method = RBI::Method.new(
|
95
|
-
|
94
|
+
method = RBI::Method.new(
|
95
|
+
name,
|
96
|
+
sigs: [sig],
|
97
|
+
is_singleton: class_method,
|
98
|
+
visibility: visibility,
|
99
|
+
comments: comments,
|
100
|
+
)
|
96
101
|
parameters.each do |param|
|
97
102
|
method << param.param
|
98
103
|
sig << RBI::SigParam.new(param.param.name, param.type)
|
@@ -26,12 +26,15 @@ module Tapioca
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
DEFAULT_RBI_FORMATTER = T.let(
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
DEFAULT_RBI_FORMATTER = T.let(
|
30
|
+
RBIFormatter.new(
|
31
|
+
add_sig_templates: false,
|
32
|
+
group_nodes: true,
|
33
|
+
max_line_length: nil,
|
34
|
+
nest_singleton_methods: true,
|
35
|
+
nest_non_public_methods: true,
|
36
|
+
sort_nodes: true,
|
37
|
+
),
|
38
|
+
RBIFormatter,
|
39
|
+
)
|
37
40
|
end
|
@@ -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|
|
@@ -13,6 +13,23 @@ module Tapioca
|
|
13
13
|
class << self
|
14
14
|
extend T::Sig
|
15
15
|
|
16
|
+
sig do
|
17
|
+
type_parameters(:Return)
|
18
|
+
.params(blk: T.proc.returns(T.type_parameter(:Return)))
|
19
|
+
.returns(T.type_parameter(:Return))
|
20
|
+
end
|
21
|
+
def with_trackers_enabled(&blk)
|
22
|
+
# Currently this is a dirty hack to ensure disabling trackers
|
23
|
+
# doesn't work while in the block passed to this method.
|
24
|
+
disable_all_method = method(:disable_all!)
|
25
|
+
define_singleton_method(:disable_all!) {}
|
26
|
+
blk.call
|
27
|
+
ensure
|
28
|
+
if disable_all_method
|
29
|
+
define_singleton_method(:disable_all!, disable_all_method)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
16
33
|
sig { void }
|
17
34
|
def disable_all!
|
18
35
|
@trackers.each(&:disable!)
|
@@ -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
|
data/lib/tapioca/version.rb
CHANGED
data/lib/tapioca.rb
CHANGED
@@ -43,11 +43,14 @@ module Tapioca
|
|
43
43
|
DEFAULT_TODO_FILE = T.let("#{DEFAULT_RBI_DIR}/todo.rbi", String)
|
44
44
|
DEFAULT_ANNOTATIONS_DIR = T.let("#{DEFAULT_RBI_DIR}/annotations", String)
|
45
45
|
|
46
|
-
DEFAULT_OVERRIDES = T.let(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
DEFAULT_OVERRIDES = T.let(
|
47
|
+
{
|
48
|
+
# ActiveSupport overrides some core methods with different signatures
|
49
|
+
# so we generate a typed: false RBI for it to suppress errors
|
50
|
+
"activesupport" => "false",
|
51
|
+
}.freeze,
|
52
|
+
T::Hash[String, String],
|
53
|
+
)
|
51
54
|
|
52
55
|
DEFAULT_RBI_MAX_LINE_LENGTH = 120
|
53
56
|
DEFAULT_ENVIRONMENT = "development"
|
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.
|
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:
|
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
|