Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added Apotomo::Hooks to have simpler hooks and callbacks.
- Loading branch information
Showing
6 changed files
with
185 additions
and
84 deletions.
There are no files selected for viewing
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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
module Apotomo | ||
# Almost like ActiveSupport::Callbacks but 76,6% less complex. | ||
# | ||
# Example: | ||
# | ||
# class CatWidget < Apotomo::Widget | ||
# define_hook :after_dinner | ||
# | ||
# Now you can add callbacks to your hook declaratively in your class. | ||
# | ||
# after_dinner do puts "Ice cream!" end | ||
# after_dinner :have_a_desert # => refers to CatWidget#have_a_desert | ||
# | ||
# Running the callbacks happens on instances. It will run the block and #have_a_desert from above. | ||
# | ||
# cat.run_hook :after_dinner | ||
module Hooks | ||
def self.included(base) | ||
base.extend ClassMethods | ||
end | ||
|
||
module ClassMethods | ||
def define_hook(name) | ||
accessor_name = "_#{name}_callbacks" | ||
|
||
setup_hook_accessors(accessor_name) | ||
define_hook_writer(name, accessor_name) | ||
end | ||
|
||
private | ||
def define_hook_writer(hook, accessor_name) | ||
instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 | ||
def #{hook}(method=nil, &block) | ||
callback = block_given? ? block : method | ||
#{accessor_name} << callback | ||
end | ||
RUBY_EVAL | ||
end | ||
|
||
def setup_hook_accessors(accessor_name) | ||
class_inheritable_array(accessor_name, :instance_writer => false) | ||
send("#{accessor_name}=", []) # initialize ivar. | ||
end | ||
|
||
end | ||
|
||
# Runs the callbacks (method/block) for the specified hook +name+. Additional arguments will | ||
# be passed to the callback. | ||
# | ||
# Example: | ||
# | ||
# cat.run_hook :after_dinner, "i want ice cream!" | ||
# | ||
# will invoke the callbacks like | ||
# | ||
# desert("i want ice cream!") | ||
# block.call("i want ice cream!") | ||
def run_hook(name, *args) | ||
self.class.send("_#{name}_callbacks").each do |callback| | ||
send(callback, *args) and next if callback.kind_of? Symbol | ||
callback.call(*args) | ||
end | ||
end | ||
end | ||
end |
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 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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
require File.join(File.dirname(__FILE__), '..', 'test_helper') | ||
|
||
class HooksTest < ActiveSupport::TestCase | ||
context "Hooks.define_hook" do | ||
setup do | ||
@klass = Class.new(Object) do | ||
include Apotomo::Hooks | ||
|
||
def executed | ||
@executed ||= []; | ||
end | ||
end | ||
|
||
@mum = @klass.new | ||
@mum.class.define_hook :after_eight | ||
end | ||
|
||
should "provide accessors to the stored callbacks" do | ||
assert_equal [], @klass._after_eight_callbacks | ||
@klass._after_eight_callbacks << :dine | ||
assert_equal [:dine], @klass._after_eight_callbacks | ||
end | ||
|
||
context "creates a public writer for the hook that" do | ||
should "accepts method names" do | ||
@klass.after_eight :dine | ||
assert_equal [:dine], @klass._after_eight_callbacks | ||
end | ||
|
||
should "accepts blocks" do | ||
@klass.after_eight do true; end | ||
assert @klass._after_eight_callbacks.first.kind_of? Proc | ||
end | ||
end | ||
|
||
context "Hooks.run_hook"do | ||
should "run without parameters" do | ||
@mum.instance_eval do | ||
def a; executed << :a; end | ||
def b; executed << :b; end | ||
|
||
self.class.after_eight :b | ||
self.class.after_eight :a | ||
end | ||
|
||
@mum.run_hook(:after_eight) | ||
|
||
assert_equal [:b, :a], @mum.executed | ||
end | ||
|
||
should "accept arbitrary parameters" do | ||
@mum.instance_eval do | ||
def a(me, arg); executed << arg+1; end | ||
end | ||
@mum.class.after_eight :a | ||
@mum.class.after_eight lambda { |me, arg| me.executed << arg-1 } | ||
|
||
@mum.run_hook(:after_eight, @mum, 1) | ||
|
||
assert_equal [2, 0], @mum.executed | ||
end | ||
end | ||
end | ||
end |
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