hooks 0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +8 -6
- data/Rakefile +2 -4
- data/lib/hooks.rb +38 -20
- data/lib/hooks/inheritable_attribute.rb +33 -0
- data/test/hooks_test.rb +35 -2
- data/test/inheritable_attribute_test.rb +49 -0
- metadata +8 -18
data/README.rdoc
CHANGED
@@ -5,12 +5,10 @@
|
|
5
5
|
|
6
6
|
== Introduction
|
7
7
|
|
8
|
-
|
8
|
+
_Hooks_ lets you define hooks declaratively in your ruby class. You can add callbacks to your hook, which will be
|
9
9
|
run as soon as _you_ run the hook!
|
10
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.
|
11
|
+
It's almost like ActiveSupport::Callbacks but 76,6% less complex. Instead, it is not more than 60 lines of code, one method compilation, no +method_missing+ and no magic.
|
14
12
|
|
15
13
|
|
16
14
|
== Example
|
@@ -35,7 +33,7 @@ Now you can add callbacks to your hook declaratively in your class.
|
|
35
33
|
puts "Hell, yeah!"
|
36
34
|
end
|
37
35
|
|
38
|
-
Running the callbacks happens on instances. It will run the block and
|
36
|
+
Running the callbacks happens on instances. It will run the block and #have_a_desert from above.
|
39
37
|
|
40
38
|
cat.run_hook :after_dinner
|
41
39
|
# => Ice cream!
|
@@ -73,9 +71,13 @@ The current gem requires
|
|
73
71
|
* active_support 2.3.x
|
74
72
|
|
75
73
|
|
74
|
+
== Anybody using it?
|
75
|
+
|
76
|
+
* Hooks is already used in [Apotomo:http://github.com/apotonick/apotomo], a hot widget framework for Rails. Look at +lib/apotomo/widget.rb+ for examples and into +lib/apotomo/tree_node.rb+ to learn how modules-driven code might benefit from hooks.
|
77
|
+
|
76
78
|
== Similar libraries
|
77
79
|
|
78
|
-
* http://github.com/nakajima/aspectory
|
80
|
+
* http://github.com/nakajima/aspectory
|
79
81
|
* http://github.com/auser/backcall
|
80
82
|
* http://github.com/mmcgrana/simple_callbacks
|
81
83
|
|
data/Rakefile
CHANGED
@@ -13,8 +13,8 @@ Rake::TestTask.new(:test) do |test|
|
|
13
13
|
end
|
14
14
|
|
15
15
|
require 'jeweler'
|
16
|
-
$:.unshift File.dirname(__FILE__) # add current dir to LOAD_PATHS
|
17
|
-
require '
|
16
|
+
$:.unshift File.dirname(__FILE__)+"/lib" # add current dir to LOAD_PATHS
|
17
|
+
require 'hooks'
|
18
18
|
|
19
19
|
Jeweler::Tasks.new do |spec|
|
20
20
|
spec.name = "hooks"
|
@@ -26,8 +26,6 @@ Jeweler::Tasks.new do |spec|
|
|
26
26
|
spec.email = "apotonick@gmail.com"
|
27
27
|
|
28
28
|
spec.files = FileList["[A-Z]*", File.join(*%w[{lib} ** *]).to_s]
|
29
|
-
|
30
|
-
spec.add_dependency 'activesupport', '>= 2.3.0'
|
31
29
|
end
|
32
30
|
|
33
31
|
Jeweler::GemcutterTasks.new
|
data/lib/hooks.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "hooks/inheritable_attribute"
|
2
2
|
|
3
3
|
# Almost like ActiveSupport::Callbacks but 76,6% less complex.
|
4
4
|
#
|
@@ -16,9 +16,10 @@ require 'active_support/core_ext/class/inheritable_attributes.rb'
|
|
16
16
|
#
|
17
17
|
# cat.run_hook :after_dinner
|
18
18
|
module Hooks
|
19
|
-
VERSION = "0.1"
|
19
|
+
VERSION = "0.1.1"
|
20
20
|
|
21
21
|
def self.included(base)
|
22
|
+
base.extend InheritableAttribute
|
22
23
|
base.extend ClassMethods
|
23
24
|
end
|
24
25
|
|
@@ -30,21 +31,41 @@ module Hooks
|
|
30
31
|
define_hook_writer(name, accessor_name)
|
31
32
|
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
34
|
+
# Like Hooks#run_hook but for the class. Note that +:callbacks+ must be class methods.
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# class Cat
|
39
|
+
# after_eight :grab_a_beer
|
40
|
+
#
|
41
|
+
# def self.grab_a_beer(*) # and so on...
|
42
|
+
#
|
43
|
+
# where <tt>Cat.run_hook :after_eight</tt> will call the class method +grab_a_beer+.
|
44
|
+
def run_hook(name, *args)
|
45
|
+
run_hook_for(name, self, *args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_hook_for(name, scope, *args)
|
49
|
+
send("_#{name}_callbacks").each do |callback|
|
50
|
+
scope.send(callback, *args) and next if callback.kind_of? Symbol
|
51
|
+
callback.call(*args)
|
46
52
|
end
|
47
|
-
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def define_hook_writer(hook, accessor_name)
|
57
|
+
instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
58
|
+
def #{hook}(method=nil, &block)
|
59
|
+
callback = block_given? ? block : method
|
60
|
+
#{accessor_name} << callback
|
61
|
+
end
|
62
|
+
RUBY_EVAL
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup_hook_accessors(accessor_name)
|
66
|
+
inheritable_attr(accessor_name)
|
67
|
+
send("#{accessor_name}=", []) # initialize ivar.
|
68
|
+
end
|
48
69
|
end
|
49
70
|
|
50
71
|
# Runs the callbacks (method/block) for the specified hook +name+. Additional arguments will
|
@@ -59,9 +80,6 @@ module Hooks
|
|
59
80
|
# desert("i want ice cream!")
|
60
81
|
# block.call("i want ice cream!")
|
61
82
|
def run_hook(name, *args)
|
62
|
-
self.class.
|
63
|
-
send(callback, *args) and next if callback.kind_of? Symbol
|
64
|
-
callback.call(*args)
|
65
|
-
end
|
83
|
+
self.class.run_hook_for(name, self, *args)
|
66
84
|
end
|
67
85
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Hooks
|
2
|
+
module InheritableAttribute
|
3
|
+
# Creates an inheritable attribute with accessors in the singleton class. Derived classes inherit the
|
4
|
+
# attributes. This is especially helpful with arrays or hashes that are extended in the inheritance
|
5
|
+
# chain. Note that you have to initialize the inheritable attribute.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# class Cat
|
10
|
+
# inheritable_attr :drinks
|
11
|
+
# self.drinks = ["Becks"]
|
12
|
+
#
|
13
|
+
# class Garfield < Cat
|
14
|
+
# self.drinks << "Fireman's 4"
|
15
|
+
#
|
16
|
+
# and then, later
|
17
|
+
#
|
18
|
+
# Cat.drinks #=> ["Becks"]
|
19
|
+
# Garfield.drinks #=> ["Becks", "Fireman's 4"]
|
20
|
+
def inheritable_attr(name)
|
21
|
+
instance_eval %Q{
|
22
|
+
def #{name}=(v)
|
23
|
+
@#{name} = v
|
24
|
+
end
|
25
|
+
|
26
|
+
def #{name}
|
27
|
+
return @#{name} unless superclass.respond_to?(:#{name})
|
28
|
+
@#{name} ||= superclass.#{name}.clone # only do this once.
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/test/hooks_test.rb
CHANGED
@@ -31,9 +31,16 @@ class HooksTest < ActiveSupport::TestCase
|
|
31
31
|
@klass.after_eight do true; end
|
32
32
|
assert @klass._after_eight_callbacks.first.kind_of? Proc
|
33
33
|
end
|
34
|
+
|
35
|
+
should "be inherited" do
|
36
|
+
@klass.after_eight :dine
|
37
|
+
subklass = Class.new(@klass)
|
38
|
+
|
39
|
+
assert_equal [:dine], subklass._after_eight_callbacks
|
40
|
+
end
|
34
41
|
end
|
35
42
|
|
36
|
-
context "Hooks
|
43
|
+
context "Hooks#run_hook" do
|
37
44
|
should "run without parameters" do
|
38
45
|
@mum.instance_eval do
|
39
46
|
def a; executed << :a; end
|
@@ -59,6 +66,32 @@ class HooksTest < ActiveSupport::TestCase
|
|
59
66
|
|
60
67
|
assert_equal [2, 0], @mum.executed
|
61
68
|
end
|
62
|
-
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "in class context" do
|
72
|
+
should "run a callback block" do
|
73
|
+
executed = []
|
74
|
+
@klass.after_eight do
|
75
|
+
executed << :klass
|
76
|
+
end
|
77
|
+
@klass.run_hook :after_eight
|
78
|
+
|
79
|
+
assert_equal [:klass], executed
|
80
|
+
end
|
81
|
+
|
82
|
+
should "run a class methods" do
|
83
|
+
executed = []
|
84
|
+
@klass.instance_eval do
|
85
|
+
after_eight :have_dinner
|
86
|
+
|
87
|
+
def have_dinner(executed)
|
88
|
+
executed << :have_dinner
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@klass.run_hook :after_eight, executed
|
92
|
+
|
93
|
+
assert_equal [:have_dinner], executed
|
94
|
+
end
|
95
|
+
end
|
63
96
|
end
|
64
97
|
end
|
@@ -0,0 +1,49 @@
|
|
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
|
+
extend Hooks::InheritableAttribute
|
8
|
+
end
|
9
|
+
|
10
|
+
@mum = @klass.new
|
11
|
+
@klass.inheritable_attr :drinks
|
12
|
+
end
|
13
|
+
|
14
|
+
should "provide a reader with inherited attributes, already" do
|
15
|
+
assert_equal nil, @klass.drinks
|
16
|
+
end
|
17
|
+
|
18
|
+
should "provide an attribute copy in subclasses" do
|
19
|
+
@klass.drinks = []
|
20
|
+
assert @klass.drinks.object_id != Class.new(@klass).drinks.object_id
|
21
|
+
end
|
22
|
+
|
23
|
+
should "provide a writer" do
|
24
|
+
@klass.drinks = [:cabernet]
|
25
|
+
assert_equal [:cabernet], @klass.drinks
|
26
|
+
end
|
27
|
+
|
28
|
+
should "inherit attributes" do
|
29
|
+
@klass.drinks = [:cabernet]
|
30
|
+
|
31
|
+
subklass_a = Class.new(@klass)
|
32
|
+
subklass_a.drinks << :becks
|
33
|
+
|
34
|
+
subklass_b = Class.new(@klass)
|
35
|
+
|
36
|
+
assert_equal [:cabernet], @klass.drinks
|
37
|
+
assert_equal [:cabernet, :becks], subklass_a.drinks
|
38
|
+
assert_equal [:cabernet], subklass_b.drinks
|
39
|
+
end
|
40
|
+
|
41
|
+
should "not inherit attributes if we set explicitely" do
|
42
|
+
@klass.drinks = [:cabernet]
|
43
|
+
subklass = Class.new(@klass)
|
44
|
+
|
45
|
+
subklass.drinks = [:merlot] # we only want merlot explicitely.
|
46
|
+
assert_equal [:merlot], subklass.drinks # no :cabernet, here
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -5,7 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
9
10
|
platform: ruby
|
10
11
|
authors:
|
11
12
|
- Nick Sutterer
|
@@ -13,24 +14,10 @@ autorequire:
|
|
13
14
|
bindir: bin
|
14
15
|
cert_chain: []
|
15
16
|
|
16
|
-
date: 2010-
|
17
|
+
date: 2010-10-03 00:00:00 +02:00
|
17
18
|
default_executable:
|
18
|
-
dependencies:
|
19
|
-
|
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
|
19
|
+
dependencies: []
|
20
|
+
|
34
21
|
description: Declaratively define hooks, add callbacks and run them with the options you like.
|
35
22
|
email: apotonick@gmail.com
|
36
23
|
executables: []
|
@@ -45,6 +32,8 @@ files:
|
|
45
32
|
- README.rdoc
|
46
33
|
- Rakefile
|
47
34
|
- lib/hooks.rb
|
35
|
+
- lib/hooks/inheritable_attribute.rb
|
36
|
+
- test/inheritable_attribute_test.rb
|
48
37
|
- test/test_helper.rb
|
49
38
|
- test/hooks_test.rb
|
50
39
|
has_rdoc: true
|
@@ -80,5 +69,6 @@ signing_key:
|
|
80
69
|
specification_version: 3
|
81
70
|
summary: Generic hooks with callbacks for Ruby.
|
82
71
|
test_files:
|
72
|
+
- test/inheritable_attribute_test.rb
|
83
73
|
- test/test_helper.rb
|
84
74
|
- test/hooks_test.rb
|