rhook 0.1.6 → 0.1.7
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/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
|