Hook system now executes hooks in order.

Also the return value of exec_hook() is now the value of the last executed hook.
Internally, hooks now use arrays (rather than hash). Also added a get_hooks() method.
Updated tests to reflect these changes.
This commit is contained in:
John Mair 2011-11-23 18:51:54 +13:00
parent 76336c5780
commit c800c0f04f
2 changed files with 136 additions and 48 deletions

View File

@ -6,17 +6,20 @@ class Pry
end
# Add a new hook to be executed for the `name` even.
# @param [Symbol] name The name of the event.
# @param [Symbol] hook_function_name The name of the hook.
# @param [Symbol] event_name The name of the event.
# @param [Symbol] hook_name The name of the hook.
# @param [#call] callable The callable.
# @yield The block to use as the callable (if `callable` parameter not provided)
def add_hook(name, hook_function_name, callable=nil, &block)
@hooks[name] ||= {}
def add_hook(event_name, hook_name, callable=nil, &block)
@hooks[event_name] ||= []
# do not allow duplicates
raise ArgumentError, "Hook with name '#{hook_name}' already defined!" if hook_exists?(event_name, hook_name)
if block
@hooks[name][hook_function_name] = block
@hooks[event_name] << [hook_name, block]
elsif callable
@hooks[name][hook_function_name] = callable
@hooks[event_name] << [hook_name, callable]
else
raise ArgumentError, "Must provide a block or callable."
end
@ -24,47 +27,82 @@ class Pry
self
end
# Execute the list of hooks for the `name` event.
# @param [Symbol] name The name of the event.
# Execute the list of hooks for the `event_name` event.
# @param [Symbol] event_name The name of the event.
# @param [Array] args The arguments to pass to each hook function.
# @return [Hash] The return values for each of the hook functions.
def exec_hook(name, *args, &block)
@hooks[name] ||= {}
Hash[@hooks[name].each.map { |k, v| [k, v.call(*args, &block)] }]
# @return [Object] The return value of the last executed hook.
def exec_hook(event_name, *args, &block)
@hooks[event_name] ||= []
# silence warnings to get rid of 1.8's "warning: multiple values
# for a block parameter" warnings
Pry::Helpers::BaseHelpers.silence_warnings do
@hooks[event_name].map { |hook_name, callable| callable.call(*args, &block) }.last
end
end
# Return the number of hook functions registered for the `name` event.
# @param [Symbol] name The name of the event.
# @return [Fixnum] The number of hook functions for the `name` event.
def hook_count(name)
@hooks[name] ||= {}
@hooks[name].size
# Return the number of hook functions registered for the `event_name` event.
# @param [Symbol] event_name The name of the event.
# @return [Fixnum] The number of hook functions for `event_name`.
def hook_count(event_name)
@hooks[event_name] ||= []
@hooks[event_name].size
end
# Return a specific hook for a given event.
# @param [Symbol] event_name The name of the event.
# @param [Symbol[ hook_name The name of the hook
# @return [#call] The requested hook.
def get_hook(event_name, hook_name)
@hooks[event_name] ||= []
hook = @hooks[event_name].find { |current_hook_name, callable| current_hook_name == hook_name }
hook.last if hook
end
# Return the hash of hook names / hook functions for a
# given event.
# @param [Symbol] name The name of the event.
# @return [Hash]
def get_hook(name, hook_function_name)
@hooks[name] ||= {}
@hooks[name][hook_function_name]
# given event. (Note that modifying the returned hash does not
# alter the hooks, use add_hook/delete_hook for that).
# @param [Symbol] event_name The name of the event.
# @return [Hash] The hash of hook names / hook functions.
def get_hooks(event_name)
@hooks[event_name] ||= []
Hash[@hooks[event_name]]
end
# Delete a hook for an event.
# @param [Symbol] name The name of the event.
# @param [Symbol] hook_function_name The name of the hook.
# @param [Symbol] event_name The name of the event.
# @param [Symbol] hook_name The name of the hook.
# to delete.
# @return [#call] The deleted hook.
def delete_hook(name, hook_function_name)
@hooks[name] ||= {}
@hooks[name].delete(hook_function_name)
def delete_hook(event_name, hook_name)
@hooks[event_name] ||= []
deleted_callable = nil
@hooks[event_name].delete_if do |current_hook_name, callable|
if current_hook_name == hook_name
deleted_callable = callable
true
else
false
end
end
deleted_callable
end
# Clear all hooks functions for a given event.
# @param [String] name The name of the event.
def clear(name)
@hooks[name] = {}
# @param [String] event_name The name of the event.
def clear(event_name)
@hooks[event_name] = []
end
# @param [Symbol] event_name Name of the event.
# @param [Symbol] hook_name Name of the hook.
# @return [Boolean] Whether the hook by the name `hook_name`
# is defined for the event.
def hook_exists?(event_name, hook_name)
!!@hooks[event_name].find { |name, _| name == hook_name }
end
private :hook_exists?
end
end

