tapioca 0.13.3 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: feabe451c23d5c95ab011b2996a0f53daee19e20d7bf7075aeb4da7fa812f9a5
4
- data.tar.gz: 1a47b4e7a3942786a0fd064c8c49bb02aae2f79f23bf22a2933a6608ba68ca5e
3
+ metadata.gz: 469c53709eb6cb5de25abc9526d40c8dff3b852bf218bcbbc966078271754bfd
4
+ data.tar.gz: fbc47526d02ef8fea90ad262007865ee56ab56a72dc5832c375540f42a3d9e11
5
5
  SHA512:
6
- metadata.gz: 982979631a9a78a2f6b589be02f2064b94fd6b95675316a2bd2271a21f2366af752c994ade6194d412f5bbabf5666623416e728d2c4d56dd336d5b17d8803f03
7
- data.tar.gz: 1fb7b58f1f361a5a05d25a7dfad6b491efc65c084be0c3e293755e81192ac8e25c8477b1f6f75c12fe0740cf8e8ed38921e5e314b63f54efb7189aa9edd03cd3
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 [writting a custom DSL compiler](#writing-custom-dsl-compilers).
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 Controlller base helper methods module, so we should
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 dynmaic module builder pattern
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) && defined?(ActiveSupport::TestCase) && defined?(ActiveRecord::TestFixtures)
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(fixture_names: T.any(String, Symbol)).returns(T.untyped) }
28
- # def posts(*fixture_names); end
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
- mod.create_method(
108
- name,
109
- parameters: [create_rest_param("fixture_names", type: "T.any(String, Symbol)")],
110
- return_type: "T.untyped",
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::PrivateAssocationRelation` that subclasses `ActiveRecord::AssociationRelation`. This synthetic
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::PrivateAssocationRelation` instance. The difference between this
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::PrivateAssocationRelation` instance.
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, #{method_name == :average ? "Numeric" : "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, Numeric]",
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, Numeric]",
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 = "T.any(String, Symbol, ::ActiveSupport::Multibyte::Chars, T::Boolean, BigDecimal, Numeric, " \
661
- "::ActiveRecord::Type::Binary::Data, ::ActiveRecord::Type::Time::Value, Date, Time, " \
662
- "::ActiveSupport::Duration, T::Class[T.anything])"
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 ? "Numeric" : "T.untyped",
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: "Numeric",
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
- create_common_method(
826
- "sum",
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
- create_opt_param("column_name", type: "T.nilable(T.any(String, Symbol))", default: "nil"),
829
- create_block_param("block", type: "T.nilable(T.proc.params(record: T.untyped).returns(T.untyped))"),
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(reviewd: T::Boolean).returns(T::Boolean) }
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 becuase `Config::Options` is an
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 assinged a hash value. If this goes through, then it's a map type.
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
@@ -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 { params(requested_constants: T::Array[Module], requested_paths: T::Array[Pathname]).returns(T::Set[Module]) }
132
- def gather_constants(requested_constants, requested_paths)
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
- constants &= requested_constants unless requested_constants.empty? && requested_paths.empty?
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 overriden the inherited method yet, just subclass
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 matchs.
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
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.13.3"
5
+ VERSION = "0.14.0"
6
6
  end
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.13.3
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-04-15 00:00:00.000000000 Z
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.0'
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.9
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