Skip to content

Instantly share code, notes, and snippets.

@jodosha
Last active August 29, 2015 13:57
Show Gist options
  • Save jodosha/9621213 to your computer and use it in GitHub Desktop.
Save jodosha/9621213 to your computer and use it in GitHub Desktop.
Instance configuration vs global configuration
# This is a simplified version of Lotus::Controller
# Problem:
#
# Global configurations are handy both for framework authors and for developers.
# However, they make hard using the same library twice in the same Ruby process.
# Solution constraints:
#
# 1. It should offer a DSL to developers
# 2. It should be transparent for developers (they will only use the DSL)
# 3. It should use instance level configuration
# Proposed solution:
#
# * The Configuration class decides the defaults (see Configuration#initialize keyword args).
# * It also expose a DSL that fiddles with low level details of getting/setting values (see Dsl#handle_exceptions and handled_exceptions).
# * The DSL gets extended by the actions, so they can use it and have separated, class level configurations (see Show.handle_exceptions)
# * The DSL exposes a public class method, so that `Show.configuration` will return the configuration, according to the DSL settings and/or defaults.
# * The #initialize process, gets a new instance of that configuration and holds a reference at the instance level (see @config)
module Lotus
module Controller
class Configuration
attr_reader :handle_exceptions, :handled_exceptions
def initialize(handle_exceptions: true, handled_exceptions: {})
@handle_exceptions = handle_exceptions
@handled_exceptions = handled_exceptions
end
module Dsl
def configuration
Controller::Configuration.new(
handled_exceptions: handled_exceptions,
handle_exceptions: handle_exceptions
)
end
private
def handle_exception(klass, code)
handled_exceptions[klass] = code
end
def handle_exceptions(value = nil)
if !value.nil?
@handle_exceptions = value
else
@handle_exceptions = true unless defined?(@handle_exceptions)
@handle_exceptions
end
end
def handled_exceptions
@handled_exceptions ||= {}
end
end
end
end
module Action
def self.included(base)
base.extend Controller::Configuration::Dsl
base.prepend InstanceMethods
end
module InstanceMethods
def initialize(*)
super
@config = self.class.configuration
end
def call(env)
begin
super
@code || 200
rescue Exception => e
_handle_exception(e)
end
end
private
def _handle_exception(e)
raise e unless @config.handle_exceptions
@code = @config.handled_exceptions.fetch(e.class, 500)
end
end
end
end
class RecordNotFound < StandardError
end
class Index
include Lotus::Action
def call(env)
raise 'exception'
end
end
class Show
include Lotus::Action
handle_exception RecordNotFound, 404
def call(env)
raise RecordNotFound
end
end
class Whatever
include Lotus::Action
handle_exceptions false
# This is just to prove that arity of #initialize won't be affected for end devs.
def initialize(error)
@error = error
end
def call(env)
raise @error
end
end
puts Index.new.call({})
puts Show.new.call({})
puts Whatever.new('hell').call({})
__FILE__
# output
# 500
# 404
# conf.rb:99:in `call': hell (RuntimeError)
# from conf.rb:54:in `call'
# from conf.rb:105:in `<main>'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment