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