rhook 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/examples/benchmark_empty_method.rb +43 -0
- data/examples/benchmark_logger.rb +38 -0
- data/lib/rhook.rb +32 -4
- data/rhook.gemspec +6 -2
- data/spec/rhook_minor_spec.rb +66 -1
- data/spec/rhook_spec.rb +34 -22
- data/spec/spec_helper.rb +0 -41
- metadata +8 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.7
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
# $LOAD_PATH.unshift(File.dirname(__FILE__)+"/../lib")
|
3
|
+
require "rhook"
|
4
|
+
|
5
|
+
require "benchmark"
|
6
|
+
|
7
|
+
class Target
|
8
|
+
def target
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
n = 30000
|
14
|
+
Benchmark.bm(24) do |x|
|
15
|
+
t = Target.new
|
16
|
+
|
17
|
+
doit = lambda do
|
18
|
+
n.times do
|
19
|
+
t.target
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# ========================================================================
|
24
|
+
x.report("direct:") do
|
25
|
+
doit.call
|
26
|
+
end
|
27
|
+
|
28
|
+
# ========================================================================
|
29
|
+
hook = Target._rhook.hack(:target) do |inv|
|
30
|
+
inv.call
|
31
|
+
end
|
32
|
+
|
33
|
+
x.report("rhook enabled:") do
|
34
|
+
doit.call
|
35
|
+
end
|
36
|
+
|
37
|
+
# ========================================================================
|
38
|
+
hook.disable
|
39
|
+
|
40
|
+
x.report("rhook disabled:") do
|
41
|
+
doit.call
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
# $LOAD_PATH.unshift(File.dirname(__FILE__)+"/../lib")
|
3
|
+
require "rhook"
|
4
|
+
|
5
|
+
require "benchmark"
|
6
|
+
require "logger"
|
7
|
+
|
8
|
+
n = 30000
|
9
|
+
Benchmark.bm(24) do |x|
|
10
|
+
logger = Logger.new(open("/dev/null", "w"))
|
11
|
+
|
12
|
+
doit = lambda do
|
13
|
+
n.times do
|
14
|
+
logger.info("msg")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# ========================================================================
|
19
|
+
x.report("direct:") do
|
20
|
+
doit.call
|
21
|
+
end
|
22
|
+
|
23
|
+
# ========================================================================
|
24
|
+
hook = Logger._rhook.hack(:add) do |inv|
|
25
|
+
inv.call
|
26
|
+
end
|
27
|
+
|
28
|
+
x.report("rhook enabled:") do
|
29
|
+
doit.call
|
30
|
+
end
|
31
|
+
|
32
|
+
# ========================================================================
|
33
|
+
hook.disable
|
34
|
+
|
35
|
+
x.report("rhook disabled:") do
|
36
|
+
doit.call
|
37
|
+
end
|
38
|
+
end
|
data/lib/rhook.rb
CHANGED
@@ -21,6 +21,8 @@ module RHook
|
|
21
21
|
class RHookService
|
22
22
|
# @private
|
23
23
|
attr_reader :hooks_map
|
24
|
+
# @private
|
25
|
+
attr_accessor :last_name_call_method_done
|
24
26
|
|
25
27
|
# @private
|
26
28
|
def initialize(obj)
|
@@ -42,7 +44,8 @@ module RHook
|
|
42
44
|
# Add hook to the {#bound_object}. If it is a kind of Class, the hook is affected to all instance & subclasses of the class.
|
43
45
|
#
|
44
46
|
# @param [Symbol] name hook-point's name (commonly equals to method name)
|
45
|
-
# @option opt [true] :disable
|
47
|
+
# @option opt [true] :disable Creates hook but make disabled. (by default, automatically enabled.)
|
48
|
+
# @option opt [RHook::HookGroup] :group Adds itself into the specified hook-group.
|
46
49
|
# @yield [inv] The hook block.
|
47
50
|
# @yieldparam [Invocation] inv
|
48
51
|
# @yieldreturn The result value. (Returned value of called method.)
|
@@ -53,7 +56,11 @@ module RHook
|
|
53
56
|
(@hooks_map[name.to_sym] ||= []).unshift( hook )
|
54
57
|
RHook.registry.class_cached_flag_map.delete(name)
|
55
58
|
opt[:disable] or hook.enable()
|
56
|
-
|
59
|
+
if opt[:group]
|
60
|
+
opt[:group].add(hook)
|
61
|
+
else
|
62
|
+
HookGroup.add_to_current_groups(hook)
|
63
|
+
end
|
57
64
|
hook
|
58
65
|
end
|
59
66
|
|
@@ -124,6 +131,11 @@ module RHook
|
|
124
131
|
|
125
132
|
# @private
|
126
133
|
def call_method(name, method_name, args, block, opt = {})
|
134
|
+
if @last_name_call_method_done == name
|
135
|
+
return @obj.__send__(method_name, *args, &block)
|
136
|
+
end
|
137
|
+
@last_name_call_method_done = name
|
138
|
+
|
127
139
|
hooks = concat_hooks([], name)
|
128
140
|
hooks.empty? and return @obj.__send__(method_name, *args, &block)
|
129
141
|
|
@@ -133,9 +145,13 @@ module RHook
|
|
133
145
|
inv.args = args
|
134
146
|
inv.block = block
|
135
147
|
inv.hooks = hooks
|
136
|
-
inv.target_proc =
|
148
|
+
inv.target_proc = lambda do |*args, &block|
|
149
|
+
@obj.__send__(method_name, *args, &block)
|
150
|
+
end
|
137
151
|
inv.hint = opt[:hint] || {}
|
138
152
|
inv.proceed()
|
153
|
+
ensure
|
154
|
+
@last_name_call_method_done = nil
|
139
155
|
end
|
140
156
|
|
141
157
|
# Wraps the code block to be hookable.
|
@@ -398,6 +414,12 @@ module RHook
|
|
398
414
|
self
|
399
415
|
end
|
400
416
|
|
417
|
+
# Tests the given hook is registered in this group.
|
418
|
+
# @return [Boolean]
|
419
|
+
def include?(hook)
|
420
|
+
@hooks.include?(hook)
|
421
|
+
end
|
422
|
+
|
401
423
|
# @private
|
402
424
|
def self.add_to_current_groups(hook)
|
403
425
|
(Thread.current["rhook_group"] || []).each do |group|
|
@@ -415,8 +437,14 @@ module RHook
|
|
415
437
|
# }
|
416
438
|
# XXX_feature_for_library.disable
|
417
439
|
#
|
440
|
+
# @example
|
441
|
+
# grp = RHook.group
|
442
|
+
# Target._rhook.bind(..., :group => grp) { ... }
|
443
|
+
#
|
418
444
|
# @return [HookGroup]
|
419
445
|
def self.group(&block)
|
420
|
-
HookGroup.new
|
446
|
+
group = HookGroup.new
|
447
|
+
block and group.wrap(&block)
|
448
|
+
group
|
421
449
|
end
|
422
450
|
end
|
data/rhook.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rhook}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kaoru Kobo"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-01-14}
|
13
13
|
s.description = %q{You can provide hook point in your code, and can customize its behavior from outside. Also you can 'hack' (== injecting hook point from outside) any methods in existing code.}
|
14
14
|
s.email = %q{}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -23,6 +23,8 @@ Gem::Specification.new do |s|
|
|
23
23
|
"README.rdoc",
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
|
+
"examples/benchmark_empty_method.rb",
|
27
|
+
"examples/benchmark_logger.rb",
|
26
28
|
"examples/log_socket_data.rb",
|
27
29
|
"lib/rhook.rb",
|
28
30
|
"rhook.gemspec",
|
@@ -38,6 +40,8 @@ Gem::Specification.new do |s|
|
|
38
40
|
s.rubygems_version = %q{1.3.7}
|
39
41
|
s.summary = %q{Easily drive AOP & hacking existing library with Ruby}
|
40
42
|
s.test_files = [
|
43
|
+
"examples/benchmark_empty_method.rb",
|
44
|
+
"examples/benchmark_logger.rb",
|
41
45
|
"examples/log_socket_data.rb",
|
42
46
|
"spec/examples/log_buffer_example_spec.rb",
|
43
47
|
"spec/rhook_minor_spec.rb",
|
data/spec/rhook_minor_spec.rb
CHANGED
@@ -31,7 +31,7 @@ describe "rhook (minor specifications / behavior, and bugs)" do
|
|
31
31
|
# [bug] It calls the target method/procedure twice when no hooks are registered.
|
32
32
|
t.order_call
|
33
33
|
t.order_ary.should == ["orig"]
|
34
|
-
|
34
|
+
|
35
35
|
t._rhook.bind(:order_target) do |inv|
|
36
36
|
t.order_ary << "before_call"
|
37
37
|
inv.call
|
@@ -119,6 +119,71 @@ describe "rhook (minor specifications / behavior, and bugs)" do
|
|
119
119
|
result = Target._rhook.on_method(:on_method_test_non_existent, :ifdef => true)
|
120
120
|
result.should == true
|
121
121
|
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
# ========================================================================
|
126
|
+
describe "is applied to the method (== 'hack'ed), and also it is called via _rhook.to(), " do
|
127
|
+
class Target
|
128
|
+
def both_on_method_and_to
|
129
|
+
_rhook.to.both_on_method_and_to_target
|
130
|
+
end
|
131
|
+
|
132
|
+
def both_on_method_and_to_target
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
example "even though, the hook should be just called once." do
|
138
|
+
m = mock
|
139
|
+
m.should_receive(:called).once
|
140
|
+
|
141
|
+
Target._rhook.hack(:both_on_method_and_to_target) { |inv|
|
142
|
+
m.called
|
143
|
+
inv.call
|
144
|
+
}
|
145
|
+
Target.new.both_on_method_and_to()
|
146
|
+
end
|
122
147
|
end
|
123
148
|
end
|
149
|
+
|
150
|
+
# ========================================================================
|
151
|
+
describe "#call_method(#to): " do
|
152
|
+
before :each do
|
153
|
+
end
|
154
|
+
|
155
|
+
example "[bugfix] super: no superclass method" do
|
156
|
+
class CallMethodSuperTest_Super
|
157
|
+
def the_method
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
module CallMethodSuperTest_Module
|
163
|
+
def the_method
|
164
|
+
super
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class CallMethodSuperTest_Inherited < CallMethodSuperTest_Super
|
169
|
+
include CallMethodSuperTest_Module
|
170
|
+
end
|
171
|
+
|
172
|
+
# This bug does not appear without hooks.
|
173
|
+
CallMethodSuperTest_Inherited._rhook.bind(:the_method) do |inv|
|
174
|
+
inv.call
|
175
|
+
end
|
176
|
+
|
177
|
+
# OK.
|
178
|
+
CallMethodSuperTest_Inherited.new.the_method
|
179
|
+
|
180
|
+
# We fixed the bug this cause error:
|
181
|
+
lambda {
|
182
|
+
CallMethodSuperTest_Inherited.new._rhook.to.the_method
|
183
|
+
}.should_not raise_error
|
184
|
+
# - super: no superclass method `the_method'
|
185
|
+
end
|
186
|
+
end
|
187
|
+
# ========================================================================
|
188
|
+
|
124
189
|
end
|
data/spec/rhook_spec.rb
CHANGED
@@ -213,6 +213,16 @@ describe "rhook (advanced usage)" do
|
|
213
213
|
t.group_1.should == "hack1"
|
214
214
|
t.group_2.should == "hack2"
|
215
215
|
end
|
216
|
+
|
217
|
+
example "Adding a hook to group with bind()'s :group option." do
|
218
|
+
# Instantiate a group with RHook.group() without block.
|
219
|
+
group = RHook.group
|
220
|
+
t = Target.new
|
221
|
+
hook = t._rhook.hack(:group_1, :group => group) do |inv|
|
222
|
+
inv.call
|
223
|
+
end
|
224
|
+
group.include?(hook).should be_true
|
225
|
+
end
|
216
226
|
end
|
217
227
|
# ================================================================
|
218
228
|
|
@@ -234,29 +244,31 @@ describe "rhook (advanced usage)" do
|
|
234
244
|
|
235
245
|
end
|
236
246
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
247
|
+
m = mock
|
248
|
+
m.should_receive(:second).once.ordered
|
249
|
+
m.should_receive(:first).once.ordered
|
250
|
+
|
251
|
+
t = Target.new
|
252
|
+
t._rhook.bind(:invocation_object_target) { |inv|
|
253
|
+
# first hook
|
254
|
+
m.first
|
255
|
+
inv.call()
|
256
|
+
inv.returned.should == :returned_value
|
244
257
|
:returned_value_hooked
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
}.should calls_hook(:times => 2)
|
258
|
+
}
|
259
|
+
t._rhook.bind(:invocation_object_target) { |inv|
|
260
|
+
# second hook
|
261
|
+
m.second
|
262
|
+
inv.args.should == args
|
263
|
+
inv.block.should == block
|
264
|
+
inv.hint[:hintkey].should == :hintval
|
265
|
+
inv.receiver.should == t
|
266
|
+
inv.target.should == t
|
267
|
+
inv.returned.should be_nil
|
268
|
+
inv.call()
|
269
|
+
inv.returned.should == :returned_value_hooked
|
270
|
+
}
|
271
|
+
t.invocation_object(*args, &block)
|
260
272
|
end
|
261
273
|
end
|
262
274
|
# ========================================================================
|
data/spec/spec_helper.rb
CHANGED
@@ -11,44 +11,3 @@ Spec::Runner.configure do |config|
|
|
11
11
|
|
12
12
|
end
|
13
13
|
|
14
|
-
# ========================================================================
|
15
|
-
# Defines custom matcher to test the hook was called.
|
16
|
-
|
17
|
-
module ::Spec::Matchers
|
18
|
-
class HookCalledMatcher
|
19
|
-
def initialize(opt)
|
20
|
-
@times = opt[:times]
|
21
|
-
@count = 0
|
22
|
-
end
|
23
|
-
|
24
|
-
def yes
|
25
|
-
@count += 1
|
26
|
-
end
|
27
|
-
|
28
|
-
def matches?(block)
|
29
|
-
block.call(self)
|
30
|
-
if @times
|
31
|
-
begin
|
32
|
-
@count.should == @times
|
33
|
-
rescue
|
34
|
-
@times_msg = " (Exactly called count: #{$!})"
|
35
|
-
false
|
36
|
-
end
|
37
|
-
else
|
38
|
-
@count > 0
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def failure_message
|
43
|
-
"Expected the hook to be called.#{@times_msg}"
|
44
|
-
end
|
45
|
-
|
46
|
-
def negative_failure_message
|
47
|
-
"Expected the hook not to be called.#{@times_msg}"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def calls_hook(opt = {})
|
52
|
-
HookCalledMatcher.new(opt)
|
53
|
-
end
|
54
|
-
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rhook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 7
|
10
|
+
version: 0.1.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kaoru Kobo
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-14 00:00:00 +09:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -66,6 +66,8 @@ files:
|
|
66
66
|
- README.rdoc
|
67
67
|
- Rakefile
|
68
68
|
- VERSION
|
69
|
+
- examples/benchmark_empty_method.rb
|
70
|
+
- examples/benchmark_logger.rb
|
69
71
|
- examples/log_socket_data.rb
|
70
72
|
- lib/rhook.rb
|
71
73
|
- rhook.gemspec
|
@@ -109,6 +111,8 @@ signing_key:
|
|
109
111
|
specification_version: 3
|
110
112
|
summary: Easily drive AOP & hacking existing library with Ruby
|
111
113
|
test_files:
|
114
|
+
- examples/benchmark_empty_method.rb
|
115
|
+
- examples/benchmark_logger.rb
|
112
116
|
- examples/log_socket_data.rb
|
113
117
|
- spec/examples/log_buffer_example_spec.rb
|
114
118
|
- spec/rhook_minor_spec.rb
|