tapioca 0.13.3 → 0.14.0
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/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 +91 -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: 469c53709eb6cb5de25abc9526d40c8dff3b852bf218bcbbc966078271754bfd
|
4
|
+
data.tar.gz: fbc47526d02ef8fea90ad262007865ee56ab56a72dc5832c375540f42a3d9e11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f64c8009bf0e0d5a4b11595cc0bf9c72c5aca3fee4bcae7da5b036009dc0e27c9c5fc2d921564f9664aef0f2b1a648000e3228902d3d2a8b13338af85773d48e
|
7
|
+
data.tar.gz: b751f2a0618927d616edebad5841333af85cb5c7d773bd8904c119d521ab871a65b3405c72efe56426faccdada8b32204f2e5a56cde3ddc4f067884270f106b4
|
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,10 @@ 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: T.any(String, Symbol), other_fixtures: NilClass).returns(Post) }
|
30
|
+
# sig { params(fixture_name: T.any(String, Symbol), other_fixtures: T.any(String, Symbol))
|
31
|
+
# .returns(T::Array[Post]) }
|
32
|
+
# def posts(fixture_name, *other_fixtures); end
|
29
33
|
# end
|
30
34
|
# ~~~
|
31
35
|
class ActiveRecordFixtures < Compiler
|
@@ -104,10 +108,90 @@ module Tapioca
|
|
104
108
|
|
105
109
|
sig { params(mod: RBI::Scope, name: String).void }
|
106
110
|
def create_fixture_method(mod, name)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
+
return_type = return_type_for_fixture(name)
|
112
|
+
mod << RBI::Method.new(name) do |node|
|
113
|
+
node.add_param("fixture_name")
|
114
|
+
node.add_rest_param("other_fixtures")
|
115
|
+
|
116
|
+
node.add_sig do |sig|
|
117
|
+
sig.add_param("fixture_name", "T.any(String, Symbol)")
|
118
|
+
sig.add_param("other_fixtures", "NilClass")
|
119
|
+
sig.return_type = return_type
|
120
|
+
end
|
121
|
+
|
122
|
+
node.add_sig do |sig|
|
123
|
+
sig.add_param("fixture_name", "T.any(String, Symbol)")
|
124
|
+
sig.add_param("other_fixtures", "T.any(String, Symbol)")
|
125
|
+
sig.return_type = "T::Array[#{return_type}]"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
sig { params(fixture_name: String).returns(String) }
|
131
|
+
def return_type_for_fixture(fixture_name)
|
132
|
+
fixture_class_mapping_from_fixture_files[fixture_name] ||
|
133
|
+
fixture_class_from_fixture_set(fixture_name) ||
|
134
|
+
fixture_class_from_active_record_base_class_mapping[fixture_name] ||
|
135
|
+
"T.untyped"
|
136
|
+
end
|
137
|
+
|
138
|
+
sig { params(fixture_name: String).returns(T.nilable(String)) }
|
139
|
+
def fixture_class_from_fixture_set(fixture_name)
|
140
|
+
# only rails 7.1+ support fixture sets so this is conditional
|
141
|
+
return unless fixture_loader.respond_to?(:fixture_sets)
|
142
|
+
|
143
|
+
model_name_from_fixture_set = T.unsafe(fixture_loader).fixture_sets[fixture_name]
|
144
|
+
return unless model_name_from_fixture_set
|
145
|
+
|
146
|
+
model_name = ActiveRecord::FixtureSet.default_fixture_model_name(model_name_from_fixture_set)
|
147
|
+
return unless Object.const_defined?(model_name)
|
148
|
+
|
149
|
+
model_name
|
150
|
+
end
|
151
|
+
|
152
|
+
sig { returns(T::Hash[String, String]) }
|
153
|
+
def fixture_class_from_active_record_base_class_mapping
|
154
|
+
@fixture_class_mapping ||= T.let(
|
155
|
+
begin
|
156
|
+
ActiveRecord::Base.descendants.each_with_object({}) do |model_class, mapping|
|
157
|
+
class_name = model_class.name
|
158
|
+
|
159
|
+
fixture_name = class_name.underscore.gsub("/", "_")
|
160
|
+
fixture_name = fixture_name.pluralize if ActiveRecord::Base.pluralize_table_names
|
161
|
+
|
162
|
+
mapping[fixture_name] = class_name
|
163
|
+
|
164
|
+
mapping
|
165
|
+
end
|
166
|
+
end,
|
167
|
+
T.nilable(T::Hash[String, String]),
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
sig { returns(T::Hash[String, String]) }
|
172
|
+
def fixture_class_mapping_from_fixture_files
|
173
|
+
@fixture_file_class_mapping ||= T.let(
|
174
|
+
begin
|
175
|
+
fixture_paths = if T.unsafe(fixture_loader).respond_to?(:fixture_paths)
|
176
|
+
T.unsafe(fixture_loader).fixture_paths
|
177
|
+
else
|
178
|
+
T.unsafe(fixture_loader).fixture_path
|
179
|
+
end
|
180
|
+
|
181
|
+
Array(fixture_paths).each_with_object({}) do |path, mapping|
|
182
|
+
Dir["#{path}{.yml,/{**,*}/*.yml}"].select do |file|
|
183
|
+
next unless ::File.file?(file)
|
184
|
+
|
185
|
+
ActiveRecord::FixtureSet::File.open(file) do |fh|
|
186
|
+
next unless fh.model_class
|
187
|
+
|
188
|
+
fixture_name = file.delete_prefix(path.to_s).delete_prefix("/").delete_suffix(".yml")
|
189
|
+
mapping[fixture_name] = fh.model_class
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end,
|
194
|
+
T.nilable(T::Hash[String, String]),
|
111
195
|
)
|
112
196
|
end
|
113
197
|
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.0
|
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-07 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
|