tapioca 0.4.27 → 0.5.3
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/Gemfile +15 -15
- data/README.md +2 -2
- data/Rakefile +5 -7
- data/exe/tapioca +2 -2
- data/lib/tapioca/cli.rb +172 -2
- data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
- data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
- data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
- data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
- data/lib/tapioca/compilers/dsl/active_model_secure_password.rb +101 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
- data/lib/tapioca/compilers/dsl/active_record_fixtures.rb +86 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
- data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
- data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
- data/lib/tapioca/compilers/dsl/active_support_concern.rb +106 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
- data/lib/tapioca/compilers/dsl/base.rb +108 -82
- data/lib/tapioca/compilers/dsl/config.rb +111 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
- data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
- data/lib/tapioca/compilers/dsl/mixed_in_class_attributes.rb +74 -0
- data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
- data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
- data/lib/tapioca/compilers/dsl/smart_properties.rb +21 -33
- data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
- data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
- data/lib/tapioca/compilers/dsl_compiler.rb +25 -40
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +198 -0
- data/lib/tapioca/compilers/requires_compiler.rb +2 -2
- data/lib/tapioca/compilers/sorbet.rb +25 -5
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +122 -206
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
- data/lib/tapioca/compilers/symbol_table_compiler.rb +5 -11
- data/lib/tapioca/compilers/todos_compiler.rb +1 -1
- data/lib/tapioca/config.rb +3 -0
- data/lib/tapioca/config_builder.rb +5 -2
- data/lib/tapioca/constant_locator.rb +6 -8
- data/lib/tapioca/gemfile.rb +14 -11
- data/lib/tapioca/generators/base.rb +61 -0
- data/lib/tapioca/generators/dsl.rb +362 -0
- data/lib/tapioca/generators/gem.rb +345 -0
- data/lib/tapioca/generators/init.rb +79 -0
- data/lib/tapioca/generators/require.rb +52 -0
- data/lib/tapioca/generators/todo.rb +76 -0
- data/lib/tapioca/generators.rb +9 -0
- data/lib/tapioca/generic_type_registry.rb +25 -98
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
- data/lib/tapioca/internal.rb +2 -10
- data/lib/tapioca/loader.rb +11 -31
- data/lib/tapioca/rbi_ext/model.rb +166 -0
- data/lib/tapioca/reflection.rb +138 -0
- data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
- data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +3 -0
- metadata +45 -23
- data/lib/tapioca/cli/main.rb +0 -146
- data/lib/tapioca/core_ext/class.rb +0 -28
- data/lib/tapioca/core_ext/string.rb +0 -18
- data/lib/tapioca/generator.rb +0 -633
- data/lib/tapioca/rbi/model.rb +0 -405
- data/lib/tapioca/rbi/printer.rb +0 -410
- data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
- data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
- data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
- data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
- data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -0,0 +1,86 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "rails"
|
6
|
+
require "active_record"
|
7
|
+
require "active_record/fixtures"
|
8
|
+
require "active_support/test_case"
|
9
|
+
rescue LoadError
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
module Tapioca
|
14
|
+
module Compilers
|
15
|
+
module Dsl
|
16
|
+
# `Tapioca::Compilers::Dsl::ActiveRecordFixtures` decorates RBIs for test fixture methods
|
17
|
+
# that are created dynamically by Rails.
|
18
|
+
#
|
19
|
+
# For example, given an application with a posts table, we can have a fixture file
|
20
|
+
#
|
21
|
+
# ~~~yaml
|
22
|
+
# first_post:
|
23
|
+
# author: John
|
24
|
+
# title: My post
|
25
|
+
# ~~~
|
26
|
+
#
|
27
|
+
# Rails will allow us to invoke `posts(:first_post)` in tests to get the fixture record.
|
28
|
+
# The generated RBI by this generator will produce the following
|
29
|
+
#
|
30
|
+
# ~~~rbi
|
31
|
+
# # test_case.rbi
|
32
|
+
# # typed: true
|
33
|
+
# class ActiveSupport::TestCase
|
34
|
+
# sig { params(fixture_names: Symbol).returns(T.untyped) }
|
35
|
+
# def posts(*fixture_names); end
|
36
|
+
# end
|
37
|
+
# ~~~
|
38
|
+
class ActiveRecordFixtures < Base
|
39
|
+
extend T::Sig
|
40
|
+
|
41
|
+
sig { override.params(root: RBI::Tree, constant: T.class_of(ActiveSupport::TestCase)).void }
|
42
|
+
def decorate(root, constant)
|
43
|
+
method_names = fixture_loader.ancestors # get all ancestors from class that includes AR fixtures
|
44
|
+
.drop(1) # drop the anonymous class itself from the array
|
45
|
+
.reject(&:name) # only collect anonymous ancestors because fixture methods are always on an anonymous module
|
46
|
+
.map! do |mod|
|
47
|
+
[mod.private_instance_methods(false), mod.instance_methods(false)]
|
48
|
+
end
|
49
|
+
.flatten # merge methods into a single list
|
50
|
+
return if method_names.empty?
|
51
|
+
|
52
|
+
root.create_path(constant) do |mod|
|
53
|
+
method_names.each do |name|
|
54
|
+
create_fixture_method(mod, name.to_s)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
sig { override.returns(T::Enumerable[Module]) }
|
60
|
+
def gather_constants
|
61
|
+
[ActiveSupport::TestCase]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
sig { returns(Class) }
|
67
|
+
def fixture_loader
|
68
|
+
Class.new do
|
69
|
+
T.unsafe(self).include(ActiveRecord::TestFixtures)
|
70
|
+
T.unsafe(self).fixture_path = Rails.root.join("test", "fixtures")
|
71
|
+
T.unsafe(self).fixtures(:all)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
sig { params(mod: RBI::Scope, name: String).void }
|
76
|
+
def create_fixture_method(mod, name)
|
77
|
+
mod.create_method(
|
78
|
+
name,
|
79
|
+
parameters: [create_rest_param("fixture_names", type: "Symbol")],
|
80
|
+
return_type: "T.untyped"
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
|
6
4
|
begin
|
7
5
|
require "active_record"
|
8
6
|
rescue LoadError
|
@@ -47,7 +45,7 @@ module Tapioca
|
|
47
45
|
|
48
46
|
sig do
|
49
47
|
override.params(
|
50
|
-
root:
|
48
|
+
root: RBI::Tree,
|
51
49
|
constant: T.class_of(::ActiveRecord::Base)
|
52
50
|
).void
|
53
51
|
end
|
@@ -55,7 +53,7 @@ module Tapioca
|
|
55
53
|
scope_method_names = constant.send(:generated_relation_methods).instance_methods(false)
|
56
54
|
return if scope_method_names.empty?
|
57
55
|
|
58
|
-
root.
|
56
|
+
root.create_path(constant) do |model|
|
59
57
|
module_name = "GeneratedRelationMethods"
|
60
58
|
|
61
59
|
model.create_module(module_name) do |mod|
|
@@ -70,7 +68,7 @@ module Tapioca
|
|
70
68
|
|
71
69
|
sig { override.returns(T::Enumerable[Module]) }
|
72
70
|
def gather_constants
|
73
|
-
::ActiveRecord::Base.
|
71
|
+
descendants_of(::ActiveRecord::Base).reject(&:abstract_class?)
|
74
72
|
end
|
75
73
|
|
76
74
|
private
|
@@ -78,19 +76,18 @@ module Tapioca
|
|
78
76
|
sig do
|
79
77
|
params(
|
80
78
|
scope_method: String,
|
81
|
-
mod:
|
79
|
+
mod: RBI::Scope,
|
82
80
|
).void
|
83
81
|
end
|
84
82
|
def generate_scope_method(scope_method, mod)
|
85
83
|
# This return type should actually be Model::ActiveRecord_Relation
|
86
84
|
return_type = "T.untyped"
|
87
85
|
|
88
|
-
create_method(
|
89
|
-
mod,
|
86
|
+
mod.create_method(
|
90
87
|
scope_method,
|
91
88
|
parameters: [
|
92
|
-
|
93
|
-
|
89
|
+
create_rest_param("args", type: "T.untyped"),
|
90
|
+
create_block_param("blk", type: "T.untyped"),
|
94
91
|
],
|
95
92
|
return_type: return_type,
|
96
93
|
)
|
@@ -1,9 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
require "tapioca/core_ext/class"
|
6
|
-
|
7
4
|
begin
|
8
5
|
require "activerecord-typedstore"
|
9
6
|
rescue LoadError
|
@@ -92,7 +89,7 @@ module Tapioca
|
|
92
89
|
sig do
|
93
90
|
override
|
94
91
|
.params(
|
95
|
-
root:
|
92
|
+
root: RBI::Tree,
|
96
93
|
constant: T.class_of(::ActiveRecord::Base)
|
97
94
|
)
|
98
95
|
.void
|
@@ -101,7 +98,7 @@ module Tapioca
|
|
101
98
|
stores = constant.typed_stores
|
102
99
|
return if stores.values.flat_map(&:accessors).empty?
|
103
100
|
|
104
|
-
root.
|
101
|
+
root.create_path(constant) do |model|
|
105
102
|
stores.values.each do |store_data|
|
106
103
|
store_data.accessors.each do |accessor|
|
107
104
|
field = store_data.fields[accessor]
|
@@ -116,7 +113,7 @@ module Tapioca
|
|
116
113
|
|
117
114
|
sig { override.returns(T::Enumerable[Module]) }
|
118
115
|
def gather_constants
|
119
|
-
::ActiveRecord::Base.
|
116
|
+
descendants_of(::ActiveRecord::Base).select do |klass|
|
120
117
|
klass.include?(ActiveRecord::TypedStore::Behavior)
|
121
118
|
end
|
122
119
|
end
|
@@ -142,7 +139,7 @@ module Tapioca
|
|
142
139
|
|
143
140
|
sig do
|
144
141
|
params(
|
145
|
-
klass:
|
142
|
+
klass: RBI::Scope,
|
146
143
|
name: String,
|
147
144
|
type: String
|
148
145
|
)
|
@@ -151,7 +148,7 @@ module Tapioca
|
|
151
148
|
def generate_methods(klass, name, type)
|
152
149
|
klass.create_method(
|
153
150
|
"#{name}=",
|
154
|
-
parameters: [
|
151
|
+
parameters: [create_param(name, type: type)],
|
155
152
|
return_type: type
|
156
153
|
)
|
157
154
|
klass.create_method(name, return_type: type)
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
|
6
4
|
begin
|
7
5
|
require "active_resource"
|
8
6
|
rescue LoadError
|
@@ -63,16 +61,11 @@ module Tapioca
|
|
63
61
|
class ActiveResource < Base
|
64
62
|
extend T::Sig
|
65
63
|
|
66
|
-
sig
|
67
|
-
override.params(
|
68
|
-
root: Parlour::RbiGenerator::Namespace,
|
69
|
-
constant: T.class_of(::ActiveResource::Base)
|
70
|
-
).void
|
71
|
-
end
|
64
|
+
sig { override.params(root: RBI::Tree, constant: T.class_of(::ActiveResource::Base)).void }
|
72
65
|
def decorate(root, constant)
|
73
66
|
return if constant.schema.blank?
|
74
67
|
|
75
|
-
root.
|
68
|
+
root.create_path(constant) do |resource|
|
76
69
|
constant.schema.each do |attribute, type|
|
77
70
|
create_schema_methods(resource, attribute, type)
|
78
71
|
end
|
@@ -81,7 +74,7 @@ module Tapioca
|
|
81
74
|
|
82
75
|
sig { override.returns(T::Enumerable[Module]) }
|
83
76
|
def gather_constants
|
84
|
-
::ActiveResource::Base
|
77
|
+
descendants_of(::ActiveResource::Base)
|
85
78
|
end
|
86
79
|
|
87
80
|
private
|
@@ -104,36 +97,15 @@ module Tapioca
|
|
104
97
|
TYPES.fetch(attr_type, "T.untyped")
|
105
98
|
end
|
106
99
|
|
107
|
-
sig
|
108
|
-
params(
|
109
|
-
klass: Parlour::RbiGenerator::Namespace,
|
110
|
-
attribute: String,
|
111
|
-
type: String
|
112
|
-
).void
|
113
|
-
end
|
100
|
+
sig { params(klass: RBI::Scope, attribute: String, type: String).void }
|
114
101
|
def create_schema_methods(klass, attribute, type)
|
115
102
|
return_type = type_for(type.to_sym)
|
116
103
|
|
117
|
-
create_method(
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
)
|
122
|
-
|
123
|
-
create_method(
|
124
|
-
klass,
|
125
|
-
"#{attribute}?",
|
126
|
-
return_type: "T::Boolean"
|
127
|
-
)
|
128
|
-
|
129
|
-
create_method(
|
130
|
-
klass,
|
131
|
-
"#{attribute}=",
|
132
|
-
parameters: [
|
133
|
-
Parlour::RbiGenerator::Parameter.new("value", type: return_type),
|
134
|
-
],
|
135
|
-
return_type: return_type
|
136
|
-
)
|
104
|
+
klass.create_method(attribute, return_type: return_type)
|
105
|
+
klass.create_method("#{attribute}?", return_type: "T::Boolean")
|
106
|
+
klass.create_method("#{attribute}=", parameters: [
|
107
|
+
create_param("value", type: return_type),
|
108
|
+
], return_type: return_type)
|
137
109
|
end
|
138
110
|
end
|
139
111
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "active_storage"
|
6
|
+
require "active_storage/reflection"
|
7
|
+
rescue LoadError
|
8
|
+
return
|
9
|
+
end
|
10
|
+
|
11
|
+
module Tapioca
|
12
|
+
module Compilers
|
13
|
+
module Dsl
|
14
|
+
# `Tapioca::Compilers::Dsl::ActiveStorage` decorates RBI files for subclasses of
|
15
|
+
# `ActiveRecord::Base` that declare [one](https://edgeguides.rubyonrails.org/active_storage_overview.html#has-one-attached)
|
16
|
+
# or [many](https://edgeguides.rubyonrails.org/active_storage_overview.html#has-many-attached) attachments.
|
17
|
+
#
|
18
|
+
# For example, with the following `ActiveRecord::Base` subclass:
|
19
|
+
#
|
20
|
+
# ~~~rb
|
21
|
+
# class Post < ApplicationRecord
|
22
|
+
# has_one_attached :photo
|
23
|
+
# has_many_attached :blogs
|
24
|
+
# end
|
25
|
+
# ~~~
|
26
|
+
#
|
27
|
+
# this generator will produce the RBI file `post.rbi` with the following content:
|
28
|
+
#
|
29
|
+
# ~~~rbi
|
30
|
+
# # typed: strong
|
31
|
+
#
|
32
|
+
# class Post
|
33
|
+
# sig { returns(ActiveStorage::Attached::Many) }
|
34
|
+
# def blogs; end
|
35
|
+
#
|
36
|
+
# sig { params(attachable: T.untyped).returns(T.untyped) }
|
37
|
+
# def blogs=(attachable); end
|
38
|
+
#
|
39
|
+
# sig { returns(ActiveStorage::Attached::One) }
|
40
|
+
# def photo; end
|
41
|
+
#
|
42
|
+
# sig { params(attachable: T.untyped).returns(T.untyped) }
|
43
|
+
# def photo=(attachable); end
|
44
|
+
# end
|
45
|
+
# ~~~
|
46
|
+
class ActiveStorage < Base
|
47
|
+
extend T::Sig
|
48
|
+
|
49
|
+
sig do
|
50
|
+
override.params(root: RBI::Tree,
|
51
|
+
constant: T.all(Module, ::ActiveStorage::Reflection::ActiveRecordExtensions::ClassMethods)).void
|
52
|
+
end
|
53
|
+
def decorate(root, constant)
|
54
|
+
return if constant.reflect_on_all_attachments.empty?
|
55
|
+
|
56
|
+
root.create_path(constant) do |scope|
|
57
|
+
constant.reflect_on_all_attachments.each do |reflection|
|
58
|
+
type = type_of(reflection)
|
59
|
+
name = reflection.name.to_s
|
60
|
+
scope.create_method(
|
61
|
+
name,
|
62
|
+
return_type: type
|
63
|
+
)
|
64
|
+
scope.create_method(
|
65
|
+
"#{name}=",
|
66
|
+
parameters: [create_param("attachable", type: "T.untyped")],
|
67
|
+
return_type: "T.untyped"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
sig { override.returns(T::Enumerable[Module]) }
|
74
|
+
def gather_constants
|
75
|
+
descendants_of(::ActiveRecord::Base)
|
76
|
+
.reject(&:abstract_class?)
|
77
|
+
.grep(::ActiveStorage::Reflection::ActiveRecordExtensions::ClassMethods)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
sig do
|
83
|
+
params(reflection: ActiveRecord::Reflection::MacroReflection).returns(String)
|
84
|
+
end
|
85
|
+
def type_of(reflection)
|
86
|
+
case reflection
|
87
|
+
when ::ActiveStorage::Reflection::HasOneAttachedReflection
|
88
|
+
"ActiveStorage::Attached::One"
|
89
|
+
when ::ActiveStorage::Reflection::HasManyAttachedReflection
|
90
|
+
"ActiveStorage::Attached::Many"
|
91
|
+
else
|
92
|
+
"T.untyped"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "tapioca/compilers/sorbet"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "active_support"
|
8
|
+
rescue LoadError
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
module Tapioca
|
13
|
+
module Compilers
|
14
|
+
module Dsl
|
15
|
+
# `Tapioca::Compilers::Dsl::ActiveSupportConcern` generates RBI files for classes that both `extend`
|
16
|
+
# `ActiveSupport::Concern` and `include` another class that extends `ActiveSupport::Concern`
|
17
|
+
#
|
18
|
+
# For example for the following hierarchy:
|
19
|
+
#
|
20
|
+
# ~~~rb
|
21
|
+
# # concern.rb
|
22
|
+
# module Foo
|
23
|
+
# extend ActiveSupport::Concern
|
24
|
+
# module ClassMethods; end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# module Bar
|
28
|
+
# extend ActiveSupport::Concern
|
29
|
+
# module ClassMethods; end
|
30
|
+
# include Foo
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# class Baz
|
34
|
+
# include Bar
|
35
|
+
# end
|
36
|
+
# ~~~
|
37
|
+
#
|
38
|
+
# this generator will produce the RBI file `concern.rbi` with the following content:
|
39
|
+
#
|
40
|
+
# ~~~rbi
|
41
|
+
# # typed: true
|
42
|
+
# module Bar
|
43
|
+
# mixes_in_class_methods(::Foo::ClassMethods)
|
44
|
+
# end
|
45
|
+
# ~~~
|
46
|
+
class ActiveSupportConcern < Base
|
47
|
+
extend T::Sig
|
48
|
+
|
49
|
+
sig { override.params(root: RBI::Tree, constant: Module).void }
|
50
|
+
def decorate(root, constant)
|
51
|
+
dependencies = linearized_dependencies_of(constant)
|
52
|
+
|
53
|
+
mixed_in_class_methods = dependencies
|
54
|
+
.uniq # Deduplicate
|
55
|
+
.map do |concern| # Map to class methods module name, if exists
|
56
|
+
"#{qualified_name_of(concern)}::ClassMethods" if concern.const_defined?(:ClassMethods)
|
57
|
+
end
|
58
|
+
.compact # Remove non-existent records
|
59
|
+
|
60
|
+
return if mixed_in_class_methods.empty?
|
61
|
+
|
62
|
+
root.create_path(constant) do |mod|
|
63
|
+
mixed_in_class_methods.each do |mix|
|
64
|
+
mod.create_mixes_in_class_methods(mix)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
sig { override.returns(T::Enumerable[Module]) }
|
70
|
+
def gather_constants
|
71
|
+
# Find all Modules that are:
|
72
|
+
all_modules.select do |mod|
|
73
|
+
# named (i.e. not anonymous)
|
74
|
+
name_of(mod) &&
|
75
|
+
# not singleton classes
|
76
|
+
!mod.singleton_class? &&
|
77
|
+
# extend ActiveSupport::Concern, and
|
78
|
+
mod.singleton_class < ActiveSupport::Concern &&
|
79
|
+
# have dependencies (i.e. include another concern)
|
80
|
+
!dependencies_of(mod).empty?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
sig { params(concern: Module).returns(T::Array[Module]) }
|
87
|
+
def dependencies_of(concern)
|
88
|
+
concern.instance_variable_get(:@_dependencies)
|
89
|
+
end
|
90
|
+
|
91
|
+
sig { params(concern: Module).returns(T::Array[Module]) }
|
92
|
+
def linearized_dependencies_of(concern)
|
93
|
+
# Grab all the dependencies of the concern
|
94
|
+
dependencies = dependencies_of(concern)
|
95
|
+
|
96
|
+
# Flatten this concern's dependencies and all of their dependencies
|
97
|
+
dependencies.flat_map do |dependency|
|
98
|
+
# Linearize dependencies of the current dependency,
|
99
|
+
# which, itself, is a concern
|
100
|
+
linearized_dependencies_of(dependency) << dependency
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
|
6
4
|
begin
|
7
5
|
require "active_support"
|
6
|
+
# The following is needed due to https://github.com/rails/rails/pull/41610
|
7
|
+
require "active_support/core_ext/module/delegation"
|
8
8
|
rescue LoadError
|
9
9
|
return
|
10
10
|
end
|
@@ -65,7 +65,7 @@ module Tapioca
|
|
65
65
|
sig do
|
66
66
|
override
|
67
67
|
.params(
|
68
|
-
root:
|
68
|
+
root: RBI::Tree,
|
69
69
|
constant: T.class_of(::ActiveSupport::CurrentAttributes)
|
70
70
|
)
|
71
71
|
.void
|
@@ -75,7 +75,7 @@ module Tapioca
|
|
75
75
|
instance_methods = instance_methods_for(constant) - dynamic_methods
|
76
76
|
return if dynamic_methods.empty? && instance_methods.empty?
|
77
77
|
|
78
|
-
root.
|
78
|
+
root.create_path(constant) do |current_attributes|
|
79
79
|
dynamic_methods.each do |method|
|
80
80
|
method = method.to_s
|
81
81
|
# We want to generate each method both on the class
|
@@ -95,7 +95,7 @@ module Tapioca
|
|
95
95
|
|
96
96
|
sig { override.returns(T::Enumerable[Module]) }
|
97
97
|
def gather_constants
|
98
|
-
::ActiveSupport::CurrentAttributes
|
98
|
+
descendants_of(::ActiveSupport::CurrentAttributes)
|
99
99
|
end
|
100
100
|
|
101
101
|
private
|
@@ -110,11 +110,16 @@ module Tapioca
|
|
110
110
|
constant.instance_methods(false)
|
111
111
|
end
|
112
112
|
|
113
|
-
sig { params(klass:
|
113
|
+
sig { params(klass: RBI::Scope, method: String, class_method: T::Boolean).void }
|
114
114
|
def generate_method(klass, method, class_method:)
|
115
115
|
if method.end_with?("=")
|
116
|
-
parameter =
|
117
|
-
klass.create_method(
|
116
|
+
parameter = create_param("value", type: "T.untyped")
|
117
|
+
klass.create_method(
|
118
|
+
method,
|
119
|
+
class_method: class_method,
|
120
|
+
parameters: [parameter],
|
121
|
+
return_type: "T.untyped"
|
122
|
+
)
|
118
123
|
else
|
119
124
|
klass.create_method(method, class_method: class_method, return_type: "T.untyped")
|
120
125
|
end
|