View File

@ -7,12 +7,15 @@ describe Pry::Hooks do
describe "adding a new hook" do
it 'should not execute hook while adding it' do
@hooks.add_hook(:test_hook, :my_name) { @test_var = true }
@test_var.should == nil
run = false
@hooks.add_hook(:test_hook, :my_name) { run = true }
run.should == false
end
it 'should return a count of 0 for an empty hook' do
@hooks.hook_count(:test_hook).should == 0
it 'should not allow adding of a hook with a duplicate name' do
@hooks.add_hook(:test_hook, :my_name) {}
lambda { @hooks.add_hook(:test_hook, :my_name) {} }.should.raise ArgumentError
end
it 'should create a new hook with a block' do
@ -20,12 +23,12 @@ describe Pry::Hooks do
@hooks.hook_count(:test_hook).should == 1
end
it 'should create a new hook for an event' do
it 'should create a new hook with a callable' do
@hooks.add_hook(:test_hook, :my_name, proc { })
@hooks.hook_count(:test_hook).should == 1
end
it 'should use just block if given both block and callable' do
it 'should use block if given both block and callable' do
run = false
foo = false
@hooks.add_hook(:test_hook, :my_name, proc { foo = true }) { run = true }
@ -44,17 +47,44 @@ describe Pry::Hooks do
@hooks.add_hook(:test_hook, :my_name2) {}
@hooks.hook_count(:test_hook).should == 2
end
it 'should return a count of 0 for an empty hook' do
@hooks.hook_count(:test_hook).should == 0
end
end
describe "getting a hook" do
it 'should return the correct requested hook' do
run = false
fun = false
@hooks.add_hook(:test_hook, :my_name) { run = true }
@hooks.add_hook(:test_hook, :my_name2) { fun = true }
@hooks.get_hook(:test_hook, :my_name).call
run.should == true
fun.should == false
describe "getting hooks" do
describe "get_hook" do
it 'should return the correct requested hook' do
run = false
fun = false
@hooks.add_hook(:test_hook, :my_name) { run = true }
@hooks.add_hook(:test_hook, :my_name2) { fun = true }
@hooks.get_hook(:test_hook, :my_name).call
run.should == true
fun.should == false
end
it 'should return nil if hook does not exist' do
@hooks.get_hook(:test_hook, :my_name).should == nil
end
end
describe "get_hooks" do
it 'should return a hash of hook names/hook functions for an event' do
hook1 = proc { 1 }
hook2 = proc { 2 }
@hooks.add_hook(:test_hook, :my_name1, hook1)
@hooks.add_hook(:test_hook, :my_name2, hook2)
hash = @hooks.get_hooks(:test_hook)
hash.size.should == 2
hash[:my_name1].should == hook1
hash[:my_name2].should == hook2
end
it 'should return an empty hash if no hooks defined' do
@hooks.get_hooks(:test_hook).should == {}
end
end
end
@ -81,6 +111,10 @@ describe Pry::Hooks do
@hooks.delete_hook(:test_hook, :my_name).call
run.should == true
end
it 'should return nil if hook does not exist' do
@hooks.delete_hook(:test_hook, :my_name).should == nil
end
end
describe "executing a hook" do
@ -115,11 +149,27 @@ describe Pry::Hooks do
it 'should execute all hooks for an event if more than one is defined' do
x = nil
y = nil
@hooks.add_hook(:test_hook, :my_name1) { y = true }
@hooks.add_hook(:test_hook, :my_name2) { x = true }
@hooks.add_hook(:test_hook, :my_name) { y = true }
@hooks.exec_hook(:test_hook)
x.should == true
y.should == true
end
it 'should execute hooks in order' do
array = []
@hooks.add_hook(:test_hook, :my_name1) { array << 1 }
@hooks.add_hook(:test_hook, :my_name2) { array << 2 }
@hooks.add_hook(:test_hook, :my_name3) { array << 3 }
@hooks.exec_hook(:test_hook)
array.should == [1, 2, 3]
end
it 'return value of exec_hook should be that of last executed hook' do
@hooks.add_hook(:test_hook, :my_name1) { 1 }
@hooks.add_hook(:test_hook, :my_name2) { 2 }
@hooks.add_hook(:test_hook, :my_name3) { 3 }
@hooks.exec_hook(:test_hook).should == 3
end
end
end