hooked 0.1.2 → 0.2.0

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/spec/hook_spec.rb ADDED
@@ -0,0 +1,69 @@
1
+ require "spec_helper"
2
+
3
+ describe Hooked::Hook do
4
+ describe "#initialize" do
5
+ it "sets the pointcut and creates the graph" do
6
+ pointcut, graph = double("pointcut"), double("graph")
7
+ Hooked::Graph.stub :new => graph
8
+
9
+ obj = Hooked::Hook.new pointcut
10
+ obj.pointcut.should == pointcut
11
+ obj.graph.should == graph
12
+ end
13
+ end
14
+
15
+ describe "#add_aspect" do
16
+ it "adds an aspect to the graph" do
17
+ args = [:before, proc {}, {:after => [proc {}]}]
18
+ aspect = double "aspect"
19
+ obj = Hooked::Hook.new double("pointcut")
20
+
21
+ Hooked::Aspect.should_receive(:new).with(*args).and_return aspect
22
+ obj.graph.should_receive(:<<).with aspect
23
+ obj.add_aspect *args
24
+ end
25
+ end
26
+
27
+ describe "#call" do
28
+ it "rebuilds the chain if it hasn't been built yet or the graph changed" do
29
+ obj = Hooked::Hook.new double("pointcut")
30
+ chain = stub "chain", :call => nil
31
+
32
+ obj.graph.stub :changed? => false
33
+ obj.should_receive(:build_chain).once.and_return chain
34
+ obj.call
35
+
36
+ obj.call
37
+
38
+ obj.graph.stub :changed? => true
39
+ obj.should_receive(:build_chain).once.and_return chain
40
+ obj.call
41
+ end
42
+
43
+ it "forwards to the chain" do
44
+ obj = Hooked::Hook.new double("pointcut")
45
+
46
+ chain = stub "chain"
47
+ obj.stub :build_chain => chain
48
+ chain.should_receive(:call).with("foo").and_return "bar"
49
+
50
+ obj.call("foo").should == "bar"
51
+ end
52
+ end
53
+
54
+ describe "#build_chain" do
55
+ it "builds the chain" do
56
+ obj = Hooked::Hook.new double("pointcut")
57
+
58
+ aspect = Struct.new :pos, :pointcut
59
+ obj.graph.should_receive :sort
60
+ obj.graph.stub :output => [aspect.new(1), aspect.new(2), aspect.new(3)]
61
+ chain = obj.build_chain
62
+
63
+ chain.pos.should == 1
64
+ chain.pointcut.pos.should == 2
65
+ chain.pointcut.pointcut.pos.should == 3
66
+ chain.pointcut.pointcut.pointcut.should == obj.pointcut
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+
3
+ describe Hooked do
4
+ klass = Class.new do
5
+ include Hooked
6
+ def foo; end
7
+ end
8
+
9
+ [:before, :after, :around].each do |type|
10
+ describe "##{type}" do
11
+ it "hooks the method" do
12
+ obj = klass.new
13
+ obj.stub :hooked => {:foo => double("hooked", :add_aspect => nil)}
14
+
15
+ obj.should_receive(:hook!).with(:foo)
16
+ obj.send type, :foo, nil
17
+ end
18
+
19
+ it "adds an aspect to the hook" do
20
+ obj = klass.new
21
+ advice, deps = stub("advice"), stub("deps")
22
+
23
+ obj.hook! :foo
24
+ obj.hooked[:foo].should_receive(:add_aspect).with type, advice, deps
25
+
26
+ obj.send type, :foo, advice, deps
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#hook!" do
32
+ it "replaces the original method with a hook" do
33
+ obj = klass.new
34
+ args, result = ["bar", 123], stub("result")
35
+
36
+ obj.hook! :foo
37
+ obj.hooked[:foo].should_receive(:call).with(*args).and_return result
38
+ obj.foo(*args).should == result
39
+ end
40
+
41
+ it "doesn't hook a method twice" do
42
+ obj = klass.new
43
+ obj.hook! :foo
44
+
45
+ Hooked::Hook.should_not_receive :new
46
+ obj.hook! :foo
47
+ end
48
+ end
49
+
50
+ describe "#unhook!" do
51
+ it "brings the original back in place" do
52
+ obj = klass.new
53
+
54
+ obj.hook! :foo
55
+ hook = obj.hooked[:foo]
56
+ obj.unhook! :foo
57
+
58
+ hook.should_not_receive :call
59
+ obj.foo
60
+
61
+ obj.hooked[:foo].should be_nil
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,6 @@
1
+ require "bundler"
2
+ Bundler.setup :default, :development
3
+
4
+ require "hooked"
5
+
6
+ require "awesome_print"
metadata CHANGED
@@ -1,64 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hooked
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 1
8
- - 2
9
- version: 0.1.2
4
+ prerelease:
5
+ version: 0.2.0
10
6
  platform: ruby
