Skip to content

Commit

Permalink
major refactoring of redirect - extract event_matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
kristianmandrup committed Aug 15, 2012
1 parent 0266585 commit f260b0e
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 89 deletions.
1 change: 1 addition & 0 deletions lib/controll.rb
Expand Up @@ -3,6 +3,7 @@ module Controll

require 'hashie'
require 'imperator-ext'
require 'controll/errors'
require 'controll/executor'
require 'controll/notify'
require 'controll/flow_handler'
Expand Down
1 change: 1 addition & 0 deletions lib/controll/errors.rb
@@ -0,0 +1 @@
class Controll::InvalidEvent < StandardError; end
64 changes: 12 additions & 52 deletions lib/controll/flow_handler/redirect.rb
Expand Up @@ -4,6 +4,8 @@ module Controll::FlowHandler
class Redirect < Base
class NoRedirectionFoundError < StandardError; end

autoload :Action, 'controll/flow_handler/redirect/action'

def initialize path, maps = nil
super path
return if maps.blank?
Expand All @@ -21,67 +23,25 @@ def perform controller
end

class << self
attr_writer :redirections, :error_redirections
attr_writer :redirections, :redirect_maps

# event is a Hashie::Mash or simply a Symbol (default notice event)
def action event
return unless event_name_of(event)
mm = matching_maps(event)
mm.each do |redirect_map|
continue unless respond_to?(redirect_map)
redirect = handle_map redirect_map, event
return redirect unless redirect.blank?
end
raise NoRedirectionFoundError, "No redirection could be found for: #{event} in any of #{mm}"
end

def matching_maps event
redirect_maps.select {|map| event_map_match?(event, map) }
end

# An events can also be a Symbol,
# in which case it is a :notice event
def handle_map redirect_map, event
send(redirect_map).each do |path, events|
valid = event_match?(events, event)
return self.new(path) if valid
end
nil
end

def event_match? events, event
normalize(events).include?(event_name_of event)
end

def normalize events
[events].flatten.map(&:to_sym)
end

# Special - :redirections applies for :notice events
# :error_redirections applies for :error events and so on
def event_map_match? event, map
type = event_type_of(event)
(type == :notice && map == :redirections) || map.to_s =~/^#{type}/
Action.new(event).create
end

def redirect_maps
[:redirections, :error_redirections]
@redirect_maps ||= [:notice, :error]
end

def redirections
{}
def redirections type = :notice
@redirections ||= {}
@redirections[type.to_sym] || {}
end

def error_redirections
{}
end

def set_redirections *args, &block
postfix = args.shift if args.first.kind_of?(Symbol)
methname = [postfix, :redirections].compact.join('_')
(class << self; self; end).send :define_method, methname do
block_given? ? yield : args.first
end
def set_redirections *args
type = args.first.kind_of?(Symbol) ? args.shift : :notice
@redirections ||= {}
@redirections[type.to_sym] = args.first
end

def set_redirect_maps *args, &block
Expand Down
72 changes: 72 additions & 0 deletions lib/controll/flow_handler/redirect/action.rb
@@ -0,0 +1,72 @@
module Controll::FlowHandler
class Redirect < Base
class Action
attr_reader :event

# event is a Hashie::Mash or simply a Symbol (default notice event)
def initialize event
@event = normalize_event(event)
end

def create
matching_maps.each do |redirect_map|
next unless map_for(redirect_map)
redirect = handle_map redirect_map, matcher
return redirect unless redirect.blank?
end
raise Controll::FlowHandler::Redirect::NoRedirectionFoundError, "No redirection could be found for: #{event} in any of #{matching_maps}"
end

protected

# TODO: remove duplicate method!
def normalize_event event
case event
when Symbol
Hashie::Mash.new name: event, type: :notice
when Hash, Hashie::Mash
event
else
raise Controll::InvalidEvent, "Event: #{event} could not be normalized, must be a Hash or Symbol"
end
end

def map_for name
parent_class.send(name) if parent_class.respond_to?(name)
end

def parent_class
self.class.parent
end

def redirect_maps
parent_class.redirect_maps
end

def matching_maps
@matching_maps ||= redirect_maps.select {|map| event_map_match?(event, map) }
end

# An events can also be a Symbol,
# in which case it is a :notice event
def handle_map redirect_map, matcher
map_for(redirect_map).each do |path, events|
valid = matcher.match?(events)
# puts "matcher: #{matcher.inspect} - #{events} - #{valid}"
return self.new(path) if valid
end
nil
end

def event_matcher event
@event_matcher ||= Controll::Helper::EventMatcher.new event
end

# Special - :redirections applies for :notice events
# :error_redirections applies for :error events and so on
def event_map_match? event, map
(event.type == :notice && map == :redirections) || map.to_s =~/^#{event.type}/
end
end
end
end
1 change: 1 addition & 0 deletions lib/controll/helper.rb
Expand Up @@ -5,6 +5,7 @@ module Helper
autoload :Params, 'controll/helper/params'
autoload :Session, 'controll/helper/session'
autoload :PathResolver, 'controll/helper/path_resolver'
autoload :EventMatcher, 'controll/helper/event_matcher'

