hooked 0.0.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/Gemfile +7 -0
- data/LICENSE +19 -0
- data/README.md +12 -0
- data/Rakefile +10 -0
- data/hooked.gemspec +27 -0
- data/lib/hooked.rb +6 -0
- data/lib/hooked/container.rb +52 -0
- data/lib/hooked/controller.rb +92 -0
- data/lib/hooked/hook.rb +33 -0
- data/lib/hooked/hookable.rb +20 -0
- data/lib/hooked/version.rb +3 -0
- data/test/container_test.rb +71 -0
- data/test/controller_test.rb +176 -0
- data/test/helper.rb +9 -0
- data/test/hook_test.rb +95 -0
- data/test/hookable_test.rb +71 -0
- metadata +127 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Lars Gierth
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Ruby library for Aspect Oriented Programming
|
2
|
+
============================================
|
3
|
+
|
4
|
+
hooked makes AOP a breeze. It lets you define and invoke code that other
|
5
|
+
gems or parts of your application can hook onto. Supported hooks are before,
|
6
|
+
after and around.
|
7
|
+
|
8
|
+
To Do
|
9
|
+
-----
|
10
|
+
|
11
|
+
* Skip hooks
|
12
|
+
* Control flow (return, next, break)
|
data/Rakefile
ADDED
data/hooked.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "hooked/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "hooked"
|
7
|
+
s.version = Hooked::VERSION
|
8
|
+
s.date = Date.today.to_s
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Lars Gierth"]
|
11
|
+
s.email = ["lars.gierth@gmail.com"]
|
12
|
+
s.homepage = "http://rubygems.org/gems/hooked"
|
13
|
+
s.summary = %q{Ruby library for Aspect Oriented Programming}
|
14
|
+
s.description = %q{hooked makes AOP a breeze. It lets you define and invoke
|
15
|
+
code that other gems or parts of your application can hook
|
16
|
+
onto. Supported hooks are before, after and around.}
|
17
|
+
|
18
|
+
s.add_dependency "depression"
|
19
|
+
|
20
|
+
s.add_development_dependency "test-unit"
|
21
|
+
s.add_development_dependency "mocha"
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n") - [".gitignore", ".rvmrc"]
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
end
|
data/lib/hooked.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Hooked
|
2
|
+
module Container
|
3
|
+
attr_accessor :hooked
|
4
|
+
|
5
|
+
def hookable(name, *args, &block)
|
6
|
+
hooked.invoke(name, *args, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
attr_reader :hookables, :hooks
|
11
|
+
|
12
|
+
def hookable(name, &block)
|
13
|
+
@hookables << Hookable.new(name, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def hook(type, hookable, name, opts = {}, &block)
|
17
|
+
opts[:before] ||= []
|
18
|
+
unless opts[:before].respond_to?(:each) or opts[:before] == :all
|
19
|
+
opts[:before] = [opts[:before]]
|
20
|
+
end
|
21
|
+
|
22
|
+
opts[:after] ||= []
|
23
|
+
unless opts[:after].respond_to?(:each) or opts[:after] == :all
|
24
|
+
opts[:after] = [opts[:after]]
|
25
|
+
end
|
26
|
+
|
27
|
+
@hooks[hookable] ||= []
|
28
|
+
@hooks[hookable] << Hook.new(type, name, opts, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def before(*args, &block)
|
32
|
+
hook(:before, *args, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def around(*args, &block)
|
36
|
+
hook(:around, *args, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def after(*args, &block)
|
40
|
+
hook(:after, *args, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.included(klass)
|
45
|
+
klass.class_eval do
|
46
|
+
extend(ClassMethods)
|
47
|
+
@hookables = []
|
48
|
+
@hooks = {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Hooked
|
2
|
+
Result = Struct.new(:type, :value)
|
3
|
+
|
4
|
+
class Controller
|
5
|
+
attr_reader :name, :containers, :hookables, :hooks
|
6
|
+
|
7
|
+
def initialize(name, *containers)
|
8
|
+
@name = name
|
9
|
+
|
10
|
+
@containers = []
|
11
|
+
@hookables = {}
|
12
|
+
@hooks = {}
|
13
|
+
|
14
|
+
add(*containers)
|
15
|
+
refresh
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(*containers)
|
19
|
+
@containers.push(*containers)
|
20
|
+
containers.each do |c|
|
21
|
+
c.class.hookables.each do |hkbl|
|
22
|
+
hkbl.container = c
|
23
|
+
hookables[hkbl.name] = hkbl
|
24
|
+
end
|
25
|
+
c.class.hooks.each do |hkbl_name, hs|
|
26
|
+
hs.each {|h| h.container = c }
|
27
|
+
@hooks[hkbl_name] ||= []
|
28
|
+
@hooks[hkbl_name].push(*hs)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def refresh
|
34
|
+
hooks.each {|hkbl_name, hs| hooks[hkbl_name] = Depression.process(hs) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def invoke(name, *args, &block)
|
38
|
+
if block
|
39
|
+
hookable = Hookable.new(name, &block)
|
40
|
+
else
|
41
|
+
hookable = hookables[name] || raise("Unknown hookable: #{name}")
|
42
|
+
end
|
43
|
+
|
44
|
+
result = catch(:hooked) do
|
45
|
+
(hooks[name] || []).reverse.inject(hookable) do |next_item, item|
|
46
|
+
proc {|a| call_hook(item, next_item, a) }
|
47
|
+
end.call(args)
|
48
|
+
end
|
49
|
+
if (result[0] rescue nil) == :hooked_abort_chain
|
50
|
+
result[1]
|
51
|
+
else
|
52
|
+
result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
# def call_wrapped_hook(hook, hookable, args)
|
59
|
+
# ret = nil
|
60
|
+
#
|
61
|
+
# j = (0..1).each do |i|
|
62
|
+
# ret = call_hook(hook, hookable, args) if i == 0
|
63
|
+
# end
|
64
|
+
# unless j
|
65
|
+
# throw(:hooked, :hooked_abort_chain)
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# ret
|
69
|
+
# end
|
70
|
+
|
71
|
+
def call_hook(hook, hookable, args)
|
72
|
+
args = hook.call(*args) if hook.before?
|
73
|
+
|
74
|
+
result = catch(:hooked) do
|
75
|
+
if hook.around?
|
76
|
+
hook.call(hookable, *args)
|
77
|
+
else
|
78
|
+
hookable.call(*args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
if result == :hooked_abort_chain
|
82
|
+
throw(:hooked, [:hooked_abort_chain, args])
|
83
|
+
else
|
84
|
+
args = result
|
85
|
+
end
|
86
|
+
|
87
|
+
args = hook.call(*args) if hook.after?
|
88
|
+
|
89
|
+
args
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/hooked/hook.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Hooked
|
2
|
+
class Hook
|
3
|
+
attr_reader :type, :name, :relations
|
4
|
+
attr_accessor :container, :weight
|
5
|
+
|
6
|
+
def initialize(type, name, relations = {}, container = nil, &block)
|
7
|
+
raise ArgumentError, "Invalid hook type `#{type}'" unless [
|
8
|
+
:before, :after, :around
|
9
|
+
].include?(type.to_sym)
|
10
|
+
@type, @name, @relations = type.to_sym, name.to_sym, relations
|
11
|
+
|
12
|
+
raise ArgumentError, "Hooked::Hookable.new expects a block." unless block
|
13
|
+
@container, @block = container, block
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(*args)
|
17
|
+
raise RuntimeError, "No container set for hook `#{name}'" unless container
|
18
|
+
container.instance_exec(*args, &@block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def before?
|
22
|
+
type == :before
|
23
|
+
end
|
24
|
+
|
25
|
+
def after?
|
26
|
+
type == :after
|
27
|
+
end
|
28
|
+
|
29
|
+
def around?
|
30
|
+
type == :around
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Hooked
|
2
|
+
class Hookable
|
3
|
+
attr_reader :name
|
4
|
+
attr_accessor :container
|
5
|
+
|
6
|
+
def initialize(name, container = nil, &block)
|
7
|
+
raise ArgumentError, "Hooked::Hookable.new expects a block." unless block
|
8
|
+
|
9
|
+
@name, @container, @block = name.to_sym, container, block
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(*args)
|
13
|
+
if container
|
14
|
+
container.instance_exec(*args, &@block)
|
15
|
+
else
|
16
|
+
@block.call(*args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + "/helper.rb"
|
2
|
+
|
3
|
+
class ContainerTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@container = Class.new { include(Hooked::Container) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
Mocha::Mockery.instance.teardown
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_has_hookables
|
13
|
+
block = proc {}
|
14
|
+
@container.hookable(:foo, &block)
|
15
|
+
|
16
|
+
assert_equal :foo, @container.hookables[0].name
|
17
|
+
assert @container.hookables[0].respond_to?(:call)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_has_a_shorthand_method_for_invoking_hookables
|
21
|
+
container = @container.new
|
22
|
+
container.hooked = stub("controller") do
|
23
|
+
expects(:invoke).once.with(:foo, 123).returns("asdf")
|
24
|
+
end
|
25
|
+
|
26
|
+
assert_equal "asdf", container.hookable(:foo, 123)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_has_hooks
|
30
|
+
block = proc {}
|
31
|
+
@container.hook(:after, :work, :start_revolution, {
|
32
|
+
:before => :utopia
|
33
|
+
}, &block)
|
34
|
+
@container.hook(:around, :foo, :do_something, {
|
35
|
+
:before => :all,
|
36
|
+
:after => [:do_something_else, :snooze]
|
37
|
+
}, &block)
|
38
|
+
|
39
|
+
h = @container.hooks[:work][0]
|
40
|
+
assert_equal :after, h.type
|
41
|
+
assert_equal :start_revolution, h.name
|
42
|
+
assert_equal({
|
43
|
+
:before => [:utopia],
|
44
|
+
:after => []
|
45
|
+
}, h.relations)
|
46
|
+
assert h.respond_to?(:call)
|
47
|
+
assert h.respond_to?(:container=)
|
48
|
+
|
49
|
+
h = @container.hooks[:foo][0]
|
50
|
+
assert_equal :around, h.type
|
51
|
+
assert_equal :do_something, h.name
|
52
|
+
assert_equal({
|
53
|
+
:before => :all,
|
54
|
+
:after => [:do_something_else, :snooze]
|
55
|
+
}, h.relations)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_has_shorthand_methods_for_before_around_and_after
|
59
|
+
args = [:foo, :bar, {:before => :all}]
|
60
|
+
block = proc {}
|
61
|
+
|
62
|
+
@container.expects(:hook).once.with(:before, *args)
|
63
|
+
@container.before(*args, &block)
|
64
|
+
|
65
|
+
@container.expects(:hook).once.with(:around, *args)
|
66
|
+
@container.around(*args, &block)
|
67
|
+
|
68
|
+
@container.expects(:hook).once.with(:after, *args)
|
69
|
+
@container.after(*args, &block)
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + "/helper.rb"
|
2
|
+
|
3
|
+
class ControllerTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@containers = [
|
6
|
+
Class.new do
|
7
|
+
include Hooked::Container
|
8
|
+
before :foo, :do_something do |*args|; end
|
9
|
+
end,
|
10
|
+
Class.new do
|
11
|
+
include Hooked::Container
|
12
|
+
around :foo, :do_something_else do |h, *args|
|
13
|
+
h.call(*args)
|
14
|
+
end
|
15
|
+
end,
|
16
|
+
Class.new do
|
17
|
+
include Hooked::Container
|
18
|
+
hookable :foo do |*args|; end
|
19
|
+
end
|
20
|
+
].map {|c| c.new }
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_has_a_name
|
24
|
+
name = :foo
|
25
|
+
controller = Hooked::Controller.new(name)
|
26
|
+
|
27
|
+
assert_equal name, controller.name
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_has_containers
|
31
|
+
controller = Hooked::Controller.new(:foo, @containers[0])
|
32
|
+
controller.add(*@containers[1..2])
|
33
|
+
|
34
|
+
assert_equal @containers, controller.containers
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_collects_hookables_and_hooks_from_containers
|
38
|
+
hookable = @containers[2].class.hookables[0]
|
39
|
+
hooks = [
|
40
|
+
@containers[0].class.hooks[:foo][0],
|
41
|
+
@containers[1].class.hooks[:foo][0]
|
42
|
+
]
|
43
|
+
controller = Hooked::Controller.new(:baz, *@containers)
|
44
|
+
|
45
|
+
assert_equal hookable, controller.hookables[hookable.name]
|
46
|
+
assert_equal hooks, controller.hooks[:foo]
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_sorts_hooks
|
50
|
+
container = Class.new do
|
51
|
+
include(Hooked::Container)
|
52
|
+
before(:foo, :do_something) {}
|
53
|
+
before(:foo, :do_dishes, :after => :all) {}
|
54
|
+
around(:foo, :do_something_more, :before => :do_something) {}
|
55
|
+
after(:foo, :do_something_else, :after => :do_something_more, :before => :do_something) {}
|
56
|
+
around(:foo, :do_laundry, :before => :all) {}
|
57
|
+
end
|
58
|
+
order = [:do_laundry, :do_something_more, :do_something_else, :do_something, :do_dishes]
|
59
|
+
|
60
|
+
controller = Hooked::Controller.new(:baz, container.new)
|
61
|
+
assert_equal order, controller.hooks[:foo].map {|h| h.name }
|
62
|
+
|
63
|
+
controller = Hooked::Controller.new(:baz)
|
64
|
+
controller.add(container.new)
|
65
|
+
assert_not_equal order, controller.hooks[:foo].map {|h| h.name }
|
66
|
+
|
67
|
+
controller.hooks[:foo].shuffle!
|
68
|
+
controller.refresh
|
69
|
+
assert_equal order, controller.hooks[:foo].map {|h| h.name }
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_sets_the_container_on_hookables_and_hooks
|
73
|
+
controller = Hooked::Controller.new(:foo, *@containers[1..2])
|
74
|
+
assert_equal @containers[1], controller.hooks[:foo][0].container
|
75
|
+
assert_equal @containers[2], controller.hookables[:foo].container
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_invokes_hooks_in_their_containers_scope
|
79
|
+
scope = nil
|
80
|
+
container = Class.new do
|
81
|
+
include(Hooked::Container)
|
82
|
+
hookable(:something) {}
|
83
|
+
before(:something, :do_something_else) { scope = self }
|
84
|
+
end.new
|
85
|
+
|
86
|
+
controller = Hooked::Controller.new(:foo, container)
|
87
|
+
controller.invoke(:something)
|
88
|
+
|
89
|
+
assert_equal(container, scope)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_invokes_hookables_in_their_containers_scope
|
93
|
+
scope = nil
|
94
|
+
container = Class.new do
|
95
|
+
include(Hooked::Container)
|
96
|
+
hookable(:something) { scope = self }
|
97
|
+
end.new
|
98
|
+
|
99
|
+
controller = Hooked::Controller.new(:foo, container)
|
100
|
+
controller.invoke(:something)
|
101
|
+
|
102
|
+
assert_equal(container, scope)
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_invokes_dynamic_hookables_in_their_original_scope
|
106
|
+
controller = Hooked::Controller.new(:foo)
|
107
|
+
|
108
|
+
scope = nil
|
109
|
+
controller.invoke(:something) { scope = self }
|
110
|
+
|
111
|
+
assert_equal(self, scope)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_hookables_and_hooks_can_use_next
|
115
|
+
pend
|
116
|
+
|
117
|
+
called = []
|
118
|
+
container = Class.new do
|
119
|
+
include(Hooked::Container)
|
120
|
+
hookable(:something) { called << :something }
|
121
|
+
before(:something, :do_something_more) { called << :do_something_more }
|
122
|
+
before(:something, :do_something_else) { next; called << :do_something_else }
|
123
|
+
end
|
124
|
+
|
125
|
+
controller = Hooked::Controller.new(:foo, container.new)
|
126
|
+
controller.invoke(:something)
|
127
|
+
assert_equal [:do_something_more, :something], called
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_hookables_and_hooks_can_use_return
|
131
|
+
pend
|
132
|
+
|
133
|
+
called = []
|
134
|
+
returned_arg = nil
|
135
|
+
container = Class.new do
|
136
|
+
include(Hooked::Container)
|
137
|
+
hookable(:something) { called << :something }
|
138
|
+
before(:something, :do_something_more) {|arg| return arg + 1; called << :do_something_more }
|
139
|
+
before(:something, :do_something_else) {|arg| returned_arg = arg; return; called << :do_something_else }
|
140
|
+
end
|
141
|
+
|
142
|
+
controller = Hooked::Controller.new(:foo, container.new)
|
143
|
+
fail("Doesn't reach the code after controller.invoke()")
|
144
|
+
controller.invoke(:something, 1)
|
145
|
+
assert_equal [:do_something_more, :asd], called
|
146
|
+
assert_equal 3, returned_arg
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_hookables_and_hooks_can_use_break
|
150
|
+
pend
|
151
|
+
|
152
|
+
called = []
|
153
|
+
container = Class.new do
|
154
|
+
include(Hooked::Container)
|
155
|
+
hookable(:something) { called << :something }
|
156
|
+
before(:something, :do_something_more) { called << :do_something_more }
|
157
|
+
before(:something, :do_something_else) { called << :do_something_else; break }
|
158
|
+
end
|
159
|
+
|
160
|
+
controller = Hooked::Controller.new(:foo, container.new)
|
161
|
+
controller.invoke(:something)
|
162
|
+
assert_equal [:do_something_more, :do_something_else], called
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_arguments_and_return_values_are_passed_in_and_out
|
166
|
+
container = Class.new do
|
167
|
+
include(Hooked::Container)
|
168
|
+
hookable(:something) {|arg| arg += 1; arg }
|
169
|
+
before(:something, :do_something_more) {|arg| arg += 1; [arg] }
|
170
|
+
after(:something, :do_something_else) {|arg| arg += 1; arg }
|
171
|
+
end
|
172
|
+
|
173
|
+
controller = Hooked::Controller.new(:foo, container.new)
|
174
|
+
assert_equal 4, controller.invoke(:something, 1)
|
175
|
+
end
|
176
|
+
end
|
data/test/helper.rb
ADDED
data/test/hook_test.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper.rb"
|
2
|
+
|
3
|
+
class HookTest < Test::Unit::TestCase
|
4
|
+
def test_has_a_name
|
5
|
+
name = :foo
|
6
|
+
hook = Hooked::Hook.new(:before, name) {}
|
7
|
+
|
8
|
+
assert_equal name, hook.name
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_has_a_type
|
12
|
+
build_hook = proc do |type|
|
13
|
+
Hooked::Hook.new(type, :foo) {}
|
14
|
+
end
|
15
|
+
|
16
|
+
hook = build_hook.call(:before)
|
17
|
+
assert_equal :before, hook.type
|
18
|
+
|
19
|
+
assert hook.before?
|
20
|
+
assert build_hook.call(:after).after?
|
21
|
+
assert build_hook.call(:around).around?
|
22
|
+
|
23
|
+
assert_raise(ArgumentError) do
|
24
|
+
build_hook.call(:asdas)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_has_relations
|
29
|
+
relations = {
|
30
|
+
:before => 123
|
31
|
+
}
|
32
|
+
hook = Hooked::Hook.new(:before, :foo, relations) {}
|
33
|
+
assert_equal(123, hook.relations[:before])
|
34
|
+
|
35
|
+
hook.relations[:before] = 456
|
36
|
+
assert_equal(456, hook.relations[:before])
|
37
|
+
|
38
|
+
hook.relations[:after] = "asd"
|
39
|
+
assert_equal("asd", hook.relations[:after])
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_has_a_container
|
43
|
+
container = stub("container")
|
44
|
+
hook = Hooked::Hook.new(:before, :foo, {}, container) {}
|
45
|
+
assert_equal container, hook.container
|
46
|
+
|
47
|
+
container2 = stub("container2")
|
48
|
+
hook.container = container2
|
49
|
+
assert_equal container2, hook.container
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_expects_a_block
|
53
|
+
assert_nothing_raised do
|
54
|
+
Hooked::Hook.new(:before, :foo) {}
|
55
|
+
end
|
56
|
+
|
57
|
+
assert_raise(ArgumentError) do
|
58
|
+
Hooked::Hook.new(:before, :foo)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_executes_block_in_the_containers_scope
|
63
|
+
scope = nil
|
64
|
+
hook = Hooked::Hook.new(:before, :foo) do
|
65
|
+
scope = self
|
66
|
+
end
|
67
|
+
|
68
|
+
assert_raise(RuntimeError) { hook.call }
|
69
|
+
|
70
|
+
container = stub("container")
|
71
|
+
hook.container = container
|
72
|
+
hook.call
|
73
|
+
assert_equal(container, scope)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_passes_arguments_to_the_block
|
77
|
+
args = [123, "asd"]
|
78
|
+
passed_args = nil
|
79
|
+
hook = Hooked::Hook.new(:before, :foo, {}, stub("container")) do |*args|
|
80
|
+
passed_args = args
|
81
|
+
end
|
82
|
+
|
83
|
+
hook.call(args)
|
84
|
+
assert_equal(args, passed_args)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_passes_blocks_return_to_the_caller
|
88
|
+
ret = 123
|
89
|
+
hook = Hooked::Hook.new(:before, :foo, {}, stub("container")) do
|
90
|
+
ret
|
91
|
+
end
|
92
|
+
|
93
|
+
assert_equal(ret, hook.call)
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper.rb"
|
2
|
+
|
3
|
+
class HookableTest < Test::Unit::TestCase
|
4
|
+
def test_has_a_name
|
5
|
+
name = :foo
|
6
|
+
hookable = Hooked::Hookable.new(name) {}
|
7
|
+
|
8
|
+
assert_equal name, hookable.name
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_can_have_a_container
|
12
|
+
container = Object.new
|
13
|
+
hookable = Hooked::Hookable.new(:foo, container) {}
|
14
|
+
|
15
|
+
assert_equal container, hookable.container
|
16
|
+
|
17
|
+
assert_nil Hooked::Hookable.new(:foo) {}.container
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_expects_a_block
|
21
|
+
assert_nothing_raised do
|
22
|
+
Hooked::Hookable.new(:foo) {}
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_raise(ArgumentError) do
|
26
|
+
Hooked::Hookable.new(:foo)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_executes_block_in_the_containers_scope
|
31
|
+
container = Object.new
|
32
|
+
|
33
|
+
scope = nil
|
34
|
+
hookable = Hooked::Hookable.new(:foo, container) do
|
35
|
+
scope = self
|
36
|
+
end
|
37
|
+
|
38
|
+
hookable.call
|
39
|
+
assert_equal container, scope
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_executes_block_in_its_original_scope_if_no_container_given
|
43
|
+
scope = nil
|
44
|
+
hookable = Hooked::Hookable.new(:foo) do
|
45
|
+
scope = self
|
46
|
+
end
|
47
|
+
|
48
|
+
hookable.call
|
49
|
+
assert_equal self, scope
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_passes_arguments_to_the_block
|
53
|
+
args = [123, "asd"]
|
54
|
+
passed_args = nil
|
55
|
+
hookable = Hooked::Hookable.new(:foo) do |*args|
|
56
|
+
passed_args = args
|
57
|
+
end
|
58
|
+
|
59
|
+
hookable.call(args)
|
60
|
+
assert_equal(args, passed_args)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_passes_block_return_to_the_caller
|
64
|
+
ret = 123
|
65
|
+
hookable = Hooked::Hookable.new(:foo) do
|
66
|
+
ret
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_equal(ret, hookable.call)
|
70
|
+
end
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hooked
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Lars Gierth
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-25 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: depression
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: test-unit
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: mocha
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: |-
|
64
|
+
hooked makes AOP a breeze. It lets you define and invoke
|
65
|
+
code that other gems or parts of your application can hook
|
66
|
+
onto. Supported hooks are before, after and around.
|
67
|
+
email:
|
68
|
+
- lars.gierth@gmail.com
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files: []
|
74
|
+
|
75
|
+
files:
|
76
|
+
- Gemfile
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- hooked.gemspec
|
81
|
+
- lib/hooked.rb
|
82
|
+
- lib/hooked/container.rb
|
83
|
+
- lib/hooked/controller.rb
|
84
|
+
- lib/hooked/hook.rb
|
85
|
+
- lib/hooked/hookable.rb
|
86
|
+
- lib/hooked/version.rb
|
87
|
+
- test/container_test.rb
|
88
|
+
- test/controller_test.rb
|
89
|
+
- test/helper.rb
|
90
|
+
- test/hook_test.rb
|
91
|
+
- test/hookable_test.rb
|
92
|
+
has_rdoc: true
|
93
|
+
homepage: http://rubygems.org/gems/hooked
|
94
|
+
licenses: []
|
95
|
+
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 3
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
requirements: []
|
120
|
+
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.3.7
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: Ruby library for Aspect Oriented Programming
|
126
|
+
test_files: []
|
127
|
+
|