11
7
  authors:
12
- - Lars Gierth
8
+ - Lars Gierth
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2011-01-13 00:00:00 +01:00
13
+ date: 2011-04-20 00:00:00 +02:00
18
14
  default_executable:
19
15
  dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: depression
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 0
29
- version: "0"
30
- type: :runtime
31
- version_requirements: *id001
32
- - !ruby/object:Gem::Dependency
33
- name: test-unit
34
- prerelease: false
35
- requirement: &id002 !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- segments:
40
- - 0
41
- version: "0"
42
- type: :development
43
- version_requirements: *id002
44
- - !ruby/object:Gem::Dependency
45
- name: mocha
46
- prerelease: false
47
- requirement: &id003 !ruby/object:Gem::Requirement
48
- requirements:
49
- - - ">="
50
- - !ruby/object:Gem::Version
51
- segments:
52
- - 0
53
- version: "0"
54
- type: :development
55
- version_requirements: *id003
56
- description: |-
57
- Hooked makes AOP a breeze. It lets you define and invoke
58
- code that gems or other parts of your application can hook
59
- onto.
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ description: Hooked lets you transparently aspectify your methods and blocks.
60
28
  email:
61
- - lars.gierth@gmail.com
29
+ - lars.gierth@gmail.com
62
30
  executables: []
63
31
 
64
32
  extensions: []
@@ -66,23 +34,22 @@ extensions: []
66
34
  extra_rdoc_files: []
67
35
 
68
36
  files:
69
- - Gemfile
70
- - LICENSE
71
- - README.md
72
- - Rakefile
73
- - hooked.gemspec
74
- - lib/hooked.rb
75
- - lib/hooked/container.rb
76
- - lib/hooked/context.rb
77
- - lib/hooked/controller.rb
78
- - lib/hooked/hook.rb
79
- - lib/hooked/hookable.rb
80
- - lib/hooked/version.rb
81
- - test/container_test.rb
82
- - test/controller_test.rb
83
- - test/helper.rb
84
- - test/hook_test.rb
85
- - test/hookable_test.rb
37
+ - .rspec
38
+ - Gemfile
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - hooked.gemspec
43
+ - lib/hooked.rb
44
+ - lib/hooked/aspect.rb
45
+ - lib/hooked/graph.rb
46
+ - lib/hooked/hook.rb
47
+ - lib/hooked/version.rb
48
+ - spec/aspect_spec.rb
49
+ - spec/graph_spec.rb
50
+ - spec/hook_spec.rb
51
+ - spec/hooked_spec.rb
52
+ - spec/spec_helper.rb
86
53
  has_rdoc: true
87
54
  homepage: http://rubygems.org/gems/hooked
88
55
  licenses: []
@@ -91,31 +58,31 @@ post_install_message:
91
58
  rdoc_options: []
92
59
 
93
60
  require_paths:
94
- - lib
61
+ - lib
95
62
  required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
96
64
  requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- segments:
100
- - 0
101
- version: "0"
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 208393711
68
+ segments:
69
+ - 0
70
+ version: "0"
102
71
  required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
103
73
  requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- segments:
107
- - 0
108
- version: "0"
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 208393711
77
+ segments:
78
+ - 0
79
+ version: "0"
109
80
  requirements: []
110
81
 
111
82
  rubyforge_project:
112
- rubygems_version: 1.3.6
83
+ rubygems_version: 1.6.2
113
84
  signing_key:
