hooks 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/Gemfile.lock +12 -0
- data/README.rdoc +87 -0
- data/Rakefile +33 -0
- data/lib/hooks.rb +67 -0
- data/test/hooks_test.rb +64 -0
- data/test/test_helper.rb +11 -0
- metadata +84 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
= Hooks
|
2
|
+
|
3
|
+
<em>Generic hooks with callbacks for Ruby.</em>
|
4
|
+
|
5
|
+
|
6
|
+
== Introduction
|
7
|
+
|
8
|
+
Hooks lets you define hooks declaratively in your ruby class. You can add callbacks to your hook, which will be
|
9
|
+
run as soon as _you_ run the hook!
|
10
|
+
|
11
|
+
It's almost like ActiveSupport::Callbacks but 76,6% less complex.
|
12
|
+
|
13
|
+
Instead, it is not more than 80 lines of code, one method compilation, no +method_missing+ and no magic.
|
14
|
+
|
15
|
+
|
16
|
+
== Example
|
17
|
+
|
18
|
+
Let's take... a cat.
|
19
|
+
|
20
|
+
class Cat
|
21
|
+
include Hooks
|
22
|
+
|
23
|
+
define_hook :after_dinner
|
24
|
+
|
25
|
+
Now you can add callbacks to your hook declaratively in your class.
|
26
|
+
|
27
|
+
after_dinner do
|
28
|
+
puts "Ice cream!"
|
29
|
+
end
|
30
|
+
|
31
|
+
after_dinner :have_a_desert # => refers to Cat#have_a_desert
|
32
|
+
|
33
|
+
|
34
|
+
def have_a_desert
|
35
|
+
puts "Hell, yeah!"
|
36
|
+
end
|
37
|
+
|
38
|
+
Running the callbacks happens on instances. It will run the block and +#have_a_desert+ from above.
|
39
|
+
|
40
|
+
cat.run_hook :after_dinner
|
41
|
+
# => Ice cream!
|
42
|
+
Hell, yeah!
|
43
|
+
|
44
|
+
|
45
|
+
== Options
|
46
|
+
|
47
|
+
You're free to pass any number of arguments to #run_callback.
|
48
|
+
|
49
|
+
cat.run_hook :before_dinner, cat, Time.now
|
50
|
+
|
51
|
+
The callbacks should be ready for receiving parameters.
|
52
|
+
|
53
|
+
before_dinner :wash_pawns
|
54
|
+
before_dinner do |who, when|
|
55
|
+
...
|
56
|
+
end
|
57
|
+
|
58
|
+
def wash_pawns(who, when)
|
59
|
+
|
60
|
+
|
61
|
+
Not sure why a cat should have ice cream for dinner. Beside that, I was tempted naming this gem _hooker_.
|
62
|
+
|
63
|
+
|
64
|
+
== Installation
|
65
|
+
|
66
|
+
gem install hooks
|
67
|
+
|
68
|
+
|
69
|
+
== Dependencies
|
70
|
+
|
71
|
+
The current gem requires
|
72
|
+
|
73
|
+
* active_support 2.3.x
|
74
|
+
|
75
|
+
|
76
|
+
== Similar libraries
|
77
|
+
|
78
|
+
* http://github.com/nakajima/aspectory/tree/master/lib/aspectory/
|
79
|
+
* http://github.com/auser/backcall
|
80
|
+
* http://github.com/mmcgrana/simple_callbacks
|
81
|
+
|
82
|
+
|
83
|
+
== License
|
84
|
+
|
85
|
+
Copyright (c) 2010, Nick Sutterer
|
86
|
+
|
87
|
+
Released under the MIT License.
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the hooks plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |test|
|
10
|
+
test.libs << 'test'
|
11
|
+
test.test_files = FileList['test/*_test.rb']
|
12
|
+
test.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'jeweler'
|
16
|
+
$:.unshift File.dirname(__FILE__) # add current dir to LOAD_PATHS
|
17
|
+
require 'lib/hooks'
|
18
|
+
|
19
|
+
Jeweler::Tasks.new do |spec|
|
20
|
+
spec.name = "hooks"
|
21
|
+
spec.version = ::Hooks::VERSION
|
22
|
+
spec.summary = %{Generic hooks with callbacks for Ruby. }
|
23
|
+
spec.description = "Declaratively define hooks, add callbacks and run them with the options you like."
|
24
|
+
spec.homepage = "http://nicksda.apotomo.de/category/hooks"
|
25
|
+
spec.authors = ["Nick Sutterer"]
|
26
|
+
spec.email = "apotonick@gmail.com"
|
27
|
+
|
28
|
+
spec.files = FileList["[A-Z]*", File.join(*%w[{lib} ** *]).to_s]
|
29
|
+
|
30
|
+
spec.add_dependency 'activesupport', '>= 2.3.0'
|
31
|
+
end
|
32
|
+
|
33
|
+
Jeweler::GemcutterTasks.new
|
data/lib/hooks.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'active_support/core_ext/class/inheritable_attributes.rb'
|
2
|
+
|
3
|
+
# Almost like ActiveSupport::Callbacks but 76,6% less complex.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
#
|
7
|
+
# class CatWidget < Apotomo::Widget
|
8
|
+
# define_hook :after_dinner
|
9
|
+
#
|
10
|
+
# Now you can add callbacks to your hook declaratively in your class.
|
11
|
+
#
|
12
|
+
# after_dinner do puts "Ice cream!" end
|
13
|
+
# after_dinner :have_a_desert # => refers to CatWidget#have_a_desert
|
14
|
+
#
|
15
|
+
# Running the callbacks happens on instances. It will run the block and #have_a_desert from above.
|
16
|
+
#
|
17
|
+
# cat.run_hook :after_dinner
|
18
|
+
module Hooks
|
19
|
+
VERSION = "0.1"
|
20
|
+
|
21
|
+
def self.included(base)
|
22
|
+
base.extend ClassMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def define_hook(name)
|
27
|
+
accessor_name = "_#{name}_callbacks"
|
28
|
+
|
29
|
+
setup_hook_accessors(accessor_name)
|
30
|
+
define_hook_writer(name, accessor_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def define_hook_writer(hook, accessor_name)
|
35
|
+
instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
36
|
+
def #{hook}(method=nil, &block)
|
37
|
+
callback = block_given? ? block : method
|
38
|
+
#{accessor_name} << callback
|
39
|
+
end
|
40
|
+
RUBY_EVAL
|
41
|
+
end
|
42
|
+
|
43
|
+
def setup_hook_accessors(accessor_name)
|
44
|
+
class_inheritable_array(accessor_name, :instance_writer => false)
|
45
|
+
send("#{accessor_name}=", []) # initialize ivar.
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# Runs the callbacks (method/block) for the specified hook +name+. Additional arguments will
|
51
|
+
# be passed to the callback.
|
52
|
+
#
|
53
|
+
# Example:
|
54
|
+
#
|
55
|
+
# cat.run_hook :after_dinner, "i want ice cream!"
|
56
|
+
#
|
57
|
+
# will invoke the callbacks like
|
58
|
+
#
|
59
|
+
# desert("i want ice cream!")
|
60
|
+
# block.call("i want ice cream!")
|
61
|
+
def run_hook(name, *args)
|
62
|
+
self.class.send("_#{name}_callbacks").each do |callback|
|
63
|
+
send(callback, *args) and next if callback.kind_of? Symbol
|
64
|
+
callback.call(*args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/test/hooks_test.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HooksTest < ActiveSupport::TestCase
|
4
|
+
context "Hooks.define_hook" do
|
5
|
+
setup do
|
6
|
+
@klass = Class.new(Object) do
|
7
|
+
include Hooks
|
8
|
+
|
9
|
+
def executed
|
10
|
+
@executed ||= [];
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
@mum = @klass.new
|
15
|
+
@mum.class.define_hook :after_eight
|
16
|
+
end
|
17
|
+
|
18
|
+
should "provide accessors to the stored callbacks" do
|
19
|
+
assert_equal [], @klass._after_eight_callbacks
|
20
|
+
@klass._after_eight_callbacks << :dine
|
21
|
+
assert_equal [:dine], @klass._after_eight_callbacks
|
22
|
+
end
|
23
|
+
|
24
|
+
context "creates a public writer for the hook that" do
|
25
|
+
should "accepts method names" do
|
26
|
+
@klass.after_eight :dine
|
27
|
+
assert_equal [:dine], @klass._after_eight_callbacks
|
28
|
+
end
|
29
|
+
|
30
|
+
should "accepts blocks" do
|
31
|
+
@klass.after_eight do true; end
|
32
|
+
assert @klass._after_eight_callbacks.first.kind_of? Proc
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "Hooks.run_hook"do
|
37
|
+
should "run without parameters" do
|
38
|
+
@mum.instance_eval do
|
39
|
+
def a; executed << :a; end
|
40
|
+
def b; executed << :b; end
|
41
|
+
|
42
|
+
self.class.after_eight :b
|
43
|
+
self.class.after_eight :a
|
44
|
+
end
|
45
|
+
|
46
|
+
@mum.run_hook(:after_eight)
|
47
|
+
|
48
|
+
assert_equal [:b, :a], @mum.executed
|
49
|
+
end
|
50
|
+
|
51
|
+
should "accept arbitrary parameters" do
|
52
|
+
@mum.instance_eval do
|
53
|
+
def a(me, arg); executed << arg+1; end
|
54
|
+
end
|
55
|
+
@mum.class.after_eight :a
|
56
|
+
@mum.class.after_eight lambda { |me, arg| me.executed << arg-1 }
|
57
|
+
|
58
|
+
@mum.run_hook(:after_eight, @mum, 1)
|
59
|
+
|
60
|
+
assert_equal [2, 0], @mum.executed
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hooks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
version: "0.1"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Nick Sutterer
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2010-09-24 00:00:00 +02:00
|
17
|
+
default_executable:
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: activesupport
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
- 3
|
30
|
+
- 0
|
31
|
+
version: 2.3.0
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description: Declaratively define hooks, add callbacks and run them with the options you like.
|
35
|
+
email: apotonick@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- README.rdoc
|
42
|
+
files:
|
43
|
+
- Gemfile
|
44
|
+
- Gemfile.lock
|
45
|
+
- README.rdoc
|
46
|
+
- Rakefile
|
47
|
+
- lib/hooks.rb
|
48
|
+
- test/test_helper.rb
|
49
|
+
- test/hooks_test.rb
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://nicksda.apotomo.de/category/hooks
|
52
|
+
licenses: []
|
53
|
+
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --charset=UTF-8
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.7
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: Generic hooks with callbacks for Ruby.
|
82
|
+
test_files:
|
83
|
+
- test/test_helper.rb
|
84
|
+
- test/hooks_test.rb
|