tapioca 0.13.3 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/lib/tapioca/cli.rb +6 -0
- data/lib/tapioca/commands/abstract_dsl.rb +4 -0
- data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_associations.rb +10 -0
- data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +98 -7
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +59 -16
- data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +1 -1
- data/lib/tapioca/dsl/compilers/config.rb +1 -1
- data/lib/tapioca/dsl/compilers/protobuf.rb +1 -1
- data/lib/tapioca/dsl/pipeline.rb +25 -4
- data/lib/tapioca/helpers/rbi_helper.rb +9 -0
- data/lib/tapioca/helpers/test/dsl_compiler.rb +23 -1
- data/lib/tapioca/loaders/loader.rb +0 -10
- data/lib/tapioca/rbi_ext/model.rb +3 -2
- data/lib/tapioca/rbi_formatter.rb +2 -0
- data/lib/tapioca/runtime/generic_type_registry.rb +1 -1
- data/lib/tapioca/static/requires_compiler.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9b033940feb12f528b3995f645ed709aa18a3e5397ec188f795df4cdbfb4c57
|
4
|
+
data.tar.gz: 0f38fe25358b09d6ebeb40c582014948bbf06787c947bd17f2bdb7cc0ed9dfd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8feb5a4f612517906add3ef224f13ac73296955c097352349d4bcf52f19951a1b97bc79ecdc184355123c22285eaffb98f8037bcf58be1bb56f60e1607b332b5
|
7
|
+
data.tar.gz: 41caacbc97da0c7dc1c0ee8260ac8d860908138fb709cc7bbe1a0bd8170953daeb6d88a82cad9f8979c28471d5a4d1b84b87c39ff6c7756ef6f152e703c26dc4
|
data/README.md
CHANGED
@@ -500,6 +500,7 @@ Options:
|
|
500
500
|
# Default: .
|
501
501
|
[--halt-upon-load-error], [--no-halt-upon-load-error], [--skip-halt-upon-load-error] # Halt upon a load error while loading the Rails application
|
502
502
|
# Default: true
|
503
|
+
[--skip-constant=constant [constant ...]] # Do not generate RBI definitions for the given application constant(s)
|
503
504
|
-c, [--config=<config file path>] # Path to the Tapioca configuration file
|
504
505
|
# Default: sorbet/tapioca/config.yml
|
505
506
|
-V, [--verbose], [--no-verbose], [--skip-verbose] # Verbose output for debugging purposes
|
@@ -885,7 +886,7 @@ Check duplicated definitions in shim RBIs
|
|
885
886
|
```
|
886
887
|
<!-- END_HELP_COMMAND_CHECK_SHIMS -->
|
887
888
|
|
888
|
-
Depending on the amount of meta-programming used in your project this can mean an overwhelming amount of manual work. In this case, you should consider [
|
889
|
+
Depending on the amount of meta-programming used in your project this can mean an overwhelming amount of manual work. In this case, you should consider [writing a custom DSL compiler](#writing-custom-dsl-compilers).
|
889
890
|
|
890
891
|
### Configuration
|
891
892
|
|
@@ -933,6 +934,7 @@ dsl:
|
|
933
934
|
list_compilers: false
|
934
935
|
app_root: "."
|
935
936
|
halt_upon_load_error: true
|
937
|
+
skip_constant: []
|
936
938
|
gem:
|
937
939
|
outdir: sorbet/rbi/gems
|
938
940
|
file_header: true
|
data/lib/tapioca/cli.rb
CHANGED
@@ -135,6 +135,11 @@ module Tapioca
|
|
135
135
|
type: :boolean,
|
136
136
|
desc: "Halt upon a load error while loading the Rails application",
|
137
137
|
default: true
|
138
|
+
option :skip_constant,
|
139
|
+
type: :array,
|
140
|
+
banner: "constant [constant ...]",
|
141
|
+
desc: "Do not generate RBI definitions for the given application constant(s)",
|
142
|
+
default: []
|
138
143
|
def dsl(*constant_or_paths)
|
139
144
|
set_environment(options)
|
140
145
|
|
@@ -149,6 +154,7 @@ module Tapioca
|
|
149
154
|
exclude: options[:exclude],
|
150
155
|
file_header: options[:file_header],
|
151
156
|
tapioca_path: TAPIOCA_DIR,
|
157
|
+
skip_constant: options[:skip_constant],
|
152
158
|
quiet: options[:quiet],
|
153
159
|
verbose: options[:verbose],
|
154
160
|
number_of_workers: options[:workers],
|
@@ -18,6 +18,7 @@ module Tapioca
|
|
18
18
|
exclude: T::Array[String],
|
19
19
|
file_header: T::Boolean,
|
20
20
|
tapioca_path: String,
|
21
|
+
skip_constant: T::Array[String],
|
21
22
|
quiet: T::Boolean,
|
22
23
|
verbose: T::Boolean,
|
23
24
|
number_of_workers: T.nilable(Integer),
|
@@ -36,6 +37,7 @@ module Tapioca
|
|
36
37
|
exclude:,
|
37
38
|
file_header:,
|
38
39
|
tapioca_path:,
|
40
|
+
skip_constant: [],
|
39
41
|
quiet: false,
|
40
42
|
verbose: false,
|
41
43
|
number_of_workers: nil,
|
@@ -60,6 +62,7 @@ module Tapioca
|
|
60
62
|
@rbi_formatter = rbi_formatter
|
61
63
|
@app_root = app_root
|
62
64
|
@halt_upon_load_error = halt_upon_load_error
|
65
|
+
@skip_constant = skip_constant
|
63
66
|
|
64
67
|
super()
|
65
68
|
end
|
@@ -124,6 +127,7 @@ module Tapioca
|
|
124
127
|
error_handler: ->(error) {
|
125
128
|
say_error(error, :bold, :red)
|
126
129
|
},
|
130
|
+
skipped_constants: constantize(@skip_constant),
|
127
131
|
number_of_workers: @number_of_workers,
|
128
132
|
)
|
129
133
|
end
|
@@ -81,7 +81,7 @@ module Tapioca
|
|
81
81
|
# Create helper method module
|
82
82
|
controller.create_module(helper_methods_name) do |helper_methods|
|
83
83
|
# If the controller has no helper defined, then it just inherits
|
84
|
-
# the Action
|
84
|
+
# the Action Controller base helper methods module, so we should
|
85
85
|
# just add that as an include and stop doing more processing.
|
86
86
|
if helpers_module.name == "ActionController::Base::HelperMethods"
|
87
87
|
next helper_methods.create_include(T.must(qualified_name_of(helpers_module)))
|
@@ -66,7 +66,7 @@ module Tapioca
|
|
66
66
|
# pre Rails 6.0, this used to be a single static module
|
67
67
|
[ActiveModel::SecurePassword::InstanceMethodsOnActivation]
|
68
68
|
else
|
69
|
-
# post Rails 6.0, this is now using a
|
69
|
+
# post Rails 6.0, this is now using a dynamic module builder pattern
|
70
70
|
# and we can have multiple different ones included into the model
|
71
71
|
constant.ancestors.grep(ActiveModel::SecurePassword::InstanceMethodsOnActivation)
|
72
72
|
end
|
@@ -92,6 +92,12 @@ module Tapioca
|
|
92
92
|
#
|
93
93
|
# sig { returns(T.nilable(::Category)) }
|
94
94
|
# def reload_category; end
|
95
|
+
#
|
96
|
+
# sig { void }
|
97
|
+
# def reset_author; end
|
98
|
+
#
|
99
|
+
# sig { void }
|
100
|
+
# def reset_category; end
|
95
101
|
# end
|
96
102
|
# end
|
97
103
|
# ~~~
|
@@ -196,6 +202,10 @@ module Tapioca
|
|
196
202
|
"reload_#{association_name}",
|
197
203
|
return_type: association_type,
|
198
204
|
)
|
205
|
+
klass.create_method(
|
206
|
+
"reset_#{association_name}",
|
207
|
+
return_type: "void",
|
208
|
+
)
|
199
209
|
unless reflection.polymorphic?
|
200
210
|
klass.create_method(
|
201
211
|
"build_#{association_name}",
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
return unless defined?(Rails) &&
|
4
|
+
return unless defined?(Rails) &&
|
5
|
+
defined?(ActiveSupport::TestCase) &&
|
6
|
+
defined?(ActiveRecord::TestFixtures)
|
5
7
|
|
6
8
|
module Tapioca
|
7
9
|
module Dsl
|
@@ -24,8 +26,11 @@ module Tapioca
|
|
24
26
|
# # test_case.rbi
|
25
27
|
# # typed: true
|
26
28
|
# class ActiveSupport::TestCase
|
27
|
-
# sig { params(
|
28
|
-
#
|
29
|
+
# sig { params(fixture_name: NilClass, other_fixtures: NilClass).returns(T::Array[Post]) }
|
30
|
+
# sig { params(fixture_name: T.any(String, Symbol), other_fixtures: NilClass).returns(Post) }
|
31
|
+
# sig { params(fixture_name: T.any(String, Symbol), other_fixtures: T.any(String, Symbol))
|
32
|
+
# .returns(T::Array[Post]) }
|
33
|
+
# def posts(fixture_name = nil, *other_fixtures); end
|
29
34
|
# end
|
30
35
|
# ~~~
|
31
36
|
class ActiveRecordFixtures < Compiler
|
@@ -104,10 +109,96 @@ module Tapioca
|
|
104
109
|
|
105
110
|
sig { params(mod: RBI::Scope, name: String).void }
|
106
111
|
def create_fixture_method(mod, name)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
112
|
+
return_type = return_type_for_fixture(name)
|
113
|
+
mod << RBI::Method.new(name) do |node|
|
114
|
+
node.add_opt_param("fixture_name", "nil")
|
115
|
+
node.add_rest_param("other_fixtures")
|
116
|
+
|
117
|
+
node.add_sig do |sig|
|
118
|
+
sig.add_param("fixture_name", "NilClass")
|
119
|
+
sig.add_param("other_fixtures", "NilClass")
|
120
|
+
sig.return_type = "T::Array[#{return_type}]"
|
121
|
+
end
|
122
|
+
|
123
|
+
node.add_sig do |sig|
|
124
|
+
sig.add_param("fixture_name", "T.any(String, Symbol)")
|
125
|
+
sig.add_param("other_fixtures", "NilClass")
|
126
|
+
sig.return_type = return_type
|
127
|
+
end
|
128
|
+
|
129
|
+
node.add_sig do |sig|
|
130
|
+
sig.add_param("fixture_name", "T.any(String, Symbol)")
|
131
|
+
sig.add_param("other_fixtures", "T.any(String, Symbol)")
|
132
|
+
sig.return_type = "T::Array[#{return_type}]"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
sig { params(fixture_name: String).returns(String) }
|
138
|
+
def return_type_for_fixture(fixture_name)
|
139
|
+
fixture_class_mapping_from_fixture_files[fixture_name] ||
|
140
|
+
fixture_class_from_fixture_set(fixture_name) ||
|
141
|
+
fixture_class_from_active_record_base_class_mapping[fixture_name] ||
|
142
|
+
"T.untyped"
|
143
|
+
end
|
144
|
+
|
145
|
+
sig { params(fixture_name: String).returns(T.nilable(String)) }
|
146
|
+
def fixture_class_from_fixture_set(fixture_name)
|
147
|
+
# only rails 7.1+ support fixture sets so this is conditional
|
148
|
+
return unless fixture_loader.respond_to?(:fixture_sets)
|
149
|
+
|
150
|
+
model_name_from_fixture_set = T.unsafe(fixture_loader).fixture_sets[fixture_name]
|
151
|
+
return unless model_name_from_fixture_set
|
152
|
+
|
153
|
+
model_name = ActiveRecord::FixtureSet.default_fixture_model_name(model_name_from_fixture_set)
|
154
|
+
return unless Object.const_defined?(model_name)
|
155
|
+
|
156
|
+
model_name
|
157
|
+
end
|
158
|
+
|
159
|
+
sig { returns(T::Hash[String, String]) }
|
160
|
+
def fixture_class_from_active_record_base_class_mapping
|
161
|
+
@fixture_class_mapping ||= T.let(
|
162
|
+
begin
|
163
|
+
ActiveRecord::Base.descendants.each_with_object({}) do |model_class, mapping|
|
164
|
+
class_name = model_class.name
|
165
|
+
|
166
|
+
fixture_name = class_name.underscore.gsub("/", "_")
|
167
|
+
fixture_name = fixture_name.pluralize if ActiveRecord::Base.pluralize_table_names
|
168
|
+
|
169
|
+
mapping[fixture_name] = class_name
|
170
|
+
|
171
|
+
mapping
|
172
|
+
end
|
173
|
+
end,
|
174
|
+
T.nilable(T::Hash[String, String]),
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
sig { returns(T::Hash[String, String]) }
|
179
|
+
def fixture_class_mapping_from_fixture_files
|
180
|
+
@fixture_file_class_mapping ||= T.let(
|
181
|
+
begin
|
182
|
+
fixture_paths = if T.unsafe(fixture_loader).respond_to?(:fixture_paths)
|
183
|
+
T.unsafe(fixture_loader).fixture_paths
|
184
|
+
else
|
185
|
+
T.unsafe(fixture_loader).fixture_path
|
186
|
+
end
|
187
|
+
|
188
|
+
Array(fixture_paths).each_with_object({}) do |path, mapping|
|
189
|
+
Dir["#{path}{.yml,/{**,*}/*.yml}"].select do |file|
|
190
|
+
next unless ::File.file?(file)
|
191
|
+
|
192
|
+
ActiveRecord::FixtureSet::File.open(file) do |fh|
|
193
|
+
next unless fh.model_class
|
194
|
+
|
195
|
+
fixture_name = file.delete_prefix(path.to_s).delete_prefix("/").delete_suffix(".yml")
|
196
|
+
mapping[fixture_name] = fh.model_class
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end,
|
201
|
+
T.nilable(T::Hash[String, String]),
|
111
202
|
)
|
112
203
|
end
|
113
204
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
return unless defined?(ActiveRecord::Base)
|
5
5
|
|
6
|
+
require "tapioca/dsl/helpers/active_model_type_helper"
|
6
7
|
require "tapioca/dsl/helpers/active_record_constants_helper"
|
7
8
|
|
8
9
|
module Tapioca
|
@@ -24,15 +25,15 @@ module Tapioca
|
|
24
25
|
# 1. A `Model::PrivateRelation` that subclasses `ActiveRecord::Relation`. This synthetic class represents
|
25
26
|
# a relation on `Model` whose methods which return a relation always return a `Model::PrivateRelation` instance.
|
26
27
|
#
|
27
|
-
# 2. `Model::
|
28
|
+
# 2. `Model::PrivateAssociationRelation` that subclasses `ActiveRecord::AssociationRelation`. This synthetic
|
28
29
|
# class represents a relation on a singular association of type `Model` (e.g. `foo.model`) whose methods which
|
29
|
-
# return a relation will always return a `Model::
|
30
|
+
# return a relation will always return a `Model::PrivateAssociationRelation` instance. The difference between this
|
30
31
|
# class and the previous one is mainly that an association relation also keeps track of the resource association
|
31
32
|
# for this relation.
|
32
33
|
#
|
33
34
|
# 3. `Model::PrivateCollectionProxy` that subclasses from `ActiveRecord::Associations::CollectionProxy`.
|
34
35
|
# This synthetic class represents a relation on a plural association of type `Model` (e.g. `foo.models`)
|
35
|
-
# whose methods which return a relation will always return a `Model::
|
36
|
+
# whose methods which return a relation will always return a `Model::PrivateAssociationRelation` instance.
|
36
37
|
# This class represents a collection of `Model` instances with some extra methods to `build`, `create`,
|
37
38
|
# etc new `Model` instances in the collection.
|
38
39
|
#
|
@@ -364,7 +365,8 @@ module Tapioca
|
|
364
365
|
parameters: [
|
365
366
|
create_param("column_name", type: "T.any(String, Symbol)"),
|
366
367
|
],
|
367
|
-
return_type: "T::Hash[T.untyped,
|
368
|
+
return_type: "T::Hash[T.untyped, " \
|
369
|
+
"#{method_name == :average ? "T.any(Integer, Float, BigDecimal)" : "T.untyped"}]",
|
368
370
|
)
|
369
371
|
when :calculate
|
370
372
|
klass.create_method(
|
@@ -373,7 +375,7 @@ module Tapioca
|
|
373
375
|
create_param("operation", type: "Symbol"),
|
374
376
|
create_param("column_name", type: "T.any(String, Symbol)"),
|
375
377
|
],
|
376
|
-
return_type: "T::Hash[T.untyped,
|
378
|
+
return_type: "T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]",
|
377
379
|
)
|
378
380
|
when :count
|
379
381
|
klass.create_method(
|
@@ -390,7 +392,7 @@ module Tapioca
|
|
390
392
|
create_opt_param("column_name", type: "T.nilable(T.any(String, Symbol))", default: "nil"),
|
391
393
|
create_block_param("block", type: "T.nilable(T.proc.params(record: T.untyped).returns(T.untyped))"),
|
392
394
|
],
|
393
|
-
return_type: "T::Hash[T.untyped,
|
395
|
+
return_type: "T::Hash[T.untyped, T.any(Integer, Float, BigDecimal)]",
|
394
396
|
)
|
395
397
|
end
|
396
398
|
end
|
@@ -560,6 +562,11 @@ module Tapioca
|
|
560
562
|
|
561
563
|
QUERY_METHODS.each do |method_name|
|
562
564
|
case method_name
|
565
|
+
when :distinct
|
566
|
+
create_relation_method(
|
567
|
+
method_name.to_s,
|
568
|
+
parameters: [create_opt_param("value", type: "T::Boolean", default: "true")],
|
569
|
+
)
|
563
570
|
when :extract_associated
|
564
571
|
parameters = [create_param("association", type: "Symbol")]
|
565
572
|
return_type = "T::Array[T.untyped]"
|
@@ -657,9 +664,30 @@ module Tapioca
|
|
657
664
|
)
|
658
665
|
when :find
|
659
666
|
# From ActiveRecord::ConnectionAdapter::Quoting#quote, minus nil
|
660
|
-
id_types =
|
661
|
-
"
|
662
|
-
"
|
667
|
+
id_types = [
|
668
|
+
"String",
|
669
|
+
"Symbol",
|
670
|
+
"::ActiveSupport::Multibyte::Chars",
|
671
|
+
"T::Boolean",
|
672
|
+
"BigDecimal",
|
673
|
+
"Numeric",
|
674
|
+
"::ActiveRecord::Type::Binary::Data",
|
675
|
+
"::ActiveRecord::Type::Time::Value",
|
676
|
+
"Date",
|
677
|
+
"Time",
|
678
|
+
"::ActiveSupport::Duration",
|
679
|
+
"T::Class[T.anything]",
|
680
|
+
].to_set
|
681
|
+
|
682
|
+
if constant.table_exists?
|
683
|
+
primary_key_type = constant.type_for_attribute(constant.primary_key)
|
684
|
+
type = Tapioca::Dsl::Helpers::ActiveModelTypeHelper.type_for(primary_key_type)
|
685
|
+
type = RBIHelper.as_non_nilable_type(type)
|
686
|
+
id_types << type if type != "T.untyped"
|
687
|
+
end
|
688
|
+
|
689
|
+
id_types = "T.any(#{id_types.to_a.join(", ")})"
|
690
|
+
|
663
691
|
array_type = if constant.try(:composite_primary_key?)
|
664
692
|
"T::Array[T::Array[#{id_types}]]"
|
665
693
|
else
|
@@ -781,7 +809,7 @@ module Tapioca
|
|
781
809
|
parameters: [
|
782
810
|
create_param("column_name", type: "T.any(String, Symbol)"),
|
783
811
|
],
|
784
|
-
return_type: method_name == :average ? "
|
812
|
+
return_type: method_name == :average ? "T.any(Integer, Float, BigDecimal)" : "T.untyped",
|
785
813
|
)
|
786
814
|
when :calculate
|
787
815
|
create_common_method(
|
@@ -790,7 +818,7 @@ module Tapioca
|
|
790
818
|
create_param("operation", type: "Symbol"),
|
791
819
|
create_param("column_name", type: "T.any(String, Symbol)"),
|
792
820
|
],
|
793
|
-
return_type: "
|
821
|
+
return_type: "T.any(Integer, Float, BigDecimal)",
|
794
822
|
)
|
795
823
|
when :count
|
796
824
|
sigs = [
|
@@ -822,13 +850,28 @@ module Tapioca
|
|
822
850
|
return_type: "T.untyped",
|
823
851
|
)
|
824
852
|
when :sum
|
825
|
-
|
826
|
-
|
853
|
+
sigs = [
|
854
|
+
common_relation_methods_module.create_sig(
|
855
|
+
parameters: { initial_value_or_column: "T.untyped" },
|
856
|
+
return_type: "T.any(Integer, Float, BigDecimal)",
|
857
|
+
),
|
858
|
+
common_relation_methods_module.create_sig(
|
859
|
+
type_parameters: ["U"],
|
860
|
+
parameters:
|
861
|
+
{
|
862
|
+
initial_value_or_column: "T.nilable(T.type_parameter(:U))",
|
863
|
+
block: "T.proc.params(object: #{constant_name}).returns(T.type_parameter(:U))",
|
864
|
+
},
|
865
|
+
return_type: "T.type_parameter(:U)",
|
866
|
+
),
|
867
|
+
]
|
868
|
+
common_relation_methods_module.create_method_with_sigs(
|
869
|
+
method_name.to_s,
|
870
|
+
sigs: sigs,
|
827
871
|
parameters: [
|
828
|
-
|
829
|
-
|
872
|
+
RBI::OptParam.new("initial_value_or_column", "nil"),
|
873
|
+
RBI::BlockParam.new("block"),
|
830
874
|
],
|
831
|
-
return_type: "Numeric",
|
832
875
|
)
|
833
876
|
end
|
834
877
|
end
|
@@ -55,7 +55,7 @@ module Tapioca
|
|
55
55
|
# sig { returns(T.nilable([T.nilable(Date), T.nilable(Date)])) }
|
56
56
|
# def saved_change_to_review_date; end
|
57
57
|
#
|
58
|
-
# sig { params(
|
58
|
+
# sig { params(reviewed: T::Boolean).returns(T::Boolean) }
|
59
59
|
# def reviewed=(reviewed); end
|
60
60
|
#
|
61
61
|
# sig { returns(T::Boolean) }
|
@@ -66,7 +66,7 @@ module Tapioca
|
|
66
66
|
root.create_constant(config_constant_name, value: "T.let(T.unsafe(nil), #{option_class_name})")
|
67
67
|
|
68
68
|
root.create_class(option_class_name, superclass_name: "::Config::Options") do |mod|
|
69
|
-
# We need this to be generic only
|
69
|
+
# We need this to be generic only because `Config::Options` is an
|
70
70
|
# enumerable and, thus, needs to redeclare the `Elem` type member.
|
71
71
|
#
|
72
72
|
# We declare it as a fixed member of `T.untyped` so that if anyone
|
@@ -239,7 +239,7 @@ module Tapioca
|
|
239
239
|
return false unless descriptor.label == :repeated
|
240
240
|
|
241
241
|
# Try to create a new instance with the field that maps to the descriptor name
|
242
|
-
# being
|
242
|
+
# being assigned a hash value. If this goes through, then it's a map type.
|
243
243
|
constant.new(**{ descriptor.name => {} })
|
244
244
|
true
|
245
245
|
rescue ArgumentError
|
data/lib/tapioca/dsl/pipeline.rb
CHANGED
@@ -15,6 +15,9 @@ module Tapioca
|
|
15
15
|
sig { returns(T::Array[Pathname]) }
|
16
16
|
attr_reader :requested_paths
|
17
17
|
|
18
|
+
sig { returns(T::Array[Module]) }
|
19
|
+
attr_reader :skipped_constants
|
20
|
+
|
18
21
|
sig { returns(T.proc.params(error: String).void) }
|
19
22
|
attr_reader :error_handler
|
20
23
|
|
@@ -28,6 +31,7 @@ module Tapioca
|
|
28
31
|
requested_compilers: T::Array[T.class_of(Compiler)],
|
29
32
|
excluded_compilers: T::Array[T.class_of(Compiler)],
|
30
33
|
error_handler: T.proc.params(error: String).void,
|
34
|
+
skipped_constants: T::Array[Module],
|
31
35
|
number_of_workers: T.nilable(Integer),
|
32
36
|
).void
|
33
37
|
end
|
@@ -37,6 +41,7 @@ module Tapioca
|
|
37
41
|
requested_compilers: [],
|
38
42
|
excluded_compilers: [],
|
39
43
|
error_handler: $stderr.method(:puts).to_proc,
|
44
|
+
skipped_constants: [],
|
40
45
|
number_of_workers: nil
|
41
46
|
)
|
42
47
|
@active_compilers = T.let(
|
@@ -46,6 +51,7 @@ module Tapioca
|
|
46
51
|
@requested_constants = requested_constants
|
47
52
|
@requested_paths = requested_paths
|
48
53
|
@error_handler = error_handler
|
54
|
+
@skipped_constants = skipped_constants
|
49
55
|
@number_of_workers = number_of_workers
|
50
56
|
@errors = T.let([], T::Array[String])
|
51
57
|
end
|
@@ -56,7 +62,7 @@ module Tapioca
|
|
56
62
|
).returns(T::Array[T.type_parameter(:T)])
|
57
63
|
end
|
58
64
|
def run(&blk)
|
59
|
-
constants_to_process = gather_constants(requested_constants, requested_paths)
|
65
|
+
constants_to_process = gather_constants(requested_constants, requested_paths, skipped_constants)
|
60
66
|
.select { |c| Module === c } # Filter value constants out
|
61
67
|
.sort_by! { |c| T.must(Runtime::Reflection.name_of(c)) }
|
62
68
|
|
@@ -128,15 +134,30 @@ module Tapioca
|
|
128
134
|
active_compilers
|
129
135
|
end
|
130
136
|
|
131
|
-
sig
|
132
|
-
|
137
|
+
sig do
|
138
|
+
params(
|
139
|
+
requested_constants: T::Array[Module],
|
140
|
+
requested_paths: T::Array[Pathname],
|
141
|
+
skipped_constants: T::Array[Module],
|
142
|
+
).returns(T::Set[Module])
|
143
|
+
end
|
144
|
+
def gather_constants(requested_constants, requested_paths, skipped_constants)
|
133
145
|
constants = Set.new.compare_by_identity
|
134
146
|
active_compilers.each do |compiler|
|
135
147
|
constants.merge(compiler.processable_constants)
|
136
148
|
end
|
137
149
|
constants = filter_anonymous_and_reloaded_constants(constants)
|
150
|
+
constants -= skipped_constants
|
138
151
|
|
139
|
-
|
152
|
+
unless requested_constants.empty? && requested_paths.empty?
|
153
|
+
constants &= requested_constants
|
154
|
+
|
155
|
+
requested_and_skipped = requested_constants & skipped_constants
|
156
|
+
if requested_and_skipped.any?
|
157
|
+
$stderr.puts("WARNING: Requested constants are being skipped due to configuration:" \
|
158
|
+
"#{requested_and_skipped}. Check the supplied arguments and your `sorbet/tapioca/config.yml` file.")
|
159
|
+
end
|
160
|
+
end
|
140
161
|
constants
|
141
162
|
end
|
142
163
|
|
@@ -96,6 +96,15 @@ module Tapioca
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
sig { params(type: String).returns(String) }
|
100
|
+
def as_non_nilable_type(type)
|
101
|
+
if type.match(/\A(?:::)?T.nilable\((.+)\)\z/)
|
102
|
+
T.must(::Regexp.last_match(1))
|
103
|
+
else
|
104
|
+
type
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
99
108
|
sig { params(name: String).returns(T::Boolean) }
|
100
109
|
def valid_method_name?(name)
|
101
110
|
# try to parse a method definition with this name
|
@@ -4,6 +4,7 @@
|
|
4
4
|
require "tapioca/helpers/test/content"
|
5
5
|
require "tapioca/helpers/test/isolation"
|
6
6
|
require "tapioca/helpers/test/template"
|
7
|
+
require "tapioca/helpers/sorbet_helper"
|
7
8
|
|
8
9
|
module Tapioca
|
9
10
|
module Helpers
|
@@ -53,6 +54,8 @@ module Tapioca
|
|
53
54
|
class CompilerContext
|
54
55
|
extend T::Sig
|
55
56
|
|
57
|
+
include SorbetHelper
|
58
|
+
|
56
59
|
sig { returns(T.class_of(Tapioca::Dsl::Compiler)) }
|
57
60
|
attr_reader :compiler_class
|
58
61
|
|
@@ -95,7 +98,26 @@ module Tapioca
|
|
95
98
|
compiler = compiler_class.new(pipeline, file.root, constant)
|
96
99
|
compiler.decorate
|
97
100
|
|
98
|
-
Tapioca::DEFAULT_RBI_FORMATTER.print_file(file)
|
101
|
+
rbi = Tapioca::DEFAULT_RBI_FORMATTER.print_file(file)
|
102
|
+
result = sorbet(
|
103
|
+
"--no-config",
|
104
|
+
"--stop-after",
|
105
|
+
"parser",
|
106
|
+
"-e",
|
107
|
+
"\"#{rbi}\"",
|
108
|
+
)
|
109
|
+
|
110
|
+
unless result.status
|
111
|
+
raise(SyntaxError, <<~MSG)
|
112
|
+
Expected generated RBI file for `#{constant_name}` to not have any parsing errors.
|
113
|
+
|
114
|
+
Got these parsing errors:
|
115
|
+
|
116
|
+
#{result.err}
|
117
|
+
MSG
|
118
|
+
end
|
119
|
+
|
120
|
+
rbi
|
99
121
|
end
|
100
122
|
|
101
123
|
sig { returns(T::Array[String]) }
|
@@ -49,8 +49,6 @@ module Tapioca
|
|
49
49
|
def load_rails_application(environment_load: false, eager_load: false, app_root: ".", halt_upon_load_error: true)
|
50
50
|
return unless File.exist?("#{app_root}/config/application.rb")
|
51
51
|
|
52
|
-
silence_deprecations
|
53
|
-
|
54
52
|
if environment_load
|
55
53
|
require "./#{app_root}/config/environment"
|
56
54
|
else
|
@@ -186,14 +184,6 @@ module Tapioca
|
|
186
184
|
nil
|
187
185
|
end
|
188
186
|
|
189
|
-
sig { void }
|
190
|
-
def silence_deprecations
|
191
|
-
# Stop any ActiveSupport Deprecations from being reported
|
192
|
-
if defined?(ActiveSupport::Deprecation)
|
193
|
-
ActiveSupport::Deprecation.silenced = true
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
187
|
sig { void }
|
198
188
|
def eager_load_rails_app
|
199
189
|
application = Rails.application
|
@@ -128,14 +128,15 @@ module RBI
|
|
128
128
|
sig do
|
129
129
|
params(
|
130
130
|
parameters: T::Hash[T.any(String, Symbol), String],
|
131
|
+
type_parameters: T::Array[String],
|
131
132
|
return_type: String,
|
132
133
|
).returns(RBI::Sig)
|
133
134
|
end
|
134
|
-
def create_sig(parameters:, return_type: "T.untyped")
|
135
|
+
def create_sig(parameters:, type_parameters: [], return_type: "T.untyped")
|
135
136
|
params = parameters.map do |name, type|
|
136
137
|
RBI::SigParam.new(name.to_s, type)
|
137
138
|
end
|
138
|
-
RBI::Sig.new(params: params, return_type: return_type)
|
139
|
+
RBI::Sig.new(type_params: type_parameters, params: params, return_type: return_type)
|
139
140
|
end
|
140
141
|
|
141
142
|
private
|
@@ -16,6 +16,8 @@ module Tapioca
|
|
16
16
|
file.comments << RBI::Comment.new("DO NOT EDIT MANUALLY")
|
17
17
|
file.comments << RBI::Comment.new("This is an autogenerated file for #{reason}.") unless reason.nil?
|
18
18
|
file.comments << RBI::Comment.new("Please instead update this file by running `#{command}`.")
|
19
|
+
# Prevent the header from being attached to the top-level node when generating YARD docs
|
20
|
+
file.comments << RBI::BlankLine.new
|
19
21
|
end
|
20
22
|
|
21
23
|
sig { params(file: RBI::File).void }
|
@@ -158,7 +158,7 @@ module Tapioca
|
|
158
158
|
# and the module that defines it
|
159
159
|
owner = inherited_method.owner
|
160
160
|
|
161
|
-
# If no one has
|
161
|
+
# If no one has overridden the inherited method yet, just subclass
|
162
162
|
return Class.new(constant) if Class == owner
|
163
163
|
|
164
164
|
begin
|
@@ -60,7 +60,7 @@ module Tapioca
|
|
60
60
|
#
|
61
61
|
# Strings beginning with / match against the
|
62
62
|
# prefix of these relative paths; others are
|
63
|
-
# substring
|
63
|
+
# substring matches.
|
64
64
|
|
65
65
|
# Matches must be against whole folder and file
|
66
66
|
# names, so `foo` matches `/foo/bar.rb` and
|
data/lib/tapioca/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tapioca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2024-
|
14
|
+
date: 2024-05-08 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -281,14 +281,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
281
281
|
requirements:
|
282
282
|
- - ">="
|
283
283
|
- !ruby/object:Gem::Version
|
284
|
-
version: '3.
|
284
|
+
version: '3.1'
|
285
285
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
286
286
|
requirements:
|
287
287
|
- - ">="
|
288
288
|
- !ruby/object:Gem::Version
|
289
289
|
version: '0'
|
290
290
|
requirements: []
|
291
|
-
rubygems_version: 3.5.
|
291
|
+
rubygems_version: 3.5.10
|
292
292
|
signing_key:
|
293
293
|
specification_version: 4
|
294
294
|
summary: A Ruby Interface file generator for gems, core types and the Ruby standard
|