Last active
August 29, 2015 13:57
-
-
Save jodosha/9621213 to your computer and use it in GitHub Desktop.
Instance configuration vs global configuration
This file contains 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
# 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