Skip to content

Commit

Permalink
Add Tapioca compiler for JobIteration
Browse files Browse the repository at this point in the history
It takes the job's `build_enumerator` method and generates the signatures
for the job's corresponding `perform_later` and `perform_now` methods.
  • Loading branch information
st0012 committed Jan 15, 2025
1 parent 7c80841 commit c76569d
Show file tree
Hide file tree
Showing 4 changed files with 481 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ gem "csv" # required for Ruby 3.4+

# for unit testing optional sorbet support
gem "sorbet-runtime"

gem "tapioca"
33 changes: 33 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ GEM
tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0)
benchmark (0.4.0)
bigdecimal (3.1.9)
coderay (1.1.3)
concurrent-ruby (1.3.4)
connection_pool (2.4.1)
csv (3.3.2)
drb (2.2.1)
erubi (1.13.1)
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.6)
Expand All @@ -54,17 +56,22 @@ GEM
mono_logger (1.1.2)
multi_json (1.15.0)
mutex_m (0.2.0)
netrc (0.11.0)
parallel (1.25.1)
parser (3.3.3.0)
ast (~> 2.4.1)
racc
prism (1.3.0)
pry (0.15.2)
coderay (~> 1.1)
method_source (~> 1.0)
racc (1.8.0)
rack (3.1.8)
rainbow (3.1.1)
rake (13.2.1)
rbi (0.2.3)
prism (~> 1.0)
sorbet-runtime (>= 0.5.9204)
redis (5.3.0)
redis-client (>= 0.22.0)
redis-client (0.23.0)
Expand Down Expand Up @@ -102,12 +109,37 @@ GEM
redis-client (>= 0.19.0)
sinatra (1.0)
rack (>= 1.0)
sorbet (0.5.11460)
sorbet-static (= 0.5.11460)
sorbet-runtime (0.5.11460)
sorbet-static (0.5.11460-universal-darwin)
sorbet-static-and-runtime (0.5.11460)
sorbet (= 0.5.11460)
sorbet-runtime (= 0.5.11460)
spoom (1.5.0)
erubi (>= 1.10.0)
prism (>= 0.28.0)
sorbet-static-and-runtime (>= 0.5.10187)
thor (>= 0.19.2)
tapioca (0.16.7)
benchmark
bundler (>= 2.2.25)
netrc (>= 0.11.0)
parallel (>= 1.21.0)
rbi (~> 0.2)
sorbet-static-and-runtime (>= 0.5.11087)
spoom (>= 1.2.0)
thor (>= 1.2.0)
yard-sorbet
thor (1.3.2)
timeout (0.4.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
yard (0.9.37)
yard-sorbet (0.9.0)
sorbet-runtime
yard

PLATFORMS
ruby
Expand All @@ -127,6 +159,7 @@ DEPENDENCIES
rubocop-shopify
sidekiq
sorbet-runtime
tapioca
yard

BUNDLED WITH
Expand Down
120 changes: 120 additions & 0 deletions lib/tapioca/dsl/compilers/job_iteration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# typed: strict
# frozen_string_literal: true

return unless defined?(JobIteration::Iteration)

module Tapioca
module Dsl
module Compilers
class JobIteration < Compiler
extend T::Sig

ConstantType = type_member { { fixed: T.class_of(::JobIteration::Iteration) } }

sig { override.void }
def decorate
return unless constant.instance_methods(false).include?(:build_enumerator)

root.create_path(constant) do |job|
method = constant.instance_method(:build_enumerator)
constant_name = name_of(constant)
expanded_parameters = compile_method_parameters_to_rbi(method).reject do |typed_param|
typed_param.param.name == "cursor"
end

job.create_method(
"perform_later",
parameters: perform_later_parameters(expanded_parameters, constant_name),
return_type: "T.any(#{constant_name}, FalseClass)",
class_method: true,
)

job.create_method(
"perform_now",
parameters: expanded_parameters,
return_type: "void",
class_method: true,
)

job.create_method(
"perform",
parameters: expanded_parameters,
return_type: "void",
class_method: false,
)
end
end

private

sig do
params(
parameters: T::Array[RBI::TypedParam],
constant_name: T.nilable(String),
).returns(T::Array[RBI::TypedParam])
end
def perform_later_parameters(parameters, constant_name)
if ::Gem::Requirement.new(">= 7.0").satisfied_by?(::ActiveJob.gem_version)
parameters.reject! { |typed_param| RBI::BlockParam === typed_param.param }
parameters + [create_block_param(
"block",
type: "T.nilable(T.proc.params(job: #{constant_name}).void)",
)]
else
parameters
end
end

def compile_method_parameters_to_rbi(method_def)
signature = signature_of(method_def)
method_def = signature.nil? ? method_def : signature.method
method_types = parameters_types_from_signature(method_def, signature)

parameters = T.let(method_def.parameters, T::Array[[Symbol, T.nilable(Symbol)]])

parameters.each_with_index.flat_map do |(type, name), index|
fallback_arg_name = "_arg#{index}"

name = name ? name.to_s : fallback_arg_name
name = fallback_arg_name unless valid_parameter_name?(name)
method_type = T.must(method_types[index])

case type
when :req
if signature && (type_value = signature.arg_types[index][1]) && type_value.is_a?(T::Types::FixedHash)
type_value.types.map do |key, value|
create_kw_param(key.to_s, type: value.to_s)
end
else
create_param(name, type: method_type)
end
when :opt
create_opt_param(name, type: method_type, default: "T.unsafe(nil)")
when :rest
create_rest_param(name, type: method_type)
when :keyreq
create_kw_param(name, type: method_type)
when :key
create_kw_opt_param(name, type: method_type, default: "T.unsafe(nil)")
when :keyrest
create_kw_rest_param(name, type: method_type)
when :block
create_block_param(name, type: method_type)
else
raise "Unknown type `#{type}`."
end
end
end

class << self
extend T::Sig

sig { override.returns(T::Enumerable[Module]) }
def gather_constants
all_classes.select { |c| c < ::JobIteration::Iteration }
end
end
end
end
end
end
Loading

0 comments on commit c76569d

Please sign in to comment.