uberhook 0.0.2 → 0.0.3

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.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