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
@@ -45,7 +45,11 @@ module Tapioca
|
|
45
45
|
|
46
46
|
sig { override.void }
|
47
47
|
def decorate
|
48
|
-
|
48
|
+
# Skip methods explicitly defined in code
|
49
|
+
arguments = constant.all_argument_definitions.select do |argument|
|
50
|
+
method_defined_by_graphql?(argument.keyword.to_s)
|
51
|
+
end
|
52
|
+
|
49
53
|
return if arguments.empty?
|
50
54
|
|
51
55
|
root.create_path(constant) do |input_object|
|
@@ -56,6 +60,22 @@ module Tapioca
|
|
56
60
|
end
|
57
61
|
end
|
58
62
|
|
63
|
+
private
|
64
|
+
|
65
|
+
sig { returns(T.nilable(String)) }
|
66
|
+
def graphql_input_object_argument_source_file
|
67
|
+
@graphql_input_object_argument_source_file ||= T.let(
|
68
|
+
GraphQL::Schema::InputObject.method(:argument).source_location&.first,
|
69
|
+
T.nilable(String),
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
sig { params(method_name: String).returns(T::Boolean) }
|
74
|
+
def method_defined_by_graphql?(method_name)
|
75
|
+
method_file = constant.instance_method(method_name).source_location&.first
|
76
|
+
!!(method_file && graphql_input_object_argument_source_file == method_file)
|
77
|
+
end
|
78
|
+
|
59
79
|
class << self
|
60
80
|
extend T::Sig
|
61
81
|
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# typed: strict
|
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
|
+
# `Tapioca::Dsl::Compilers::Kredis` decorates RBI files for all
|
14
|
+
# classes that include [`Kredis::Attributes`](https://github.com/rails/kredis/blob/main/lib/kredis/attributes.rb).
|
15
|
+
#
|
16
|
+
# For example, with the following class:
|
17
|
+
#
|
18
|
+
# ~~~rb
|
19
|
+
# class Person < ApplicationRecord
|
20
|
+
# kredis_list :names
|
21
|
+
# kredis_flag :awesome
|
22
|
+
# kredis_counter :steps, expires_in: 1.hour
|
23
|
+
# kredis_enum :morning, values: %w[ bright blue black ], default: "bright"
|
24
|
+
# end
|
25
|
+
# ~~~
|
26
|
+
#
|
27
|
+
# this compiler will produce an RBI file with the following content:
|
28
|
+
# ~~~rbi
|
29
|
+
# # typed: true
|
30
|
+
#
|
31
|
+
# class Person
|
32
|
+
# module GeneratedKredisAttributeMethods
|
33
|
+
# sig { returns(Kredis::Types::Flag) }
|
34
|
+
# def awesome; end
|
35
|
+
#
|
36
|
+
# sig { returns(T::Boolean) }
|
37
|
+
# def awesome?; end
|
38
|
+
#
|
39
|
+
# sig { returns(PrivateEnumMorning) }
|
40
|
+
# def morning; end
|
41
|
+
#
|
42
|
+
# sig { returns(Kredis::Types::List) }
|
43
|
+
# def names; end
|
44
|
+
#
|
45
|
+
# sig { returns(Kredis::Types::Counter) }
|
46
|
+
# def steps; end
|
47
|
+
#
|
48
|
+
# class PrivateEnumMorning < Kredis::Types::Enum
|
49
|
+
# sig { void }
|
50
|
+
# def black!; end
|
51
|
+
#
|
52
|
+
# sig { returns(T::Boolean) }
|
53
|
+
# def black?; end
|
54
|
+
#
|
55
|
+
# sig { void }
|
56
|
+
# def blue!; end
|
57
|
+
#
|
58
|
+
# sig { returns(T::Boolean) }
|
59
|
+
# def blue?; end
|
60
|
+
#
|
61
|
+
# sig { void }
|
62
|
+
# def bright!; end
|
63
|
+
#
|
64
|
+
# sig { returns(T::Boolean) }
|
65
|
+
# def bright?; end
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
# ~~~
|
70
|
+
class Kredis < Compiler
|
71
|
+
extend T::Sig
|
72
|
+
|
73
|
+
ConstantType = type_member { { fixed: T.all(Class, ::Kredis::Attributes::ClassMethods, Extensions::Kredis) } }
|
74
|
+
|
75
|
+
sig { override.void }
|
76
|
+
def decorate
|
77
|
+
return if constant.__tapioca_kredis_types.nil?
|
78
|
+
|
79
|
+
module_name = "GeneratedKredisAttributeMethods"
|
80
|
+
|
81
|
+
root.create_path(constant) do |model|
|
82
|
+
model.create_module(module_name) do |mod|
|
83
|
+
constant.__tapioca_kredis_types.each do |method, data|
|
84
|
+
generate_methods(mod, method, data)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
model.create_include(module_name)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class << self
|
92
|
+
extend T::Sig
|
93
|
+
|
94
|
+
sig { override.returns(T::Enumerable[Module]) }
|
95
|
+
def gather_constants
|
96
|
+
all_classes
|
97
|
+
.grep(::Kredis::Attributes::ClassMethods)
|
98
|
+
.reject { |klass| klass.to_s == "ActiveRecord::Base" || klass.try(:abstract_class?) }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
sig { params(mod: RBI::Scope, method: String, data: T::Hash[Symbol, T.untyped]).void }
|
105
|
+
def generate_methods(mod, method, data)
|
106
|
+
return_type = data.fetch(:type)
|
107
|
+
case return_type
|
108
|
+
when "Kredis::Types::Enum"
|
109
|
+
klass_name = "PrivateEnum#{method.split("_").map(&:capitalize).join}"
|
110
|
+
create_enum_class(mod, klass_name, data.fetch(:values))
|
111
|
+
return_type = klass_name
|
112
|
+
when "Kredis::Types::Flag"
|
113
|
+
mod.create_method("#{method}?", return_type: "T::Boolean")
|
114
|
+
end
|
115
|
+
|
116
|
+
mod.create_method(method, return_type: return_type)
|
117
|
+
end
|
118
|
+
|
119
|
+
sig { params(mod: RBI::Scope, klass_name: String, values: T::Array[T.untyped]).void }
|
120
|
+
def create_enum_class(mod, klass_name, values)
|
121
|
+
klass = mod.create_class(klass_name, superclass_name: "Kredis::Types::Enum")
|
122
|
+
values.each do |value|
|
123
|
+
klass.create_method("#{value}!", return_type: "void")
|
124
|
+
klass.create_method("#{value}?", return_type: "T::Boolean")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -119,10 +119,13 @@ module Tapioca
|
|
119
119
|
mod.create_method(property.reader.to_s, return_type: type)
|
120
120
|
end
|
121
121
|
|
122
|
-
BOOLEANS = T.let(
|
123
|
-
[
|
124
|
-
|
125
|
-
|
122
|
+
BOOLEANS = T.let(
|
123
|
+
[
|
124
|
+
[true, false],
|
125
|
+
[false, true],
|
126
|
+
].freeze,
|
127
|
+
T::Array[[T::Boolean, T::Boolean]],
|
128
|
+
)
|
126
129
|
|
127
130
|
sig { params(property: ::SmartProperties::Property).returns(String) }
|
128
131
|
def type_for(property)
|
@@ -102,10 +102,13 @@ module Tapioca
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
NON_DISCOVERABLE_INCLUDERS = T.let(
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
NON_DISCOVERABLE_INCLUDERS = T.let(
|
106
|
+
[
|
107
|
+
ActionDispatch::IntegrationTest,
|
108
|
+
ActionView::Helpers,
|
109
|
+
],
|
110
|
+
T::Array[Module],
|
111
|
+
)
|
109
112
|
|
110
113
|
class << self
|
111
114
|
extend T::Sig
|
@@ -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
|
@@ -19,33 +19,7 @@ module Tapioca
|
|
19
19
|
|
20
20
|
column_type = @constant.attribute_types[column_name]
|
21
21
|
|
22
|
-
getter_type =
|
23
|
-
case column_type
|
24
|
-
when defined?(MoneyColumn) && MoneyColumn::ActiveRecordType
|
25
|
-
"::Money"
|
26
|
-
when ActiveRecord::Type::Integer
|
27
|
-
"::Integer"
|
28
|
-
when ActiveRecord::Type::String
|
29
|
-
"::String"
|
30
|
-
when ActiveRecord::Type::Date
|
31
|
-
"::Date"
|
32
|
-
when ActiveRecord::Type::Decimal
|
33
|
-
"::BigDecimal"
|
34
|
-
when ActiveRecord::Type::Float
|
35
|
-
"::Float"
|
36
|
-
when ActiveRecord::Type::Boolean
|
37
|
-
"T::Boolean"
|
38
|
-
when ActiveRecord::Type::DateTime, ActiveRecord::Type::Time
|
39
|
-
"::Time"
|
40
|
-
when ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter
|
41
|
-
"::ActiveSupport::TimeWithZone"
|
42
|
-
when ActiveRecord::Enum::EnumType
|
43
|
-
"::String"
|
44
|
-
when ActiveRecord::Type::Serialized
|
45
|
-
serialized_column_type(column_type)
|
46
|
-
else
|
47
|
-
handle_unknown_type(column_type)
|
48
|
-
end
|
22
|
+
getter_type = type_for_activerecord_value(column_type)
|
49
23
|
|
50
24
|
column = @constant.columns_hash[column_name]
|
51
25
|
setter_type =
|
@@ -71,6 +45,42 @@ module Tapioca
|
|
71
45
|
|
72
46
|
private
|
73
47
|
|
48
|
+
sig { params(column_type: T.untyped).returns(String) }
|
49
|
+
def type_for_activerecord_value(column_type)
|
50
|
+
case column_type
|
51
|
+
when defined?(MoneyColumn) && MoneyColumn::ActiveRecordType
|
52
|
+
"::Money"
|
53
|
+
when ActiveRecord::Type::Integer
|
54
|
+
"::Integer"
|
55
|
+
when ActiveRecord::Type::String
|
56
|
+
"::String"
|
57
|
+
when ActiveRecord::Type::Date
|
58
|
+
"::Date"
|
59
|
+
when ActiveRecord::Type::Decimal
|
60
|
+
"::BigDecimal"
|
61
|
+
when ActiveRecord::Type::Float
|
62
|
+
"::Float"
|
63
|
+
when ActiveRecord::Type::Boolean
|
64
|
+
"T::Boolean"
|
65
|
+
when ActiveRecord::Type::DateTime, ActiveRecord::Type::Time
|
66
|
+
"::Time"
|
67
|
+
when ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter
|
68
|
+
"::ActiveSupport::TimeWithZone"
|
69
|
+
when ActiveRecord::Enum::EnumType
|
70
|
+
"::String"
|
71
|
+
when ActiveRecord::Type::Serialized
|
72
|
+
serialized_column_type(column_type)
|
73
|
+
when defined?(ActiveRecord::ConnectionAdapters::PostgreSQL) &&
|
74
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Hstore
|
75
|
+
"T::Hash[::String, ::String]"
|
76
|
+
when defined?(ActiveRecord::ConnectionAdapters::PostgreSQL) &&
|
77
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array
|
78
|
+
"T::Array[#{type_for_activerecord_value(column_type.subtype)}]"
|
79
|
+
else
|
80
|
+
handle_unknown_type(column_type)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
74
84
|
sig { params(constant: Module).returns(T::Boolean) }
|
75
85
|
def do_not_generate_strong_types?(constant)
|
76
86
|
Object.const_defined?(:StrongTypeGeneration) &&
|
@@ -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
|
@@ -7,16 +7,19 @@ module Tapioca
|
|
7
7
|
class YardDoc < Base
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
IGNORED_COMMENTS = T.let(
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
IGNORED_COMMENTS = T.let(
|
11
|
+
[
|
12
|
+
":doc:",
|
13
|
+
":nodoc:",
|
14
|
+
"typed:",
|
15
|
+
"frozen_string_literal:",
|
16
|
+
"encoding:",
|
17
|
+
"warn_indent:",
|
18
|
+
"shareable_constant_value:",
|
19
|
+
"rubocop:",
|
20
|
+
],
|
21
|
+
T::Array[String],
|
22
|
+
)
|
20
23
|
|
21
24
|
IGNORED_SIG_TAGS = T.let(["param", "return"], T::Array[String])
|
22
25
|
|
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
|
|
data/lib/tapioca/gemfile.rb
CHANGED
@@ -154,8 +154,12 @@ module Tapioca
|
|
154
154
|
|
155
155
|
IGNORED_GEMS = T.let(
|
156
156
|
[
|
157
|
-
"sorbet",
|
158
|
-
"
|
157
|
+
"sorbet",
|
158
|
+
"sorbet-static",
|
159
|
+
"sorbet-runtime",
|
160
|
+
"sorbet-static-and-runtime",
|
161
|
+
"debug",
|
162
|
+
"fakefs",
|
159
163
|
].freeze,
|
160
164
|
T::Array[String],
|
161
165
|
)
|
@@ -212,16 +212,22 @@ module Tapioca
|
|
212
212
|
|
213
213
|
sig { params(nodes: T::Array[RBI::Node]).returns(T::Array[T.any(RBI::Method, RBI::Attr)]) }
|
214
214
|
def extract_methods_and_attrs(nodes)
|
215
|
-
T.cast(
|
216
|
-
|
217
|
-
|
215
|
+
T.cast(
|
216
|
+
nodes.select do |node|
|
217
|
+
node.is_a?(RBI::Method) || node.is_a?(RBI::Attr)
|
218
|
+
end,
|
219
|
+
T::Array[T.any(RBI::Method, RBI::Attr)],
|
220
|
+
)
|
218
221
|
end
|
219
222
|
|
220
223
|
sig { params(nodes: T::Array[RBI::Node]).returns(T::Array[T.any(RBI::Mixin, RBI::RequiresAncestor)]) }
|
221
224
|
def extract_mixins(nodes)
|
222
|
-
T.cast(
|
223
|
-
|
224
|
-
|
225
|
+
T.cast(
|
226
|
+
nodes.select do |node|
|
227
|
+
node.is_a?(RBI::Mixin) || node.is_a?(RBI::RequiresAncestor)
|
228
|
+
end,
|
229
|
+
T::Array[T.all(RBI::Mixin, RBI::RequiresAncestor)],
|
230
|
+
)
|
225
231
|
end
|
226
232
|
|
227
233
|
sig { params(nodes: T::Array[T.any(RBI::Method, RBI::Attr)]).returns(T::Array[T.any(RBI::Method, RBI::Attr)]) }
|
@@ -19,10 +19,13 @@ module Tapioca
|
|
19
19
|
|
20
20
|
SORBET_PAYLOAD_URL = "https://github.com/sorbet/sorbet/tree/master/rbi"
|
21
21
|
|
22
|
-
FEATURE_REQUIREMENTS = T.let(
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
FEATURE_REQUIREMENTS = T.let(
|
23
|
+
{
|
24
|
+
# feature_name: ::Gem::Requirement.new(">= ___"), # https://github.com/sorbet/sorbet/pull/___
|
25
|
+
non_generic_weak_map: ::Gem::Requirement.new(">= 0.5.10587"), # https://github.com/sorbet/sorbet/pull/6610
|
26
|
+
}.freeze,
|
27
|
+
T::Hash[Symbol, ::Gem::Requirement],
|
28
|
+
)
|
26
29
|
|
27
30
|
sig { params(sorbet_args: String).returns(Spoom::ExecResult) }
|
28
31
|
def sorbet(*sorbet_args)
|
@@ -7,13 +7,16 @@ module URI
|
|
7
7
|
class Source < URI::File
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
COMPONENT = T.let(
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
COMPONENT = T.let(
|
11
|
+
[
|
12
|
+
:scheme,
|
13
|
+
:gem_name,
|
14
|
+
:gem_version,
|
15
|
+
:path,
|
16
|
+
:line_number,
|
17
|
+
].freeze,
|
18
|
+
T::Array[Symbol],
|
19
|
+
)
|
17
20
|
|
18
21
|
alias_method(:gem_name, :host)
|
19
22
|
alias_method(:line_number, :fragment)
|
data/lib/tapioca/loaders/gem.rb
CHANGED
@@ -18,10 +18,12 @@ module Tapioca
|
|
18
18
|
).void
|
19
19
|
end
|
20
20
|
def load_application(bundle:, prerequire:, postrequire:, default_command:)
|
21
|
-
loader = new(
|
21
|
+
loader = new(
|
22
|
+
bundle: bundle,
|
22
23
|
prerequire: prerequire,
|
23
24
|
postrequire: postrequire,
|
24
|
-
default_command: default_command
|
25
|
+
default_command: default_command,
|
26
|
+
)
|
25
27
|
loader.load
|
26
28
|
end
|
27
29
|
end
|