Last active
October 14, 2019 19:42
-
-
Save Incanus3/75c849c0fb805e81a44856bf7f1ddf4f to your computer and use it in GitHub Desktop.
scatch implementation of continuable error handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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