Skip to content

Commit

Permalink
Introduce HookCollection in Hooks module
Browse files Browse the repository at this point in the history
- Replaced conditional logic (forward/reverse processing) with
  polymorhpism (HookCollection types)
  • Loading branch information
dchelimsky committed Jul 9, 2010
1 parent 3f55e71 commit 252072c
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 27 deletions.
4 changes: 2 additions & 2 deletions lib/rspec/core/example_group.rb
Expand Up @@ -145,7 +145,7 @@ def self.eval_before_alls(example)
superclass.before_all_ivars.each { |ivar, val| example.instance_variable_set(ivar, val) }
world.run_hook_filtered(:before, :all, self, example)

run_hook!(:before, :all, example, :reverse => true)
run_hook!(:before, :all, example)
example.instance_variables.each { |ivar| before_all_ivars[ivar] = example.instance_variable_get(ivar) }
end

Expand All @@ -155,7 +155,7 @@ def self.eval_before_eachs(example)
end

def self.eval_after_eachs(example)
ancestors.each { |ancestor| ancestor.run_hook(:after, :each, example, :reverse => true) }
ancestors.each { |ancestor| ancestor.run_hook(:after, :each, example) }
world.run_hook_filtered(:after, :each, self, example)
end

Expand Down
66 changes: 41 additions & 25 deletions lib/rspec/core/hooks.rb
Expand Up @@ -2,7 +2,7 @@ module RSpec
module Core
module Hooks

class HookBase
class Hook
attr_reader :options

def initialize(options, block)
Expand All @@ -17,9 +17,7 @@ def options_apply?(group)
def to_proc
@block
end
end

class Hook < HookBase
def call
@block.call
end
Expand All @@ -29,26 +27,54 @@ def run_in(example)
end
end

class AroundHook < HookBase
class BeforeHook < Hook; end
class AfterHook < Hook; end
class AroundHook < Hook
def call(wrapped_example)
@block.call(wrapped_example)
end
end

class HookCollection < Array
def find_hooks_for(group)
dup.reject {|hook| !hook.options_apply?(group)}
end
end

class BeforeHooks < HookCollection
def run_all(example)
each {|h| h.run_in(example) }
end

def run_all!(example)
shift.run_in(example) until empty?
end
end
class AfterHooks < HookCollection
def run_all(example)
reverse.each {|h| h.run_in(example) }
end

def run_all!(example)
pop.run_in(example) until empty?
end
end
class AroundHooks < HookCollection; end

def hooks
@hooks ||= {
:around => { :each => [] },
:before => { :each => [], :all => [], :suite => [] },
:after => { :each => [], :all => [], :suite => [] }
:around => { :each => AroundHooks.new },
:before => { :each => BeforeHooks.new, :all => BeforeHooks.new, :suite => BeforeHooks.new },
:after => { :each => AfterHooks.new, :all => AfterHooks.new, :suite => AfterHooks.new }
}
end

def before(scope=:each, options={}, &block)
hooks[:before][scope] << Hook.new(options, block)
hooks[:before][scope] << BeforeHook.new(options, block)
end

def after(scope=:each, options={}, &block)
hooks[:after][scope] << Hook.new(options, block)
hooks[:after][scope] << AfterHook.new(options, block)
end

def around(scope=:each, options={}, &block)
Expand All @@ -57,32 +83,22 @@ def around(scope=:each, options={}, &block)

# Runs all of the blocks stored with the hook in the context of the
# example. If no example is provided, just calls the hook directly.
def run_hook(hook, scope, example=nil, options={})
if options[:reverse]
hooks[hook][scope].reverse.each {|h| h.run_in(example) }
else
hooks[hook][scope].each {|h| h.run_in(example) }
end
def run_hook(hook, scope, example=nil)
hooks[hook][scope].run_all(example)
end

# Just like run_hook, except it removes the blocks as it evalutes them,
# ensuring that they will only be run once.
def run_hook!(hook, scope, example, options={})
until hooks[hook][scope].empty?
if options[:reverse]
hooks[hook][scope].shift.run_in(example)
else
hooks[hook][scope].pop.run_in(example)
end
end
def run_hook!(hook, scope, example)
hooks[hook][scope].run_all!(example)
end

def run_hook_filtered(hook, scope, group, example)
find_hook(hook, scope, group).each {|h| h.run_in(example) }
find_hook(hook, scope, group).run_all(example)
end

def find_hook(hook, scope, group)
hooks[hook][scope].select {|hook| hook.options_apply?(group)}
hooks[hook][scope].find_hooks_for(group)
end
end
end
Expand Down

0 comments on commit 252072c

Please sign in to comment.