114
85
  specification_version: 3
115
- summary: Ruby Library For Aspect Oriented Programming
116
- test_files:
117
- - test/container_test.rb
118
- - test/controller_test.rb
119
- - test/helper.rb
120
- - test/hook_test.rb
121
- - test/hookable_test.rb
86
+ summary: Aspect Orientation Made Simple
87
+ test_files: []
88
+
@@ -1,52 +0,0 @@
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
@@ -1,39 +0,0 @@
1
- module Hooked
2
- class Context
3
- attr_reader :args
4
- attr_accessor :result
5
-
6
- def initialize(args)
7
- @args = args
8
- end
9
-
10
- def next(depth = 1)
11
- return if depth < 1
12
- throw(:hooked, {
13
- :type => :next,
14
- :depth => depth
15
- })
16
- end
17
-
18
- def return(result)
19
- @result = result
20
- self.next
21
- end
22
-
23
- def break(depth = 1)
24
- return if depth < 1
25
- throw(:hooked, {
26
- :type => :break,
27
- :depth => depth
28
- })
29
- end
30
-
31
- def skip(*hooks)
32
- raise "Not yet implemented."
33
- end
34
-
35
- def skip_other(hookable, *hooks)
36
- raise "Not yet implemented."
37
- end
38
- end
39
- end
@@ -1,80 +0,0 @@
1
- module Hooked
2
- Result = Struct.new(:type, :value)
3
-
4
- class Controller
5
- attr_reader :containers, :hookables, :hooks
6
-
7
- def initialize(*containers)
8
- @containers = []
9
- @hookables = {}
10
- @hooks = {}
11
-
12
- add(*containers)
13
- refresh
14
- end
15
-
16
- def add(*containers)
17
- @containers.push(*containers)
18
- containers.each do |c|
19
- c.hooked = self
20
- c.class.hookables.each do |hkbl|
21
- hkbl.container = c
22
- hookables[hkbl.name] = hkbl
23
- end
24
- c.class.hooks.each do |hkbl_name, hs|
25
- hs.each {|h| h.container = c }
26
- @hooks[hkbl_name] ||= []
27
- @hooks[hkbl_name].push(*hs)
28
- end
29
- end
30
- end
31
-
32
- def refresh
33
- hooks.each {|hkbl_name, hs| hooks[hkbl_name] = Depression.process(hs) }
34
- end
35
-
36
- def invoke(name, args = nil, &block)
37
- if block
38
- hookable = Hookable.new(name, &block)
39
- else
40
- hookable = hookables[name] || raise("Unknown hookable: #{name}")
41
- end
42
-
43
- context = Context.new(args)
44
- chain = (hooks[name] || []).reverse.inject(hookable) do |next_item, item|
45
- proc {|ctx| call_hook(item, next_item, ctx) }
46
- end
47
-
48
- ctrl = catch(:hooked) { chain.call(context); nil }
49
-
50
- if ctrl.respond_to?(:[])
51
- ctrl[:depth] -= 1
52
- throw(:hooked, ctrl) if ctrl[:depth] > 0
53
- end
54
-
55
- context.result
56
- end
57
-
58
- def call_hook(hook, hookable, context)
59
- wrap { hook.call(context) } if hook.before?
60
-
61
- wrap do
62
- if hook.around?
63
- hook.call(context, hookable)
64
- else
65
- hookable.call(context)
66
- end
67
- end
68
-
69
- wrap { hook.call(context) } if hook.after?
70
- end
71
-
72
- def wrap(&block)
73
- ctrl = catch(:hooked) { block.call; nil }
74
- if ctrl.respond_to?(:[]) && (
75
- ctrl[:type] == :break || ctrl[:depth] > 1)
76
- throw(:hooked, ctrl)
77
- end
78
- end
79
- end
80
- end
@@ -1,20 +0,0 @@
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(context)
13
- if container
14
- container.instance_exec(context, &@block)
15
- else
16
- @block.call(context)
17
- end
18
- end
19
- end
20
- end
@@ -1,71 +0,0 @@
1
- require File.expand_path("../helper", __FILE__)
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