Skip to content

Commit 2df97a2

Browse files
authored
Merge pull request #556 from Shopify/el-fix-sorbet-signature-for-generic-jobs
Update sorbet compiler so that it correctly handles generic jobs
2 parents 3237efe + f768466 commit 2df97a2

File tree

2 files changed

+94
-5
lines changed

2 files changed

+94
-5
lines changed

lib/tapioca/dsl/compilers/job_iteration.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,18 @@ def decorate
5555
# Sorbet expects optional keyword arguments to be after required keyword arguments.
5656
expanded_parameters.sort_by! { |typed_param| PARAM_TYPES_IN_ORDER.index(typed_param.param.class) }
5757

58+
number_of_generic_type_members = Tapioca::Runtime::GenericTypeRegistry.lookup_type_variables(constant)&.size
59+
60+
returned_job_class = if number_of_generic_type_members&.nonzero?
61+
"#{constant_name}[#{number_of_generic_type_members.times.map { "T.untyped" }.join(", ")}]"
62+
else
63+
constant_name
64+
end
65+
5866
job.create_method(
5967
"perform_later",
60-
parameters: perform_later_parameters(expanded_parameters, constant_name),
61-
return_type: "T.any(#{constant_name}, FalseClass)",
68+
parameters: perform_later_parameters(expanded_parameters, returned_job_class),
69+
return_type: "T.any(#{returned_job_class}, FalseClass)",
6270
class_method: true,
6371
)
6472

@@ -83,15 +91,15 @@ def decorate
8391
sig do
8492
params(
8593
parameters: T::Array[RBI::TypedParam],
86-
constant_name: T.nilable(String),
94+
returned_job_class: String,
8795
).returns(T::Array[RBI::TypedParam])
8896
end
89-
def perform_later_parameters(parameters, constant_name)
97+
def perform_later_parameters(parameters, returned_job_class)
9098
if ::Gem::Requirement.new(">= 7.0").satisfied_by?(::ActiveJob.gem_version)
9199
parameters.reject! { |typed_param| RBI::BlockParam === typed_param.param }
92100
parameters + [create_block_param(
93101
"block",
94-
type: "T.nilable(T.proc.params(job: #{constant_name}).void)",
102+
type: "T.nilable(T.proc.params(job: #{returned_job_class}).void)",
95103
)]
96104
else
97105
parameters

test/tapioca/dsl/compilers/job_iteration_test.rb

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,87 @@ def perform_now(shop_ids:, profile_ids:, extension_ids:, foo:, bar:); end
402402
RBI
403403
assert_equal(expected, rbi_for(:NotifyJob))
404404
end
405+
406+
def test_generates_correct_rbi_file_for_generic_jobs_with_one_generic_variable
407+
add_ruby_file("job.rb", <<~RUBY)
408+
class GenericJob < ActiveJob::Base
409+
include JobIteration::Iteration
410+
extend T::Sig
411+
extend T::Generic
412+
413+
Elem = type_member
414+
415+
sig { params(val: Elem, cursor: T.untyped).returns(T::Array[T.untyped]) }
416+
def build_enumerator(val:, cursor:)
417+
# ...
418+
end
419+
end
420+
RUBY
421+
422+
expected = template(<<~RBI)
423+
# typed: strong
424+
425+
class GenericJob
426+
sig { params(val: Elem).void }
427+
def perform(val:); end
428+
429+
class << self
430+
<% if rails_version(">= 7.0") %>
431+
sig { params(val: Elem, block: T.nilable(T.proc.params(job: GenericJob[T.untyped]).void)).returns(T.any(GenericJob[T.untyped], FalseClass)) }
432+
def perform_later(val:, &block); end
433+
<% else %>
434+
sig { params(val: Elem).returns(T.any(GenericJob[T.untyped], FalseClass)) }
435+
def perform_later(val:); end
436+
<% end %>
437+
438+
sig { params(val: Elem).returns(T.any(NilClass, Exception)) }
439+
def perform_now(val:); end
440+
end
441+
end
442+
RBI
443+
assert_equal(expected, rbi_for(:GenericJob))
444+
end
445+
446+
def test_generates_correct_rbi_file_for_generic_jobs_with_multiple_generic_variables
447+
add_ruby_file("job.rb", <<~RUBY)
448+
class GenericJob < ActiveJob::Base
449+
include JobIteration::Iteration
450+
extend T::Sig
451+
extend T::Generic
452+
453+
Elem = type_member
454+
SomeValue = type_member
455+
456+
sig { params(val: Elem, some_value: SomeValue, cursor: T.untyped).returns(T::Array[T.untyped]) }
457+
def build_enumerator(val:, some_value:, cursor:)
458+
# ...
459+
end
460+
end
461+
RUBY
462+
463+
expected = template(<<~RBI)
464+
# typed: strong
465+
466+
class GenericJob
467+
sig { params(val: Elem, some_value: SomeValue).void }
468+
def perform(val:, some_value:); end
469+
470+
class << self
471+
<% if rails_version(">= 7.0") %>
472+
sig { params(val: Elem, some_value: SomeValue, block: T.nilable(T.proc.params(job: GenericJob[T.untyped, T.untyped]).void)).returns(T.any(GenericJob[T.untyped, T.untyped], FalseClass)) }
473+
def perform_later(val:, some_value:, &block); end
474+
<% else %>
475+
sig { params(val: Elem, some_value: SomeValue).returns(T.any(GenericJob[T.untyped, T.untyped], FalseClass)) }
476+
def perform_later(val:, some_value:); end
477+
<% end %>
478+
479+
sig { params(val: Elem, some_value: SomeValue).returns(T.any(NilClass, Exception)) }
480+
def perform_now(val:, some_value:); end
481+
end
482+
end
483+
RBI
484+
assert_equal(expected, rbi_for(:GenericJob))
485+
end
405486
end
406487
end
407488
end

0 commit comments

Comments
 (0)