Skip to content

Instantly share code, notes, and snippets.

@Incanus3
Last active October 14, 2019 19:42
Show Gist options
  • Save Incanus3/75c849c0fb805e81a44856bf7f1ddf4f to your computer and use it in GitHub Desktop.
Save Incanus3/75c849c0fb805e81a44856bf7f1ddf4f to your computer and use it in GitHub Desktop.
scatch implementation of continuable error handling
module Dry
module Effects
CONTINUE_HANDLER_REGISTRY = {}
module Handler
def self.Continue(context_name)
Module.new do
define_method("with_#{context_name}") do |handlers_hash, &block|
::Dry::Effects::CONTINUE_HANDLER_REGISTRY[context_name] = handlers_hash
block.call
end
end
end
end
def self.Continue(context_name)
Module.new do
define_method("#{context_name}_consumer") do |&block|
report_problem = nil
fiber = Fiber.new do |_|
block.call(report_problem)
end
continuation_function = lambda do |value_from_handler|
fiber.resume(value_from_handler)
end
report_problem = lambda do |handler_name, context = nil|
Fiber.yield(handler_name, context)
end
handler_name, context = fiber.resume
loop do
handler = ::Dry::Effects::CONTINUE_HANDLER_REGISTRY[context_name][handler_name]
raise "handler for #{handler_name} not found" unless handler
# handler is passed the continuation function and may or may not call it
# if he does, the value he passes to it will be returned by the report_problem
# function in the worker
handler_name, context = handler.call(context, continuation_function)
break unless handler_name
end
end
end
end
end
end
class HandlerContext
include Dry::Effects::Handler.Continue(:stream_handling)
def perform_action_with_handling
handlers = {
cant_open_stream: lambda {|context, continue| continue.(StringIO.new)},
out_of_coffee: lambda {|context, continue|
if context[:type] != 'white' then continue.('black coffee')
else puts "can't have white coffee, will not continue"
end
}
}
with_stream_handling(handlers) { WorkerContext.new.perform_action }
end
end
class WorkerContext
include Dry::Effects.Continue(:stream_handling)
def perform_action
# ...
stream = nil
black_coffee = nil
white_coffee = nil
stream_handling_consumer do |report_problem|
begin
puts "trying to open file"
stream = File.open('nonexistent_path_lakjsdlfkjlas')
rescue SystemCallError => e
puts "couldn't open file, reporting problem"
stream = report_problem.(:cant_open_stream, {path: 'path', or: 'whatever'})
puts "problem solved, got #{stream} from upstream :D"
end
unless black_coffee
puts "out of black coffee, reporting problem"
black_coffee = report_problem.(:out_of_coffee, {type: 'black'})
puts "problem solved, got #{black_coffee}"
end
unless white_coffee
puts "out of white coffee, reporting problem"
white_coffee = report_problem.(:out_of_coffee, {type: 'white'})
# will never be printed, because handler decides not to call the continuation function
puts "problem solved, got #{black_coffee}"
end
end
puts "HERE"
p stream
p black_coffee
p white_coffee
end
end
HandlerContext.new.perform_action_with_handling
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment