tapioca 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +84 -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 +10 -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 +11 -11
- 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 +10 -8
- 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 +10 -8
- 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 +16 -14
- 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 +2 -2
- data/lib/tapioca/{compilers/dsl_compiler.rb → dsl/pipeline.rb} +41 -33
- 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/rbi_helper.rb +17 -0
- data/lib/tapioca/helpers/shims_helper.rb +87 -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/internal.rb +17 -10
- data/lib/tapioca/rbi_ext/model.rb +2 -48
- 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 +166 -0
- data/lib/tapioca/runtime/loader.rb +123 -0
- data/lib/tapioca/runtime/reflection.rb +153 -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 +33 -15
- 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 +80 -60
- 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/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,10 @@ module Tapioca
|
|
70
70
|
|
71
71
|
extend T::Sig
|
72
72
|
|
73
|
-
|
74
|
-
|
73
|
+
ConstantType = type_member(fixed: Module)
|
74
|
+
|
75
|
+
sig { override.void }
|
76
|
+
def decorate
|
75
77
|
root.create_path(constant) do |klass|
|
76
78
|
if constant == Google::Protobuf::RepeatedField
|
77
79
|
create_type_members(klass, "Elem")
|
@@ -92,7 +94,7 @@ module Tapioca
|
|
92
94
|
end
|
93
95
|
|
94
96
|
sig { override.returns(T::Enumerable[Module]) }
|
95
|
-
def gather_constants
|
97
|
+
def self.gather_constants
|
96
98
|
marker = Google::Protobuf::MessageExts::ClassMethods
|
97
99
|
results = T.cast(ObjectSpace.each_object(marker).to_a, T::Array[Module])
|
98
100
|
results.any? ? results + [Google::Protobuf::RepeatedField, Google::Protobuf::Map] : []
|
@@ -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,7 @@ 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
|
110
112
|
Object.const_set(:GeneratedUrlHelpersModule, Rails.application.routes.named_routes.url_helpers_module)
|
111
113
|
Object.const_set(:GeneratedPathHelpersModule, Rails.application.routes.named_routes.path_helpers_module)
|
112
114
|
|
@@ -140,8 +142,8 @@ module Tapioca
|
|
140
142
|
end
|
141
143
|
end
|
142
144
|
|
143
|
-
sig { params(mod: RBI::Scope,
|
144
|
-
def create_mixins_for(mod,
|
145
|
+
sig { params(mod: RBI::Scope, helper_module: Module).void }
|
146
|
+
def create_mixins_for(mod, helper_module)
|
145
147
|
include_helper = constant.ancestors.include?(helper_module) || NON_DISCOVERABLE_INCLUDERS.include?(constant)
|
146
148
|
extend_helper = constant.singleton_class.ancestors.include?(helper_module)
|
147
149
|
|
@@ -150,7 +152,7 @@ module Tapioca
|
|
150
152
|
end
|
151
153
|
|
152
154
|
sig { params(mod: Module, helper: Module).returns(T::Boolean) }
|
153
|
-
def includes_helper?(mod, helper)
|
155
|
+
private_class_method def self.includes_helper?(mod, helper)
|
154
156
|
superclass_ancestors = []
|
155
157
|
|
156
158
|
if Class === mod
|
@@ -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
|