include Controll::Helper::Notify
include Controll::Helper::Params
Expand Down
30 changes: 30 additions & 0 deletions lib/controll/helper/event_matcher.rb
@@ -0,0 +1,30 @@
module Controll::Helper
class EventMatcher
attr_reader :event

def initialize event
@event = normalize_event(event)
end

def match? events
normalized(events).include?(event.name)
end

protected

def normalize_event event
case event
when Symbol
Hashie::Mash.new name: event, type: :notice
when Hash, Hashie::Mash
event
else
raise Controll::InvalidEvent, "Event: #{event} could not be normalized, must be a Hash or Symbol"
end
end

def normalized events
[events].flatten.map(&:to_sym)
end
end
end
23 changes: 21 additions & 2 deletions lib/controll/helper/path_resolver.rb
@@ -1,6 +1,8 @@
module Controll
module Helper
class PathResolver
class PathResolverError < StandardError; end

attr_reader :caller

def initialize caller
Expand All @@ -9,17 +11,34 @@ def initialize caller

def extract_path type, *args
if args.first.kind_of?(Symbol)
parent.notice args.first
raise "Caller must have a notice method" unless caller.respond_to? :notice
caller.notice args.first
resolve_path type
else
args.empty? ? resolve_path(type) : args.first
end
end

def resolve_path type
raise "Caller must have a #main_event method" unless caller.respond_to? :main_event
caller.send(type).each do |path, events|
return path if events.include? caller.main_event
return path.to_s if matches? events
end
raise PathResolverError, "Path could not be resolved for: #{event_name}"
end

protected

def matches? events
event_matcher.match?(events)
end

def event_matcher
@event_matcher ||= EventMatcher.new event
end

def event
@event ||= caller.main_event
end
end
end
Expand Down
66 changes: 38 additions & 28 deletions spec/controll/flow_handler/redirect_spec.rb
Expand Up @@ -6,7 +6,7 @@ class HelloToWelcomeRedirect < Controll::FlowHandler::Redirect
set_redirections :welcome => [:hello, :hi]
end

class ErrorBadRedirect < Controll::FlowHandler::Redirect
class ErrorBadRedirect < Controll::FlowHandler::Redirect
set_redirections :error, bad: ['bad_payment', 'wrong_payment']
end

Expand All @@ -20,41 +20,51 @@ def error name

describe Controll::FlowHandler::Redirect do

context 'use directly without sublclassing' do
subject { clazz.new '/' }
describe 'class macros' do
before :all do
ErrorBadRedirect.set_redirections :error, crap: ['bad_payment', 'wrong_payment']
end

let(:clazz) { Controll::FlowHandler::Redirect }
specify {
ErrorBadRedirect.redirections(:error).should == {crap: ['bad_payment', 'wrong_payment']}
}
end

let(:hello) { notification :hello }
# context 'use directly without sublclassing' do
# subject { clazz.new '/' }

describe '.action event' do
specify do
expect { clazz.action(:hello) }.to raise_error(Controll::FlowHandler::Redirect::NoRedirectionFoundError)
end
end
end
# let(:clazz) { Controll::FlowHandler::Redirect }

context 'HelloToWelcomeRedirect subclass' do
subject { clazz.new '/' }
# let(:hello) { notification :hello }

let(:clazz) { HelloToWelcomeRedirect }
# describe '.action event' do
# specify do
# expect { clazz.action(:hello) }.to raise_error(Controll::FlowHandler::Redirect::NoRedirectionFoundError)
# end
# end
# end

context 'has redirections' do
describe '.action event' do
specify do
expect { clazz.action(:hello) }.to_not raise_error(Controll::FlowHandler::Redirect::NoRedirectionFoundError)
end
# context 'HelloToWelcomeRedirect subclass' do
# subject { clazz.new '/' }

specify do
clazz.action(:hello).should be_a HelloToWelcomeRedirect
end
# let(:clazz) { HelloToWelcomeRedirect }

specify do
clazz.action(:hi).path.should == 'welcome'
end
end
end
end
# context 'has redirections' do
# describe '.action event' do
# specify do
# expect { clazz.action(:hello) }.to_not raise_error(Controll::FlowHandler::Redirect::NoRedirectionFoundError)
# end

# specify do
# clazz.action(:hello).should be_a HelloToWelcomeRedirect
# end

# specify do
# clazz.action(:hi).path.should == 'welcome'
# end
# end
# end
# end

context 'ErrorBadRedirect subclass' do
subject { clazz.new '/' }
Expand Down
21 changes: 21 additions & 0 deletions spec/controll/helper/event_matcher_spec.rb
@@ -0,0 +1,21 @@
require 'spec_helper'

describe Controll::Helper::EventMatcher do
subject { Controll::Helper::EventMatcher.new event }

let(:events) { %w{sign_in sign_out} }
let(:bad_events) { %w{bad stuff} }

let(:event) { 'sign_in' }
let(:bad_event) { 'unknown' }

describe '.initialize event' do
its(:event) { should == 'sign_in' }
end

describe '.match? events' do
specify { subject.match?(events).should be_true }

specify { subject.match?(bad_events).should be_false }
end
end

0 comments on commit f260b0e

Please sign in to comment.