tapioca 0.10.5 → 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 +5 -1
- 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/dsl/compilers/aasm.rb +67 -15
- 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_relations.rb +117 -66
- 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/graphql_input_object.rb +21 -1
- data/lib/tapioca/dsl/compilers/kredis.rb +130 -0
- 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_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/pipeline.rb +14 -0
- data/lib/tapioca/loaders/loader.rb +93 -32
- data/lib/tapioca/rbi_ext/model.rb +1 -1
- 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/static/symbol_loader.rb +14 -14
- data/lib/tapioca/version.rb +1 -1
- metadata +7 -2
@@ -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)
|
data/lib/tapioca/dsl/pipeline.rb
CHANGED
@@ -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
|
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
|
data/lib/tapioca/gem/pipeline.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
67
|
-
rescue LoadError, StandardError
|
68
|
-
errored_files << file
|
116
|
+
require_dependency file
|
69
117
|
end
|
70
|
-
|
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::
|
83
|
-
def
|
84
|
-
|
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
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
124
|
-
|
125
|
-
zeitwerk_loader.eager_load_all
|
185
|
+
if defined?(Zeitwerk::Loader)
|
186
|
+
Zeitwerk::Loader.eager_load_all
|
126
187
|
end
|
127
188
|
|
128
|
-
if
|
129
|
-
|
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)).
|
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
|
data/lib/tapioca/version.rb
CHANGED
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: 2023-
|
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
|