Skip to content

Instantly share code, notes, and snippets.

@wojtha
Last active June 9, 2016 13:18
Show Gist options
  • Select an option

  • Save wojtha/76a392a14a617294f293ad29530bf69e to your computer and use it in GitHub Desktop.

Select an option

Save wojtha/76a392a14a617294f293ad29530bf69e to your computer and use it in GitHub Desktop.
FailFast module proof of concept. Inspired by Hanami::Interactor
module FailFast
class Success
attr_reader :data, :error
def initialize(data)
@data = data
end
def success?
true
end
def failure?
false
end
end
class Failure
attr_reader :data, :error
def initialize(error)
@error = error
end
def success?
false
end
def failure?
true
end
end
module Helpers
def error(message)
Failure.new(message)
end
def error!(message)
throw :fail, error(message)
end
def fail!(result = false)
throw :fail, result
end
def success(data)
Success.new(data)
end
end
def self.wrap(*method_names)
Module.new do
define_singleton_method :included do |base|
proxy = Module.new do
method_names.each do |m|
define_method(m) do |*args|
catch(:fail) do
super(*args)
end
end
end
end
base.include Helpers
base.prepend proxy
end
end
end
def self.key_wrap(*method_names)
Module.new do
define_singleton_method :included do |base|
proxy = Module.new do
method_names.each do |m|
define_method(m) do |*args, **kwargs|
catch(:fail) do
super(*args, **kwargs)
end
end
end
end
base.include Helpers
base.prepend proxy
end
end
end
def self.auto_wrap(*method_names)
Module.new do
define_singleton_method :included do |base|
proxy = Module.new do
method_names.each do |m|
# We need to detect if method supports keyword arguments to provide
# right parameters order when calling super() which doesn't support
# implicit parameters passing in define_method.
if base.instance_method(m).parameters.any? { |a| a[0] == :key }
define_method(m) do |*args, **kwargs|
catch(:fail) do
super(*args, **kwargs)
end
end
else
define_method(m) do |*args|
catch(:fail) do
super(*args)
end
end
end
end
end
base.include Helpers
base.prepend proxy
end
end
end
end
require_relative 'fail_fast'
class SomeOperation
def call(error_level = 0)
error! "Fatal error!" if error_level > 1
if error_level > 0
error "Warning!"
else
success "hello!"
end
end
def keywords(error_level: 0)
error! "Fatal error!" if error_level > 1
if error_level > 0
error "Warning!"
else
success "hello!"
end
end
def plain(error_level = 0)
if error_level > 1
fail!
elsif error_level > 0
false
else
true
end
end
# We need to include mdule after methods are loaded so we can introspect them
include FailFast.auto_wrap :call, :keywords, :plain
end
service = SomeOperation.new
puts "(0) ================================="
result = service.call(0)
puts result # =>
puts result.success? # => true
puts result.error.inspect # => nil
puts result.data.inspect # => "hello!""
puts "(1) ================================="
result = service.call(1)
puts result # =>
puts result.success? # => false
puts result.error.inspect # => "Warning!"
puts result.data.inspect # => nil
puts "(2) ================================="
result = service.call(2)
puts result # =>
puts result.success? # => false
puts result.error.inspect # => "Fatal error!"
puts result.data.inspect # => nil
puts "(3) ================================="
result = service.keywords(error_level: 0)
puts result # =>
puts result.success? # => true
puts result.error.inspect # => nil
puts result.data.inspect # => "hello!""
puts "(4) ================================="
result = service.plain(0)
puts result # => true
puts "(6) ================================="
result = service.plain(1)
puts result # => false
puts "(7) ================================="
result = service.plain(2)
puts result # => false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment