uberhook 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,22 +6,14 @@ The Uberhook gem allows you to define `before` and `after` hooks for your classe
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
10
-
11
- ```
12
- gem 'uberhook'
13
9
  ```
14
-
15
- And then execute:
16
-
17
- ```
18
- $ bundle
10
+ $ gem install uberhook
19
11
  ```
20
12
 
21
- Or install it yourself as:
13
+ Or add the following line to your application's Gemfile and run `$ bundle` afterwards.
22
14
 
23
15
  ```
24
- $ gem install uberhook
16
+ gem 'uberhook'
25
17
  ```
26
18
 
27
19
  ## Usage
@@ -30,16 +22,18 @@ $ gem install uberhook
30
22
  require 'uberhook'
31
23
 
32
24
  class Car
33
- include Uberhook
25
+ include Uberhook::Abstract
34
26
 
35
- before :drive, :start_engine
27
+ before :drive do
28
+ p 'start engine'
29
+ end
36
30
 
37
- def drive
38
- puts 'Now lets drive.'
31
+ after :drive do
32
+ p 'turn radio on'
39
33
  end
40
34
 
41
- def start_engine
42
- puts 'First start the engine.'
35
+ def drive
36
+ p 'drive'
43
37
  end
44
38
  end
45
39
 
@@ -47,6 +41,46 @@ beatle = Car.new
47
41
  beatle.drive
48
42
 
49
43
  # Output:
