tapioca 0.6.4 → 0.7.2
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 +8 -2
- data/README.md +27 -15
- data/Rakefile +10 -14
- data/lib/tapioca/cli.rb +65 -80
- data/lib/tapioca/{generators/base.rb → commands/command.rb} +16 -9
- data/lib/tapioca/{generators → commands}/dsl.rb +59 -45
- data/lib/tapioca/{generators → commands}/gem.rb +93 -30
- data/lib/tapioca/{generators → commands}/init.rb +9 -13
- data/lib/tapioca/{generators → commands}/require.rb +8 -10
- data/lib/tapioca/commands/todo.rb +86 -0
- data/lib/tapioca/commands.rb +13 -0
- data/lib/tapioca/dsl/compiler.rb +185 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/aasm.rb +12 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_controller_helpers.rb +13 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_mailer.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_job.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_attributes.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_secure_password.rb +10 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_associations.rb +28 -34
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +18 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_enum.rb +14 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_fixtures.rb +12 -8
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +712 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_scope.rb +21 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_typed_store.rb +11 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +14 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_concern.rb +19 -14
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_current_attributes.rb +16 -21
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/config.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +23 -22
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/mixed_in_class_attributes.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/protobuf.rb +22 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +12 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +20 -15
- data/lib/tapioca/dsl/compilers.rb +31 -0
- data/lib/tapioca/{compilers/dsl → dsl}/extensions/frozen_record.rb +2 -2
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +114 -0
- data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +29 -0
- data/lib/tapioca/{compilers/dsl → dsl/helpers}/param_helper.rb +6 -3
- data/lib/tapioca/dsl/pipeline.rb +169 -0
- data/lib/tapioca/gem/events.rb +120 -0
- data/lib/tapioca/gem/listeners/base.rb +48 -0
- data/lib/tapioca/gem/listeners/dynamic_mixins.rb +32 -0
- data/lib/tapioca/gem/listeners/methods.rb +183 -0
- data/lib/tapioca/gem/listeners/mixins.rb +101 -0
- data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +21 -0
- data/lib/tapioca/gem/listeners/sorbet_enums.rb +26 -0
- data/lib/tapioca/gem/listeners/sorbet_helpers.rb +29 -0
- data/lib/tapioca/gem/listeners/sorbet_props.rb +33 -0
- data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +23 -0
- data/lib/tapioca/gem/listeners/sorbet_signatures.rb +79 -0
- data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +51 -0
- data/lib/tapioca/gem/listeners/subconstants.rb +37 -0
- data/lib/tapioca/gem/listeners/yard_doc.rb +96 -0
- data/lib/tapioca/gem/listeners.rb +16 -0
- data/lib/tapioca/gem/pipeline.rb +365 -0
- data/lib/tapioca/helpers/cli_helper.rb +7 -0
- data/lib/tapioca/helpers/config_helper.rb +5 -8
- data/lib/tapioca/helpers/shims_helper.rb +87 -0
- data/lib/tapioca/helpers/signatures_helper.rb +17 -0
- data/lib/tapioca/helpers/sorbet_helper.rb +57 -0
- data/lib/tapioca/helpers/test/dsl_compiler.rb +118 -0
- data/lib/tapioca/helpers/test/isolation.rb +1 -1
- data/lib/tapioca/helpers/test/template.rb +13 -2
- data/lib/tapioca/helpers/type_variable_helper.rb +43 -0
- data/lib/tapioca/internal.rb +18 -10
- data/lib/tapioca/rbi_ext/model.rb +14 -50
- data/lib/tapioca/rbi_formatter.rb +37 -0
- data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +227 -0
- data/lib/tapioca/runtime/generic_type_registry.rb +168 -0
- data/lib/tapioca/runtime/loader.rb +123 -0
- data/lib/tapioca/runtime/reflection.rb +157 -0
- data/lib/tapioca/runtime/trackers/autoload.rb +72 -0
- data/lib/tapioca/runtime/trackers/constant_definition.rb +44 -0
- data/lib/tapioca/runtime/trackers/mixin.rb +80 -0
- data/lib/tapioca/runtime/trackers/required_ancestor.rb +50 -0
- data/lib/tapioca/{trackers.rb → runtime/trackers.rb} +4 -3
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +69 -34
- data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
- data/lib/tapioca/{compilers → static}/requires_compiler.rb +2 -2
- data/lib/tapioca/static/symbol_loader.rb +83 -0
- data/lib/tapioca/static/symbol_table_parser.rb +63 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +2 -7
- metadata +83 -62
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -720
- data/lib/tapioca/compilers/dsl/base.rb +0 -195
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
- data/lib/tapioca/compilers/dsl_compiler.rb +0 -134
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -223
- data/lib/tapioca/compilers/sorbet.rb +0 -59
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +0 -780
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +0 -90
- data/lib/tapioca/compilers/symbol_table_compiler.rb +0 -17
- data/lib/tapioca/compilers/todos_compiler.rb +0 -32
- data/lib/tapioca/generators/todo.rb +0 -76
- data/lib/tapioca/generators.rb +0 -9
- data/lib/tapioca/generic_type_registry.rb +0 -164
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -108
- data/lib/tapioca/loader.rb +0 -119
- data/lib/tapioca/reflection.rb +0 -151
- data/lib/tapioca/trackers/autoload.rb +0 -70
- data/lib/tapioca/trackers/constant_definition.rb +0 -42
- data/lib/tapioca/trackers/mixin.rb +0 -78
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::MixedInClassAttributes` generates RBI files for modules that dynamically use
|
|
14
14
|
# `class_attribute` on classes.
|
|
15
15
|
#
|
|
16
16
|
# For example, given the following concern
|
|
@@ -25,7 +25,7 @@ module Tapioca
|
|
|
25
25
|
# end
|
|
26
26
|
# ~~~
|
|
27
27
|
#
|
|
28
|
-
# this
|
|
28
|
+
# this compiler will produce the RBI file `taggeable.rbi` with the following content:
|
|
29
29
|
#
|
|
30
30
|
# ~~~rbi
|
|
31
31
|
# # typed: strong
|
|
@@ -48,12 +48,14 @@ module Tapioca
|
|
|
48
48
|
# end
|
|
49
49
|
# end
|
|
50
50
|
# ~~~
|
|
51
|
-
class MixedInClassAttributes <
|
|
51
|
+
class MixedInClassAttributes < Compiler
|
|
52
52
|
extend T::Sig
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
ConstantType = type_member { { fixed: Module } }
|
|
55
|
+
|
|
56
|
+
sig { override.void }
|
|
57
|
+
def decorate
|
|
58
|
+
mixin_compiler = Runtime::DynamicMixinCompiler.new(constant)
|
|
57
59
|
return if mixin_compiler.empty_attributes?
|
|
58
60
|
|
|
59
61
|
root.create_path(constant) do |mod|
|
|
@@ -62,10 +64,10 @@ module Tapioca
|
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
sig { override.returns(T::Enumerable[Module]) }
|
|
65
|
-
def gather_constants
|
|
67
|
+
def self.gather_constants
|
|
66
68
|
# Select all non-anonymous modules that have overridden Module.included
|
|
67
69
|
all_modules.select do |mod|
|
|
68
|
-
!mod.is_a?(Class) && name_of(mod) &&
|
|
70
|
+
!mod.is_a?(Class) && name_of(mod) && Runtime::Reflection.method_of(mod, :included).owner != Module
|
|
69
71
|
end
|
|
70
72
|
end
|
|
71
73
|
end
|
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::Protobuf` decorates RBI files for subclasses of
|
|
14
14
|
# [`Google::Protobuf::MessageExts`](https://github.com/protocolbuffers/protobuf/tree/master/ruby).
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following "cart.rb" file:
|
|
@@ -28,7 +28,7 @@ module Tapioca
|
|
|
28
28
|
# end
|
|
29
29
|
# ~~~
|
|
30
30
|
#
|
|
31
|
-
# this
|
|
31
|
+
# this compiler will produce the RBI file `cart.rbi` with the following content:
|
|
32
32
|
#
|
|
33
33
|
# ~~~rbi
|
|
34
34
|
# # cart.rbi
|
|
@@ -60,7 +60,7 @@ module Tapioca
|
|
|
60
60
|
# def number_value=(value); end
|
|
61
61
|
# end
|
|
62
62
|
# ~~~
|
|
63
|
-
class Protobuf <
|
|
63
|
+
class Protobuf < Compiler
|
|
64
64
|
class Field < T::Struct
|
|
65
65
|
prop :name, String
|
|
66
66
|
prop :type, String
|
|
@@ -70,8 +70,12 @@ module Tapioca
|
|
|
70
70
|
|
|
71
71
|
extend T::Sig
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
ConstantType = type_member { { fixed: Module } }
|
|
74
|
+
|
|
75
|
+
FIELD_RE = /^[a-z_][a-zA-Z0-9_]*$/
|
|
76
|
+
|
|
77
|
+
sig { override.void }
|
|
78
|
+
def decorate
|
|
75
79
|
root.create_path(constant) do |klass|
|
|
76
80
|
if constant == Google::Protobuf::RepeatedField
|
|
77
81
|
create_type_members(klass, "Elem")
|
|
@@ -86,13 +90,21 @@ module Tapioca
|
|
|
86
90
|
create_kw_opt_param(field.name, type: field.init_type, default: field.default)
|
|
87
91
|
end
|
|
88
92
|
|
|
89
|
-
|
|
93
|
+
if fields.all? { |field| FIELD_RE.match?(field.name) }
|
|
94
|
+
klass.create_method("initialize", parameters: parameters, return_type: "void")
|
|
95
|
+
else
|
|
96
|
+
# One of the fields has an incorrect name for a named parameter so creating the default initialize for
|
|
97
|
+
# it would create a RBI with a syntax error.
|
|
98
|
+
# The workaround is to create an initialize that takes a **kwargs instead.
|
|
99
|
+
kwargs_parameter = create_kw_rest_param("fields", type: "T.untyped")
|
|
100
|
+
klass.create_method("initialize", parameters: [kwargs_parameter], return_type: "void")
|
|
101
|
+
end
|
|
90
102
|
end
|
|
91
103
|
end
|
|
92
104
|
end
|
|
93
105
|
|
|
94
106
|
sig { override.returns(T::Enumerable[Module]) }
|
|
95
|
-
def gather_constants
|
|
107
|
+
def self.gather_constants
|
|
96
108
|
marker = Google::Protobuf::MessageExts::ClassMethods
|
|
97
109
|
results = T.cast(ObjectSpace.each_object(marker).to_a, T::Array[Module])
|
|
98
110
|
results.any? ? results + [Google::Protobuf::RepeatedField, Google::Protobuf::Map] : []
|
|
@@ -105,7 +117,7 @@ module Tapioca
|
|
|
105
117
|
klass.create_extend("T::Generic")
|
|
106
118
|
|
|
107
119
|
names.each do |name|
|
|
108
|
-
klass.
|
|
120
|
+
klass.create_type_variable(name, type: "type_member")
|
|
109
121
|
end
|
|
110
122
|
end
|
|
111
123
|
|
|
@@ -9,9 +9,9 @@ rescue LoadError
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
module Tapioca
|
|
12
|
-
module
|
|
13
|
-
module
|
|
14
|
-
# `Tapioca::Compilers::
|
|
12
|
+
module Dsl
|
|
13
|
+
module Compilers
|
|
14
|
+
# `Tapioca::Dsl::Compilers::RailsGenerators` generates RBI files for Rails generators
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following generator:
|
|
17
17
|
#
|
|
@@ -38,7 +38,7 @@ module Tapioca
|
|
|
38
38
|
# def skip_comments; end
|
|
39
39
|
# end
|
|
40
40
|
# ~~~
|
|
41
|
-
class RailsGenerators <
|
|
41
|
+
class RailsGenerators < Compiler
|
|
42
42
|
extend T::Sig
|
|
43
43
|
|
|
44
44
|
BUILT_IN_MATCHER = T.let(
|
|
@@ -46,9 +46,11 @@ module Tapioca
|
|
|
46
46
|
Regexp
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
ConstantType = type_member { { fixed: T.class_of(::Rails::Generators::Base) } }
|
|
50
|
+
|
|
51
|
+
sig { override.void }
|
|
52
|
+
def decorate
|
|
53
|
+
base_class = base_class_of_constant
|
|
52
54
|
arguments = constant.arguments - base_class.arguments
|
|
53
55
|
class_options = constant.class_options.reject do |name, option|
|
|
54
56
|
base_class.class_options[name] == option
|
|
@@ -63,7 +65,7 @@ module Tapioca
|
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
sig { override.returns(T::Enumerable[Module]) }
|
|
66
|
-
def gather_constants
|
|
68
|
+
def self.gather_constants
|
|
67
69
|
all_classes.select do |const|
|
|
68
70
|
name = qualified_name_of(const)
|
|
69
71
|
|
|
@@ -84,11 +86,8 @@ module Tapioca
|
|
|
84
86
|
)
|
|
85
87
|
end
|
|
86
88
|
|
|
87
|
-
sig
|
|
88
|
-
|
|
89
|
-
.returns(T.class_of(::Rails::Generators::Base))
|
|
90
|
-
end
|
|
91
|
-
def base_class_for(constant)
|
|
89
|
+
sig { returns(T.class_of(::Rails::Generators::Base)) }
|
|
90
|
+
def base_class_of_constant
|
|
92
91
|
ancestor = inherited_ancestors_of(constant).find do |klass|
|
|
93
92
|
qualified_name_of(klass)&.match?(BUILT_IN_MATCHER)
|
|
94
93
|
end
|
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::SidekiqWorker` generates RBI files classes that include
|
|
14
14
|
# [`Sidekiq::Worker`](https://github.com/mperham/sidekiq/wiki/Getting-Started).
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following class that includes `Sidekiq::Worker`:
|
|
@@ -24,7 +24,7 @@ module Tapioca
|
|
|
24
24
|
# end
|
|
25
25
|
# ~~~
|
|
26
26
|
#
|
|
27
|
-
# this
|
|
27
|
+
# this compiler will produce the RBI file `notifier_worker.rbi` with the following content:
|
|
28
28
|
#
|
|
29
29
|
# ~~~rbi
|
|
30
30
|
# # notifier_worker.rbi
|
|
@@ -40,11 +40,13 @@ module Tapioca
|
|
|
40
40
|
# def self.perform_in(interval, customer_id); end
|
|
41
41
|
# end
|
|
42
42
|
# ~~~
|
|
43
|
-
class SidekiqWorker <
|
|
43
|
+
class SidekiqWorker < Compiler
|
|
44
44
|
extend T::Sig
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
ConstantType = type_member { { fixed: T.class_of(::Sidekiq::Worker) } }
|
|
47
|
+
|
|
48
|
+
sig { override.void }
|
|
49
|
+
def decorate
|
|
48
50
|
return unless constant.instance_methods.include?(:perform)
|
|
49
51
|
|
|
50
52
|
root.create_path(constant) do |worker|
|
|
@@ -64,14 +66,14 @@ module Tapioca
|
|
|
64
66
|
*async_params,
|
|
65
67
|
]
|
|
66
68
|
|
|
67
|
-
generate_perform_method(
|
|
68
|
-
generate_perform_method(
|
|
69
|
-
generate_perform_method(
|
|
69
|
+
generate_perform_method(worker, "perform_async", async_params)
|
|
70
|
+
generate_perform_method(worker, "perform_at", at_params)
|
|
71
|
+
generate_perform_method(worker, "perform_in", in_params)
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
sig { override.returns(T::Enumerable[Module]) }
|
|
74
|
-
def gather_constants
|
|
76
|
+
def self.gather_constants
|
|
75
77
|
all_classes.select { |c| c < Sidekiq::Worker }
|
|
76
78
|
end
|
|
77
79
|
|
|
@@ -79,13 +81,12 @@ module Tapioca
|
|
|
79
81
|
|
|
80
82
|
sig do
|
|
81
83
|
params(
|
|
82
|
-
constant: T.class_of(::Sidekiq::Worker),
|
|
83
84
|
worker: RBI::Scope,
|
|
84
85
|
method_name: String,
|
|
85
86
|
parameters: T::Array[RBI::TypedParam]
|
|
86
87
|
).void
|
|
87
88
|
end
|
|
88
|
-
def generate_perform_method(
|
|
89
|
+
def generate_perform_method(worker, method_name, parameters)
|
|
89
90
|
if constant.method(method_name.to_sym).owner == Sidekiq::Worker::ClassMethods
|
|
90
91
|
worker.create_method(method_name, parameters: parameters, return_type: "String", class_method: true)
|
|
91
92
|
end
|
|
@@ -5,14 +5,14 @@ begin
|
|
|
5
5
|
require "smart_properties"
|
|
6
6
|
rescue LoadError
|
|
7
7
|
# means SmartProperties is not installed,
|
|
8
|
-
# so let's not even define the
|
|
8
|
+
# so let's not even define the compiler.
|
|
9
9
|
return
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
module Tapioca
|
|
13
|
-
module
|
|
14
|
-
module
|
|
15
|
-
# `Tapioca::Compilers::
|
|
13
|
+
module Dsl
|
|
14
|
+
module Compilers
|
|
15
|
+
# `Tapioca::Dsl::Compilers::SmartProperties` generates RBI files for classes that include
|
|
16
16
|
# [`SmartProperties`](https://github.com/t6d/smart_properties).
|
|
17
17
|
#
|
|
18
18
|
# For example, with the following class that includes `SmartProperties`:
|
|
@@ -29,7 +29,7 @@ module Tapioca
|
|
|
29
29
|
# end
|
|
30
30
|
# ~~~
|
|
31
31
|
#
|
|
32
|
-
# this
|
|
32
|
+
# this compiler will produce the RBI file `post.rbi` with the following content:
|
|
33
33
|
#
|
|
34
34
|
# ~~~rbi
|
|
35
35
|
# # post.rbi
|
|
@@ -60,11 +60,13 @@ module Tapioca
|
|
|
60
60
|
# def enabled=(enabled); end
|
|
61
61
|
# end
|
|
62
62
|
# ~~~
|
|
63
|
-
class SmartProperties <
|
|
63
|
+
class SmartProperties < Compiler
|
|
64
64
|
extend T::Sig
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
ConstantType = type_member { { fixed: T.class_of(::SmartProperties) } }
|
|
67
|
+
|
|
68
|
+
sig { override.void }
|
|
69
|
+
def decorate
|
|
68
70
|
properties = T.let(
|
|
69
71
|
T.unsafe(constant).properties,
|
|
70
72
|
::SmartProperties::PropertyCollection
|
|
@@ -84,7 +86,7 @@ module Tapioca
|
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
sig { override.returns(T::Enumerable[Module]) }
|
|
87
|
-
def gather_constants
|
|
89
|
+
def self.gather_constants
|
|
88
90
|
all_modules.select do |c|
|
|
89
91
|
name_of(c) &&
|
|
90
92
|
c != ::SmartProperties::Validations::Ancestor &&
|
|
@@ -5,15 +5,15 @@ begin
|
|
|
5
5
|
require "state_machines"
|
|
6
6
|
rescue LoadError
|
|
7
7
|
# means StateMachines is not installed,
|
|
8
|
-
# so let's not even define the
|
|
8
|
+
# so let's not even define the compiler.
|
|
9
9
|
return
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
module Tapioca
|
|
13
|
-
module
|
|
14
|
-
module
|
|
15
|
-
# `Tapioca::Compilers::
|
|
16
|
-
# [`state_machine`](https://github.com/state-machines/state_machines). The
|
|
13
|
+
module Dsl
|
|
14
|
+
module Compilers
|
|
15
|
+
# `Tapioca::Dsl::Compilers::StateMachines` generates RBI files for classes that setup a
|
|
16
|
+
# [`state_machine`](https://github.com/state-machines/state_machines). The compiler also
|
|
17
17
|
# processes the extra methods generated by
|
|
18
18
|
# [StateMachines Active Record](https://github.com/state-machines/state_machines-activerecord)
|
|
19
19
|
# and [StateMachines Active Model](https://github.com/state-machines/state_machines-activemodel)
|
|
@@ -38,7 +38,7 @@ module Tapioca
|
|
|
38
38
|
# end
|
|
39
39
|
# ~~~
|
|
40
40
|
#
|
|
41
|
-
# this
|
|
41
|
+
# this compiler will produce the RBI file `vehicle.rbi` with the following content:
|
|
42
42
|
#
|
|
43
43
|
# ~~~rbi
|
|
44
44
|
# # vehicle.rbi
|
|
@@ -115,11 +115,13 @@ module Tapioca
|
|
|
115
115
|
# end
|
|
116
116
|
# end
|
|
117
117
|
# ~~~
|
|
118
|
-
class StateMachines <
|
|
118
|
+
class StateMachines < Compiler
|
|
119
119
|
extend T::Sig
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
ConstantType = type_member { { fixed: T.all(Module, ::StateMachines::ClassMethods) } }
|
|
122
|
+
|
|
123
|
+
sig { override.void }
|
|
124
|
+
def decorate
|
|
123
125
|
return if constant.state_machines.empty?
|
|
124
126
|
|
|
125
127
|
root.create_path(T.unsafe(constant)) do |klass|
|
|
@@ -159,7 +161,7 @@ module Tapioca
|
|
|
159
161
|
end
|
|
160
162
|
|
|
161
163
|
sig { override.returns(T::Enumerable[Module]) }
|
|
162
|
-
def gather_constants
|
|
164
|
+
def self.gather_constants
|
|
163
165
|
all_classes.select { |mod| mod < ::StateMachines::InstanceMethods }
|
|
164
166
|
end
|
|
165
167
|
|
|
@@ -10,9 +10,9 @@ rescue LoadError
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
module Tapioca
|
|
13
|
-
module
|
|
14
|
-
module
|
|
15
|
-
# `Tapioca::Compilers::
|
|
13
|
+
module Dsl
|
|
14
|
+
module Compilers
|
|
15
|
+
# `Tapioca::Dsl::Compilers::UrlHelpers` generates RBI files for classes that include or extend
|
|
16
16
|
# [`Rails.application.routes.url_helpers`](https://api.rubyonrails.org/v5.1.7/classes/ActionDispatch/Routing/UrlFor.html#module-ActionDispatch::Routing::UrlFor-label-URL+generation+for+named+routes).
|
|
17
17
|
#
|
|
18
18
|
# For example, with the following setup:
|
|
@@ -32,13 +32,13 @@ module Tapioca
|
|
|
32
32
|
# # Use `T.unsafe` so that Sorbet does not complain about a dynamic
|
|
33
33
|
# # module being included. This allows the `include` to happen properly
|
|
34
34
|
# # at runtime but Sorbet won't see the include. However, since this
|
|
35
|
-
# #
|
|
35
|
+
# # compiler will generate the proper RBI files for the include,
|
|
36
36
|
# # static type checking will work as expected.
|
|
37
37
|
# T.unsafe(self).include Rails.application.routes.url_helpers
|
|
38
38
|
# end
|
|
39
39
|
# ~~~
|
|
40
40
|
#
|
|
41
|
-
# this
|
|
41
|
+
# this compiler will produce the following RBI files:
|
|
42
42
|
#
|
|
43
43
|
# ~~~rbi
|
|
44
44
|
# # generated_path_helpers_module.rbi
|
|
@@ -84,18 +84,20 @@ module Tapioca
|
|
|
84
84
|
# include GeneratedUrlHelpersModule
|
|
85
85
|
# end
|
|
86
86
|
# ~~~
|
|
87
|
-
class UrlHelpers <
|
|
87
|
+
class UrlHelpers < Compiler
|
|
88
88
|
extend T::Sig
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
ConstantType = type_member { { fixed: Module } }
|
|
91
|
+
|
|
92
|
+
sig { override.void }
|
|
93
|
+
def decorate
|
|
92
94
|
case constant
|
|
93
95
|
when GeneratedPathHelpersModule.singleton_class, GeneratedUrlHelpersModule.singleton_class
|
|
94
96
|
generate_module_for(root, constant)
|
|
95
97
|
else
|
|
96
98
|
root.create_path(constant) do |mod|
|
|
97
|
-
create_mixins_for(mod,
|
|
98
|
-
create_mixins_for(mod,
|
|
99
|
+
create_mixins_for(mod, GeneratedUrlHelpersModule)
|
|
100
|
+
create_mixins_for(mod, GeneratedPathHelpersModule)
|
|
99
101
|
end
|
|
100
102
|
end
|
|
101
103
|
end
|
|
@@ -106,7 +108,9 @@ module Tapioca
|
|
|
106
108
|
], T::Array[Module])
|
|
107
109
|
|
|
108
110
|
sig { override.returns(T::Enumerable[Module]) }
|
|
109
|
-
def gather_constants
|
|
111
|
+
def self.gather_constants
|
|
112
|
+
return [] unless Rails.application
|
|
113
|
+
|
|
110
114
|
Object.const_set(:GeneratedUrlHelpersModule, Rails.application.routes.named_routes.url_helpers_module)
|
|
111
115
|
Object.const_set(:GeneratedPathHelpersModule, Rails.application.routes.named_routes.path_helpers_module)
|
|
112
116
|
|
|
@@ -140,8 +144,8 @@ module Tapioca
|
|
|
140
144
|
end
|
|
141
145
|
end
|
|
142
146
|
|
|
143
|
-
sig { params(mod: RBI::Scope,
|
|
144
|
-
def create_mixins_for(mod,
|
|
147
|
+
sig { params(mod: RBI::Scope, helper_module: Module).void }
|
|
148
|
+
def create_mixins_for(mod, helper_module)
|
|
145
149
|
include_helper = constant.ancestors.include?(helper_module) || NON_DISCOVERABLE_INCLUDERS.include?(constant)
|
|
146
150
|
extend_helper = constant.singleton_class.ancestors.include?(helper_module)
|
|
147
151
|
|
|
@@ -150,7 +154,7 @@ module Tapioca
|
|
|
150
154
|
end
|
|
151
155
|
|
|
152
156
|
sig { params(mod: Module, helper: Module).returns(T::Boolean) }
|
|
153
|
-
def includes_helper?(mod, helper)
|
|
157
|
+
private_class_method def self.includes_helper?(mod, helper)
|
|
154
158
|
superclass_ancestors = []
|
|
155
159
|
|
|
156
160
|
if Class === mod
|
|
@@ -158,7 +162,8 @@ module Tapioca
|
|
|
158
162
|
superclass_ancestors = ancestors_of(superclass) if superclass
|
|
159
163
|
end
|
|
160
164
|
|
|
161
|
-
(ancestors_of(mod)
|
|
165
|
+
ancestors = Set.new.compare_by_identity.merge(ancestors_of(mod)).subtract(superclass_ancestors)
|
|
166
|
+
ancestors.any? { |ancestor| helper == ancestor }
|
|
162
167
|
end
|
|
163
168
|
end
|
|
164
169
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "tapioca/rbi_ext/model"
|
|
5
|
+
require "tapioca/dsl/helpers/param_helper"
|
|
6
|
+
require "tapioca/dsl/pipeline"
|
|
7
|
+
|
|
8
|
+
module Tapioca
|
|
9
|
+
module Dsl
|
|
10
|
+
module Compilers
|
|
11
|
+
DIRECTORY = T.let(
|
|
12
|
+
File.expand_path("compilers", __dir__),
|
|
13
|
+
String
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# DSL compilers are either built-in to Tapioca and live under the
|
|
17
|
+
# `Tapioca::Dsl::Compilers` namespace (i.e. this namespace), and
|
|
18
|
+
# can be referred to by just using the class name, or they live in
|
|
19
|
+
# a different namespace and can only be referred to using their fully
|
|
20
|
+
# qualified name. This constant encapsulates that dual lookup when
|
|
21
|
+
# a compiler needs to be resolved by name.
|
|
22
|
+
NAMESPACES = T.let(
|
|
23
|
+
[
|
|
24
|
+
"#{name}::", # compilers in this namespace
|
|
25
|
+
"::", # compilers that need to be fully namespaced
|
|
26
|
+
],
|
|
27
|
+
T::Array[String]
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Tapioca
|
|
5
|
+
module Dsl
|
|
6
|
+
module Helpers
|
|
7
|
+
class ActiveRecordColumnTypeHelper
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
sig { params(constant: T.class_of(ActiveRecord::Base)).void }
|
|
11
|
+
def initialize(constant)
|
|
12
|
+
@constant = constant
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
sig { params(column_name: String).returns([String, String]) }
|
|
16
|
+
def type_for(column_name)
|
|
17
|
+
return ["T.untyped", "T.untyped"] if do_not_generate_strong_types?(@constant)
|
|
18
|
+
|
|
19
|
+
column_type = @constant.attribute_types[column_name]
|
|
20
|
+
|
|
21
|
+
getter_type =
|
|
22
|
+
case column_type
|
|
23
|
+
when defined?(MoneyColumn) && MoneyColumn::ActiveRecordType
|
|
24
|
+
"::Money"
|
|
25
|
+
when ActiveRecord::Type::Integer
|
|
26
|
+
"::Integer"
|
|
27
|
+
when ActiveRecord::Type::String
|
|
28
|
+
"::String"
|
|
29
|
+
when ActiveRecord::Type::Date
|
|
30
|
+
"::Date"
|
|
31
|
+
when ActiveRecord::Type::Decimal
|
|
32
|
+
"::BigDecimal"
|
|
33
|
+
when ActiveRecord::Type::Float
|
|
34
|
+
"::Float"
|
|
35
|
+
when ActiveRecord::Type::Boolean
|
|
36
|
+
"T::Boolean"
|
|
37
|
+
when ActiveRecord::Type::DateTime, ActiveRecord::Type::Time
|
|
38
|
+
"::DateTime"
|
|
39
|
+
when ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter
|
|
40
|
+
"::ActiveSupport::TimeWithZone"
|
|
41
|
+
else
|
|
42
|
+
handle_unknown_type(column_type)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
column = @constant.columns_hash[column_name]
|
|
46
|
+
setter_type = getter_type
|
|
47
|
+
|
|
48
|
+
if column&.null
|
|
49
|
+
return [as_nilable_type(getter_type), as_nilable_type(setter_type)]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if column_name == @constant.primary_key ||
|
|
53
|
+
column_name == "created_at" ||
|
|
54
|
+
column_name == "updated_at"
|
|
55
|
+
getter_type = as_nilable_type(getter_type)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
[getter_type, setter_type]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
sig { params(constant: Module).returns(T::Boolean) }
|
|
64
|
+
def do_not_generate_strong_types?(constant)
|
|
65
|
+
Object.const_defined?(:StrongTypeGeneration) &&
|
|
66
|
+
!(constant.singleton_class < Object.const_get(:StrongTypeGeneration))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
sig { params(type: String).returns(String) }
|
|
70
|
+
def as_nilable_type(type)
|
|
71
|
+
if type.start_with?("T.nilable(") || type == "T.untyped"
|
|
72
|
+
type
|
|
73
|
+
else
|
|
74
|
+
"T.nilable(#{type})"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
sig { params(column_type: Object).returns(String) }
|
|
79
|
+
def handle_unknown_type(column_type)
|
|
80
|
+
return "T.untyped" unless ActiveModel::Type::Value === column_type
|
|
81
|
+
return "T.untyped" if Runtime::GenericTypeRegistry.generic_type_instance?(column_type)
|
|
82
|
+
|
|
83
|
+
lookup_return_type_of_method(column_type, :deserialize) ||
|
|
84
|
+
lookup_return_type_of_method(column_type, :cast) ||
|
|
85
|
+
lookup_arg_type_of_method(column_type, :serialize) ||
|
|
86
|
+
"T.untyped"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
sig { params(column_type: ActiveModel::Type::Value, method: Symbol).returns(T.nilable(String)) }
|
|
90
|
+
def lookup_return_type_of_method(column_type, method)
|
|
91
|
+
signature = Runtime::Reflection.signature_of(column_type.method(method))
|
|
92
|
+
return unless signature
|
|
93
|
+
|
|
94
|
+
return_type = signature.return_type
|
|
95
|
+
return if return_type == T::Private::Types::Void || return_type == T::Private::Types::NotTyped
|
|
96
|
+
|
|
97
|
+
return_type.to_s
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
sig { params(column_type: ActiveModel::Type::Value, method: Symbol).returns(T.nilable(String)) }
|
|
101
|
+
def lookup_arg_type_of_method(column_type, method)
|
|
102
|
+
signature = Runtime::Reflection.signature_of(column_type.method(method))
|
|
103
|
+
return unless signature
|
|
104
|
+
|
|
105
|
+
# Arg types is an array [name, type] entries, so we desctructure the type of
|
|
106
|
+
# first argument to get the first argument type
|
|
107
|
+
_, first_argument_type = signature.arg_types.first
|
|
108
|
+
|
|
109
|
+
first_argument_type.to_s
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Tapioca
|
|
5
|
+
module Dsl
|
|
6
|
+
module Helpers
|
|
7
|
+
module ActiveRecordConstantsHelper
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
ReflectionType = T.type_alias do
|
|
11
|
+
T.any(::ActiveRecord::Reflection::ThroughReflection, ::ActiveRecord::Reflection::AssociationReflection)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
AttributeMethodsModuleName = T.let("GeneratedAttributeMethods", String)
|
|
15
|
+
AssociationMethodsModuleName = T.let("GeneratedAssociationMethods", String)
|
|
16
|
+
|
|
17
|
+
RelationMethodsModuleName = T.let("GeneratedRelationMethods", String)
|
|
18
|
+
AssociationRelationMethodsModuleName = T.let("GeneratedAssociationRelationMethods", String)
|
|
19
|
+
CommonRelationMethodsModuleName = T.let("CommonRelationMethods", String)
|
|
20
|
+
|
|
21
|
+
RelationClassName = T.let("PrivateRelation", String)
|
|
22
|
+
RelationWhereChainClassName = T.let("PrivateRelationWhereChain", String)
|
|
23
|
+
AssociationRelationClassName = T.let("PrivateAssociationRelation", String)
|
|
24
|
+
AssociationRelationWhereChainClassName = T.let("PrivateAssociationRelationWhereChain", String)
|
|
25
|
+
AssociationsCollectionProxyClassName = T.let("PrivateCollectionProxy", String)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|