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 c189641
Show file tree
Hide file tree
Showing 4 changed files with 371 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
96 changes: 96 additions & 0 deletions lib/tapioca/dsl/compilers/job_iteration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# typed: strict
# frozen_string_literal: true

return unless defined?(JobIteration::Iteration)

require "prism"

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)
parameters = compile_method_parameters_to_rbi(method).reject do |typed_param|
typed_param.param.name == "cursor"
end

expanded_parameters = parameters.flat_map do |typed_param|
hash_param = typed_param.type.match(/\A\{.*\}\z/)
if hash_param
key_value_pairs = parse_hash_parameter(typed_param)
key_value_pairs.map do |key, value|
create_kw_param(key, type: value)
end
else
typed_param
end
end

return_type = compile_method_return_type_to_rbi(method)

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

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

private

def parse_hash_parameter(typed_param)
parse_result = Prism.parse(typed_param.type)
return "T.untyped" if parse_result.failure?

visitor = HashParamVisitor.new
parse_result.value.accept(visitor)
visitor.key_value_pairs
end

class HashParamVisitor < Prism::Visitor
attr_reader :key_value_pairs

def initialize
super
@key_value_pairs = []
end

def visit_hash_node(node)
node.elements.each do |element|
key = element.key.unescaped
value = element.value.slice
@key_value_pairs << [key, value]
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 c189641

Please sign in to comment.