50
- # First start the engine.
51
- # And now lets drive.
44
+ # start engine
45
+ # drive
46
+ # turn radio on
47
+ ```
48
+
49
+ Use the `Uberhook::Base` module to define class hooks.
50
+
51
+ ``` ruby
52
+ require 'uberhook'
53
+
54
+ class Dance
55
+ include Uberhook::Base
56
+
57
+ instance_hook :after, :step_one do
58
+ p 'two!'
59
+ end
60
+
61
+ def step_one
62
+ p 'one!'
63
+ end
64
+
65
+ class_hook :before, :rock do
66
+ p 'prepare'
67
+ end
68
+
69
+ def self.rock
70
+ p 'and rock'
71
+ end
72
+ end
73
+
74
+ waltz = Dance.new
75
+ waltz.step_one
76
+
77
+ # Output:
78
+ # one!
79
+ # two!
80
+
81
+ Dance.rock
82
+
83
+ # Output:
84
+ # prepare
85
+ # and rock
52
86
  ```
data/Rakefile CHANGED
@@ -1,11 +1,10 @@
1
- require 'bundler/gem_tasks'
2
1
  require 'rake/testtask'
3
2
 
4
3
  task :default => :test
4
+ task :spec => :test
5
5
 
6
6
  Rake::TestTask.new do |t|
7
- t.libs << 'test'
8
- t.pattern = 'test/**/*_test.rb'
7
+ t.libs << 'spec'
8
+ t.pattern = 'spec/**/*_spec.rb'
9
9
  t.warning = true
10
- t.verbose = true
11
- end
10
+ end
@@ -0,0 +1,30 @@
1
+ module Uberhook
2
+ module Abstract
3
+ def self.included(base)
4
+ base.send :include, Uberhook::Base
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ # Define a before or after hook for a specific method.
10
+ #
11
+ # @example Define before hook.
12
+ # before :foo do
13
+ # p 'before callback'
14
+ # end
15
+ #
16
+ # @example Define after hook.
17
+ # after :foo do
18
+ # p 'after callback'
19
+ # end
20
+ #
21
+ # @param [ Symbol ] target The target method.
22
+ # @param [ Proc ] callback The callback block.
23
+ [:before, :after].each do |type|
24
+ define_method(type) do |target, &callback|
25
+ instance_hook(type, target, &callback)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,112 @@
1
+ module Uberhook
2
+ module Base
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ base.class_eval do
6
+ class << self
7
+ def method_added(name)
8
+ process_instance_method_added(name)
9
+ end
10
+
11
+ def singleton_method_added(name)
12
+ process_class_method_added(name)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ def instance_hook(type, target, &callback)
20
+ instance_hooks[target][type] = callback
21
+
22
+ # Define instance hook if target is defined and
23
+ # hook is not defined yet
24
+ define_instance_hook(target) if
25
+ method_defined?(target) and
26
+ instance_hooks.has_key?(target) and
27
+ not instance_hooks[target].has_key?(:defined)
28
+ end
29
+
30
+ def class_hook(type, target, &callback)
31
+ class_hooks[target][type] = callback
32
+
33
+ # Define class hook if target is defined and
34
+ # hook is not defined yet
35
+ define_class_hook(target) if
36
+ respond_to?(target) and
37
+ class_hooks.has_key?(target) and
38
+ not class_hooks[target].has_key?(:defined)
39
+ end
40
+
41
+ private
42
+
43
+ def instance_hooks
44
+ @instance_hooks ||= Hash.new { |hash, key| hash[key] = {} }
45
+ end
46
+
47
+ def class_hooks
48
+ @class_hooks ||= Hash.new { |hash, key| hash[key] = {} }
49
+ end
50
+
51
+ def process_instance_method_added(name)
52
+ return unless instance_hooks.has_key?(name) and
53
+ not instance_hooks[name].has_key?(:defined)
54
+
55
+ define_instance_hook(name)
56
+ end
57
+
58
+ def process_class_method_added(name)
59
+ return unless class_hooks.has_key?(name) and
60
+ not class_hooks[name].has_key?(:defined)
61
+
62
+ define_class_hook(name)
63
+ end
64
+
65
+ def define_instance_hook(target)
66
+ instance_hooks[target][:defined] = true
67
+ original_target = "#{target}_without_callbacks"
68
+
69
+ alias_method original_target, target
70
+ private original_target
71
+
72
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
73
+ def #{target}(*args, &block)
74
+ self.class.send(:run_instance_callback, :before, :#{target}, self)
75
+ retval = send(:#{original_target}, *args, &block)
76
+ self.class.send(:run_instance_callback, :after, :#{target}, self)
77
+ retval
78
+ end
79
+ RUBY_EVAL
80
+ end
81
+
82
+ def define_class_hook(target)
83
+ class_hooks[target][:defined] = true
84
+ original_target = "#{target}_without_callbacks"
85
+
86
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
87
+ class << self
88
+ alias_method :#{original_target}, :#{target}
89
+ private :#{original_target}
90
+
91
+ def #{target}(*args, &block)
92
+ self.send(:run_class_callback, :before, :#{target}, self)
93
+ retval = send(:#{original_target}, *args, &block)
94
+ self.send(:run_class_callback, :after, :#{target}, self)
95
+ retval
96
+ end
97
+ end
98
+ RUBY_EVAL
99
+ end
100
+
101
+ def run_instance_callback(type, target, scope)
102
+ return unless instance_hooks[target].has_key?(type)
103
+ scope.instance_eval(&instance_hooks[target][type])
104
+ end
105
+
106
+ def run_class_callback(type, target, scope)
107
+ return unless class_hooks[target].has_key?(type)
108
+ scope.instance_eval(&class_hooks[target][type])
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,3 @@
1
+ module Uberhook
2
+ VERSION = '0.0.3'
3
+ end
data/lib/uberhook.rb CHANGED
@@ -1,88 +1,3 @@
1
- # Include the Uberhook module to define 'before' and 'after' hooks
2
- # for your classes and models.
3
- #
4
- # class Car
5
- # include Uberhook
6
- #
7
- # before :drive, :start_engine
8
- #
9
- # def drive
10
- # puts 'Now lets drive.'
11
- # end
12
- #
13
- # def start_engine
14
- # puts 'First start the engine.'
15
- # end
16
- # end
17
- #
18
- # beatle = Car.new
19
- # beatle.drive
20
- #
21
- # Output:
22
- # First start the engine.
23
- # Now lets drive.
24
- module Uberhook
25
- def self.included(base)
26
- base.extend ClassMethods
27
- base.instance_variable_set :@hooks, {}
28
- end
29
-
30
- module ClassMethods
31
- def before(target, callback)
32
- register_hook :pre, target, callback
33
- end
34
-
35
- def after(target, callback)
36
- register_hook :post, target, callback
37
- end
38
-
39
- def register_hook(type, target, callback)
40
- if method_defined? target
41
- define_hook(type, target, callback)
42
- else
43
- add_hook_to_waitinglist(type, target, callback)
44
- end
45
- end
46
-
47
- private
48
-
49
- def define_hook(type, target, callback)
50
- alias_method "#{target}_without_callbacks", target
51
- if type == :pre
52
- define_method target do
53
- send callback
54
- send "#{target}_without_callbacks"
55
- end
56
- elsif type == :post
57
- define_method target do
58
- send "#{target}_without_callbacks"
59
- send callback
60
- end
61
- end
62
- end
63
-
64
- def add_hook_to_waitinglist(type, target, callback)
65
- @hooks[type] = {} unless @hooks[type].kind_of? Hash
66
- callbacks = @hooks[type][target]
67
- @hooks[type][target] = (callbacks || []) << callback
68
- end
69
-
70
- def remove_hook_from_waitinglist(type, target, callback)
71
- @hooks[type][target].delete callback
72
- end
73
-
74
- def perform_method_added(name)
75
- @hooks.each do |type, hook|
76
- next unless hook[name]
77
- hook[name].each do |callback|
78
- remove_hook_from_waitinglist(type, name, callback)
79
- define_hook(type, name, callback)
80
- end
81
- end
82
- end
83
-
84
- def method_added(name)
85
- perform_method_added(name)
86
- end
87
- end
88
- end
1
+ require 'uberhook/abstract'
2
+ require 'uberhook/base'
3
+ require 'uberhook/version'
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Uberhook::Abstract do
4
+ let(:klass) do
5
+ Class.new { include Uberhook::Abstract }
6
+ end
7
+
8
+ describe :before do
9
+ it 'should forward hook definition' do
10
+ klass.expects(:instance_hook).with(:before, :one).once
11
+ klass.class_eval { before :one }
12
+ end
13
+ end
14
+
15
+ describe :around do
16
+ it 'should forward hook definition'
17
+ end
18
+
19
+ describe :after do
20
+ it 'should forward hook definition' do
21
+ klass.expects(:instance_hook).with(:after, :one).once
22
+ klass.class_eval { after :one }
23
+ end
24
+ end
25
+ end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+
3
+ describe Uberhook::Base do
4
+ let(:klass) do
5
+ Class.new do
6
+ include Uberhook::Base
7
+
8
+ def one
9
+ @hook_calls << :one
10
+ end
11
+
12
+ def self.two
13
+ @hook_calls << :two
14
+ end
15
+ end
16
+ end
17
+
18
+ describe :instance_hook do
19
+ it 'should save a callback' do
20
+ klass.class_eval { instance_hook :before, :one }
21
+ hash = {:one => {:before => nil, :defined => true}}
22
+ klass.send(:instance_hooks).must_equal hash
23
+ end
24
+ end
25
+
26
+ it 'should forward a hook definition' do
27
+ klass.expects(:define_instance_hook).with(:one).once
28
+ klass.class_eval { instance_hook :before, :one }
29
+ end
30
+
31
+ it 'should not forward a unkown hook definition' do
32
+ klass.expects(:define_instance_hook).times(0)
33
+ klass.class_eval { instance_hook :before, :unkown }
34
+ end
35
+
36
+ it 'should excecute hooks' do
37
+ klass.class_eval do
38
+ attr_reader :hook_calls
39
+ def initialize
40
+ @hook_calls = []
41
+ end
42
+ instance_hook :before, :one do
43
+ @hook_calls << :before_one
44
+ end
45
+ instance_hook :after, :one do
46
+ @hook_calls << :after_one
47
+ end
48
+ end
49
+ inst = klass.new
50
+ inst.one
51
+ inst.hook_calls.must_equal [:before_one, :one, :after_one]
52
+ end
53
+
54
+ describe :class_hook do
55
+ it 'should save a callback' do
56
+ klass.class_eval { class_hook :before, :two }
57
+ hash = {:two => {:before => nil, :defined => true}}
58
+ klass.send(:class_hooks).must_equal hash
59
+ end
60
+
61
+ it 'should forward a hook definition' do
62
+ klass.expects(:define_class_hook).with(:two).once
63
+ klass.class_eval { class_hook :before, :two }
64
+ end
65
+
66
+ it 'should not forward a unkown hook definition' do
67
+ klass.expects(:define_class_hook).times(0)
68
+ klass.class_eval { class_hook :before, :unkown }
69
+ end
70
+
71
+ it 'should excecute hooks' do
72
+ klass.class_eval do
73
+ @hook_calls = []
74
+ class_hook :before, :two do
75
+ @hook_calls << :before_two
76
+ end
77
+ class_hook :after, :two do
78
+ @hook_calls << :after_two
79
+ end
80
+ end
81
+ hooks = [:before_two, :two, :after_two]
82
+ klass.two
83
+ klass.instance_variable_get(:@hook_calls).must_equal hooks
84
+ end
85
+ end
86
+
87
+ describe :process_instance_method_added do
88
+ it 'should excecute when methods get added' do
89
+ klass.expects(:process_instance_method_added).with(:three).once
90
+ klass.class_eval { def three; end }
91
+ end
92
+
93
+ it 'should continue when hooks get defined' do
94
+ klass.expects(:define_instance_hook).with(:three).once
95
+ klass.class_eval do
96
+ instance_hook :before, :three
97
+ def three; end
98
+ end
99
+ end
100
+
101
+ it 'should not continue without a hook' do
102
+ klass.expects(:define_instance_hook).times(0)
103
+ klass.class_eval { def three; end }
104
+ end
105
+ end
106
+
107
+ describe :process_class_method_added do
108
+ it 'should excecute when methods get added' do
109
+ skip 'Ignore methods like :singleton_method_added'
110
+ klass.expects(:process_class_method_added).with(:four).once
111
+ klass.class_eval { def self.four; end }
112
+ end
113
+
114
+ it 'should continue when hooks get defined' do
115
+ klass.expects(:define_class_hook).with(:four).once
116
+ klass.class_eval do
117
+ class_hook :before, :four
118
+ def self.four; end
119
+ end
120
+ end
121
+
122
+ it 'should not continue without a hook' do
123
+ klass.expects(:define_class_hook).times(0)
124
+ klass.class_eval { def self.four; end }
125
+ end
126
+ end
127
+
128
+ describe :define_instance_hook do
129
+ it 'should return the targets return value' do
130
+ klass.class_eval do
131
+ instance_hook :after, :three do
132
+ 'after'
133
+ end
134
+ def three
135
+ 'three'
136
+ end
137
+ end
138
+ klass.new.three.must_equal 'three'
139
+ end
140
+
141
+ it 'should make the original target method private' do
142
+ klass.class_eval { instance_hook :before, :one }
143
+ klass.new.private_methods.must_include :one_without_callbacks
144
+ end
145
+ end
146
+
147
+ describe :define_class_hook do
148
+ it 'should return the targets return value' do
149
+ klass.class_eval do
150
+ class_hook :after, :four do
151
+ 'after'
152
+ end
153
+ def self.four
154
+ 'four'
155
+ end
156
+ end
157
+ klass.four.must_equal 'four'
158
+ end
159
+
160
+ it 'should make the original target method private' do
161
+ klass.class_eval { class_hook :before, :two }
162
+ klass.private_methods.must_include :two_without_callbacks
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,4 @@
1
+ require 'minitest/autorun'
2
+ require 'mocha'
3
+
4
+ require 'uberhook'
data/uberhook.gemspec CHANGED
@@ -1,16 +1,19 @@
1
+ require File.expand_path('../lib/uberhook/version', __FILE__)
2
+
1
3
  Gem::Specification.new do |gem|
2
4
  gem.name = 'uberhook'
3
- gem.version = '0.0.2'
4
5
  gem.authors = ['Martin Jagusch']
5
6
  gem.email = ['_@mj.io']
7
+ gem.version = Uberhook::VERSION
6
8
  gem.description = 'Allows you to define :before and :after hooks for your classes and models.'
7
9
  gem.summary = ':before and :after hooks'
8
10
  gem.homepage = 'https://github.com/mjio/uberhook'
9
11
 
10
- gem.add_development_dependency 'rake'
11
- gem.add_development_dependency 'mocha'
12
+ gem.add_development_dependency 'rake', '~> 0.9.2.2'
13
+ gem.add_development_dependency 'mocha', '~> 0.12.3'
14
+ gem.add_development_dependency 'minitest', '~> 3.3.0'
12
15
 
13
16
  gem.files = `git ls-files`.split($/)
14
- gem.test_files = gem.files.grep(%r{^test/})
17
+ gem.test_files = gem.files.grep(%r{^spec/})
15
18
  gem.require_paths = ['lib']
16
19
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uberhook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,40 +9,56 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-08 00:00:00.000000000 Z
12
+ date: 2012-08-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 0.9.2.2
22
22
  type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ! '>='
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: '0'
29
+ version: 0.9.2.2
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: mocha
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
- - - ! '>='
35
+ - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: '0'
37
+ version: 0.12.3
38
38
  type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ! '>='
43
+ - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: '0'
45
+ version: 0.12.3
46
+ - !ruby/object:Gem::Dependency
47
+ name: minitest
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 3.3.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 3.3.0
46
62
  description: Allows you to define :before and :after hooks for your classes and models.
47
63
  email:
48
64
  - _@mj.io
@@ -57,8 +73,12 @@ files:
57
73
  - README.md
58
74
  - Rakefile
59
75
  - lib/uberhook.rb
60
- - test/test_helper.rb
61
- - test/uberhook_test.rb
76
+ - lib/uberhook/abstract.rb
77
+ - lib/uberhook/base.rb
78
+ - lib/uberhook/version.rb
79
+ - spec/abstract_spec.rb
80
+ - spec/base_spec.rb
81
+ - spec/spec_helper.rb
62
82
  - uberhook.gemspec
63
83
  homepage: https://github.com/mjio/uberhook
64
84
  licenses: []
@@ -85,5 +105,6 @@ signing_key:
85
105
  specification_version: 3
86
106
  summary: ':before and :after hooks'
87
107
  test_files:
88
- - test/test_helper.rb
89
- - test/uberhook_test.rb
108
+ - spec/abstract_spec.rb
109
+ - spec/base_spec.rb
110
+ - spec/spec_helper.rb
data/test/test_helper.rb DELETED
@@ -1,10 +0,0 @@
1
- require 'test/unit'
2
- require 'mocha'
3
-
4
- require 'uberhook'
5
-
6
- class Uberhook::TestCase < Test::Unit::TestCase
7
- def self.test(name, &block)
8
- define_method("test #{name.inspect}", &block)
9
- end
10
- end
@@ -1,112 +0,0 @@
1
- require 'test_helper'
2
-
3
- class UberhookTest < Uberhook::TestCase
4
- def setup
5
- @car = Class.new do
6
- attr_reader :status
7
- include Uberhook
8
- def drive; @status = :drive; end
9
- def start; @status = :start; end
10
- def stop; end
11
- end
12
- end
13
-
14
- test 'should register :pre hook' do
15
- @car.expects(:register_hook).with(:pre, :drive, :start).once
16
- @car.class_eval do
17
- before :drive, :start
18
- end
19
- end
20
-
21
- test 'should register :post hook' do
22
- @car.expects(:register_hook).with(:post, :drive, :stop).once
23
- @car.class_eval do
24
- after :drive, :stop
25
- end
26
- end
27
-
28
- test 'should save :pre hook temporally when target is not defined' do
29
- @car.expects(:add_hook_to_waitinglist).with(:pre, :two, :one).once
30
- @car.class_eval do
31
- register_hook :pre, :two, :one
32
- def one; end
33
- def two; end
34
- end
35
- end
36
-
37
- test 'should not define :pre hook when target is not defined' do
38
- @car.expects(:define_hook).times(0)
39
- @car.class_eval do
40
- register_hook :pre, :two, :one
41
- end
42
- end
43
-
44
- test 'should not define :post hook when target is not defined' do
45
- @car.expects(:define_hook).times(0)
46
- @car.class_eval do
47
- register_hook :post, :one, :two
48
- end
49
- end
50
-
51
- test 'should run callback before the hook method' do
52
- @car.class_eval do
53
- register_hook :pre, :drive, :start
54
- end
55
- beatle = @car.new
56
- beatle.expects(:start).once
57
- beatle.drive
58
- end
59
-
60
- test 'should run callback after the hook method' do
61
- @car.class_eval do
62
- register_hook :post, :start, :drive
63
- end
64
- beatle = @car.new
65
- beatle.expects(:drive).once
66
- beatle.start
67
- end
68
-
69
- test 'should run :pre callback when hook method gets defined' do
70
- @car.class_eval do
71
- register_hook :pre, :two, :one
72
- def one; end
73
- def two; end
74
- end
75
- beatle = @car.new
76
- beatle.expects(:one).once
77
- beatle.two
78
- end
79
-
80
- test 'should run :pre callback after hook method gets defined' do
81
- @car.class_eval do
82
- register_hook :pre, :two, :one
83
- def one; @status = :one; end
84
- def two; @status = :two; end
85
- end
86
- beatle = @car.new
87
- beatle.two
88
- assert_equal :two, beatle.status
89
- end
90
-
91
- test 'should run :post callback when hook method gets defined' do
92
- @car.class_eval do
93
- register_hook :post, :one, :two
94
- def one; end
95
- def two; end
96
- end
97
- beatle = @car.new
98
- beatle.expects(:two).once
99
- beatle.one
100
- end
101
-
102
- test 'should run :post callback after hook method gets defined' do
103
- @car.class_eval do
104
- register_hook :post, :one, :two
105
- def one; @status = :one; end
106
- def two; @status = :two; end
107
- end
108
- beatle = @car.new
109
- beatle.one
110
- assert_equal :two, beatle.status
111
- end
112
- end