evalhook 0.2.0 → 0.3.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/CHANGELOG +2 -0
- data/Rakefile +4 -4
- data/lib/evalhook/hook_context.rb +144 -0
- data/lib/evalhook/hook_handler.rb +35 -0
- data/lib/evalhook/multi_hook_handler.rb +1 -1
- data/lib/evalhook.rb +44 -30
- data/spec/hook_handler/hook_handler_arguments_spec.rb +15 -0
- data/spec/hook_handler/hook_handler_defaults_spec.rb +230 -0
- data/spec/hook_handler/hook_handler_hook_spec.rb +46 -0
- data/spec/hook_handler/hook_handler_multiple_redirect_spec.rb +67 -0
- data/spec/hook_handler/hook_handler_multiple_spec.rb +99 -0
- data/spec/hook_handler/hook_handler_ruby_spec.rb +47 -0
- data/spec/hook_handler/hook_handler_visitor_spec.rb +103 -0
- data/spec/validation/hook_handler_spec.rb +19 -0
- metadata +33 -9
- data/ext/evalhook_base/evalhook_base.c +0 -795
- data/ext/evalhook_base/extconf.rb +0 -7
data/CHANGELOG
CHANGED
data/Rakefile
CHANGED
@@ -6,18 +6,18 @@ require 'rake/gempackagetask'
|
|
6
6
|
|
7
7
|
spec = Gem::Specification.new do |s|
|
8
8
|
s.name = 'evalhook'
|
9
|
-
s.version = '0.
|
9
|
+
s.version = '0.3.0'
|
10
10
|
s.author = 'Dario Seminara'
|
11
11
|
s.email = 'robertodarioseminara@gmail.com'
|
12
12
|
s.platform = Gem::Platform::RUBY
|
13
13
|
s.summary = 'Alternate eval which hook all methods executed in the evaluated code'
|
14
14
|
s.homepage = "http://github.com/tario/evalhook"
|
15
|
-
s.add_dependency "
|
15
|
+
s.add_dependency "partialruby", ">= 0.1.0"
|
16
|
+
s.add_dependency "ruby_parser", ">= 2.0.6"
|
16
17
|
s.has_rdoc = true
|
17
18
|
s.extra_rdoc_files = [ 'README' ]
|
18
19
|
s.rdoc_options << '--main' << 'README'
|
19
|
-
s.
|
20
|
-
s.files = Dir.glob("{examples,lib,test}/**/*.rb") + Dir.glob("ext/**/*.c") + Dir.glob("ext/**/*.h") + Dir.glob("ext/**/extconf.rb") +
|
20
|
+
s.files = Dir.glob("{examples,lib,spec}/**/*.rb") + Dir.glob("ext/**/*.c") + Dir.glob("ext/**/*.h") + Dir.glob("ext/**/extconf.rb") +
|
21
21
|
[ 'LICENSE', 'AUTHORS', 'CHANGELOG', 'README', 'Rakefile', 'TODO' ]
|
22
22
|
end
|
23
23
|
|
@@ -0,0 +1,144 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the evalhook project, http://github.com/tario/evalhook
|
4
|
+
|
5
|
+
Copyright (c) 2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
evalhook is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
evalhook is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with evalhook. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
require "ruby_parser"
|
22
|
+
require "partialruby"
|
23
|
+
|
24
|
+
module EvalHook
|
25
|
+
class HookContext < PartialRuby::PureRubyContext
|
26
|
+
|
27
|
+
def initialize(hook_handler)
|
28
|
+
@hook_handler = hook_handler
|
29
|
+
end
|
30
|
+
|
31
|
+
def const_path_emul(code)
|
32
|
+
if code.instance_of? Array
|
33
|
+
if (code.size == 1)
|
34
|
+
s(:const, code[-1].to_sym)
|
35
|
+
else
|
36
|
+
s(:colon2, const_path_emul(code[0..-2]), code[-1].to_sym)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
const_path_emul code.split("::")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def ruby_emul_colon3(tree)
|
44
|
+
if @hook_handler.base_namespace
|
45
|
+
emul s(:colon2, const_path_emul(@hook_handler.base_namespace.to_s), tree[1])
|
46
|
+
else
|
47
|
+
super tree
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ruby_emul_call(tree)
|
52
|
+
|
53
|
+
method_name = tree[2]
|
54
|
+
|
55
|
+
if tree.respond_to?(:is_marked?)
|
56
|
+
return super(tree)
|
57
|
+
end
|
58
|
+
|
59
|
+
args1 = s(:arglist, s(:lit, method_name), marked(s(:call, nil, :binding, s(:arglist))))
|
60
|
+
|
61
|
+
args2 = s(:arglist, s(:lit, @hook_handler))
|
62
|
+
|
63
|
+
receiver = tree[1] || s(:self)
|
64
|
+
|
65
|
+
firstcall = nil
|
66
|
+
secondcall = nil
|
67
|
+
|
68
|
+
if tree[1]
|
69
|
+
firstcall = marked(s(:call, receiver, :local_hooked_method, args1))
|
70
|
+
secondcall = marked(s(:call, firstcall, :set_hook_handler, args2))
|
71
|
+
else
|
72
|
+
firstcall = marked(s(:call, receiver, :hooked_method, args1))
|
73
|
+
secondcall = marked(s(:call, firstcall, :set_hook_handler, args2))
|
74
|
+
end
|
75
|
+
|
76
|
+
super marked(s(:call, secondcall, :call, tree[3]))
|
77
|
+
end
|
78
|
+
|
79
|
+
def ruby_emul_dxstr(tree)
|
80
|
+
|
81
|
+
dstr_tree = tree.dup
|
82
|
+
dstr_tree[0] = :dstr
|
83
|
+
|
84
|
+
args = s(:arglist, dstr_tree )
|
85
|
+
|
86
|
+
emul marked(s(:call, s(:lit, @hook_handler), :hooked_xstr, args))
|
87
|
+
end
|
88
|
+
|
89
|
+
def ruby_emul_xstr(tree)
|
90
|
+
args = s(:arglist, s(:lit, tree[1]) )
|
91
|
+
|
92
|
+
emul marked(s(:call, s(:lit, @hook_handler), :hooked_xstr, args))
|
93
|
+
end
|
94
|
+
|
95
|
+
def ruby_emul_cdecl(tree)
|
96
|
+
const_tree = tree[1]
|
97
|
+
value_tree = tree[2]
|
98
|
+
|
99
|
+
base_class_tree = nil
|
100
|
+
const_id = nil
|
101
|
+
|
102
|
+
unless const_tree.instance_of? Symbol
|
103
|
+
if const_tree[0] == :colon2
|
104
|
+
base_class_tree = const_tree[1]
|
105
|
+
const_id = const_tree[2]
|
106
|
+
elsif const_tree[0] == :colon3
|
107
|
+
base_class_tree = s(:lit, Object)
|
108
|
+
const_id = const_tree[1]
|
109
|
+
end
|
110
|
+
else
|
111
|
+
base_class_tree = s(:lit, Object)
|
112
|
+
const_id = const_tree
|
113
|
+
end
|
114
|
+
|
115
|
+
args1 = s(:arglist, base_class_tree)
|
116
|
+
args2 = s(:arglist, s(:lit, const_id))
|
117
|
+
args3 = s(:arglist, value_tree)
|
118
|
+
|
119
|
+
firstcall = marked(s(:call, s(:lit, @hook_handler), :hooked_cdecl, args1 ))
|
120
|
+
secondcall = marked(s(:call, firstcall, :set_id, args2))
|
121
|
+
thirdcall = marked(s(:call, secondcall, :set_value, args3))
|
122
|
+
|
123
|
+
emul thirdcall
|
124
|
+
end
|
125
|
+
|
126
|
+
def ruby_emul_gasgn(tree)
|
127
|
+
args1 = s(:arglist, s(:lit, tree[1]))
|
128
|
+
args2 = s(:arglist, tree[2] )
|
129
|
+
|
130
|
+
firstcall = marked(s(:call, s(:lit, @hook_handler), :hooked_gasgn, args1))
|
131
|
+
secondcall = marked(s(:call, firstcall, :set_value, args2))
|
132
|
+
emul secondcall
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def marked(ast)
|
138
|
+
def ast.is_marked?
|
139
|
+
true
|
140
|
+
end
|
141
|
+
ast
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the evalhook project, http://github.com/tario/evalhook
|
4
|
+
|
5
|
+
Copyright (c) 2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
|
6
|
+
|
7
|
+
evalhook is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the gnu general public license as published by
|
9
|
+
the free software foundation, either version 3 of the license, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
evalhook is distributed in the hope that it will be useful,
|
13
|
+
but without any warranty; without even the implied warranty of
|
14
|
+
merchantability or fitness for a particular purpose. see the
|
15
|
+
gnu general public license for more details.
|
16
|
+
|
17
|
+
you should have received a copy of the gnu general public license
|
18
|
+
along with evalhook. if not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
=end
|
21
|
+
require "ruby_parser"
|
22
|
+
|
23
|
+
module EvalHook
|
24
|
+
def self.validate_syntax(code)
|
25
|
+
begin
|
26
|
+
RubyParser.new.parse(code)
|
27
|
+
rescue
|
28
|
+
raise SyntaxError
|
29
|
+
end
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
class HookHandler
|
34
|
+
end
|
35
|
+
end
|
data/lib/evalhook.rb
CHANGED
@@ -18,19 +18,25 @@ you should have received a copy of the gnu general public license
|
|
18
18
|
along with evalhook. if not, see <http://www.gnu.org/licenses/>.
|
19
19
|
|
20
20
|
=end
|
21
|
-
require "
|
22
|
-
require "evalhook_base"
|
21
|
+
require "partialruby"
|
23
22
|
require "evalhook/redirect_helper"
|
24
23
|
require "evalhook/multi_hook_handler"
|
24
|
+
require "evalhook/hook_handler"
|
25
|
+
require "evalhook/hook_context"
|
26
|
+
begin
|
25
27
|
require "evalmimic"
|
28
|
+
$evalmimic_defined = true
|
29
|
+
rescue LoadError
|
30
|
+
$evalmimic_defined = false
|
31
|
+
end
|
26
32
|
|
27
33
|
|
28
34
|
class Object
|
29
|
-
def local_hooked_method(mname)
|
30
|
-
EvalHook::HookedMethod.new(self,mname,true)
|
35
|
+
def local_hooked_method(mname,_binding)
|
36
|
+
EvalHook::HookedMethod.new(self,mname,true,_binding)
|
31
37
|
end
|
32
|
-
def hooked_method(mname)
|
33
|
-
EvalHook::HookedMethod.new(self,mname,false)
|
38
|
+
def hooked_method(mname,_binding)
|
39
|
+
EvalHook::HookedMethod.new(self,mname,false,_binding)
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
@@ -39,10 +45,11 @@ module EvalHook
|
|
39
45
|
# used internally
|
40
46
|
class HookedMethod
|
41
47
|
|
42
|
-
def initialize(recv, m,localcall)
|
48
|
+
def initialize(recv, m,localcall,_binding)
|
43
49
|
@recv = recv
|
44
50
|
@m = m
|
45
51
|
@localcall = localcall
|
52
|
+
@_binding = _binding
|
46
53
|
end
|
47
54
|
|
48
55
|
# used internally
|
@@ -62,8 +69,15 @@ module EvalHook
|
|
62
69
|
method_handler = @method_handler
|
63
70
|
ret = nil
|
64
71
|
|
65
|
-
klass = @klass || @recv.method(@m).owner
|
66
72
|
method_name = @m
|
73
|
+
if args.length == 0
|
74
|
+
local_vars = @_binding.eval("local_variables").map(&:to_s)
|
75
|
+
if local_vars.include? method_name.to_s
|
76
|
+
return @_binding.eval(method_name.to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
klass = @klass || @recv.method(@m).owner
|
67
81
|
recv = @recv
|
68
82
|
|
69
83
|
if method_handler
|
@@ -215,35 +229,35 @@ module EvalHook
|
|
215
229
|
runstr = handle_xstr(str) || str
|
216
230
|
end
|
217
231
|
|
218
|
-
|
232
|
+
def evalhook_i(code, b_ = nil, name = "(eval)", line = 1)
|
233
|
+
|
234
|
+
EvalHook.validate_syntax code
|
235
|
+
|
236
|
+
tree = RubyParser.new.parse code
|
237
|
+
|
238
|
+
context = EvalHook::HookContext.new(self)
|
239
|
+
emulationcode = context.emul tree
|
240
|
+
|
241
|
+
eval emulationcode, b_, name, line
|
219
242
|
|
220
|
-
# used internally
|
221
|
-
def internal_eval(b_, original_args)
|
222
|
-
raise ArgumentError if original_args.size == 0
|
223
|
-
evalhook_i(original_args[0], original_args[1] || b_, original_args[2] || "(eval)", original_args[3] || 0)
|
224
243
|
end
|
225
244
|
|
226
|
-
|
245
|
+
if ($evalmimic_defined)
|
227
246
|
|
228
|
-
|
247
|
+
define_eval_method :evalhook
|
229
248
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
#{code}
|
236
|
-
end
|
237
|
-
EvalHook::FakeEvalHook
|
238
|
-
else
|
239
|
-
EvalHook
|
240
|
-
end ).hook_block(ObjectSpace._id2ref(#{object_id}))
|
241
|
-
end
|
242
|
-
retvalue
|
243
|
-
"
|
244
|
-
eval(code, b_, name, line)
|
249
|
+
# used internally
|
250
|
+
def internal_eval(b_, original_args)
|
251
|
+
raise ArgumentError if original_args.size == 0
|
252
|
+
evalhook_i(original_args[0], original_args[1] || b_, original_args[2] || "(eval)", original_args[3] || 0)
|
253
|
+
end
|
245
254
|
|
255
|
+
else
|
256
|
+
def evalhook(*args)
|
257
|
+
evalhook_i(*args)
|
258
|
+
end
|
246
259
|
end
|
260
|
+
|
247
261
|
end
|
248
262
|
|
249
263
|
module ModuleMethods
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
describe EvalHook::HookHandler, "hook handler" do
|
5
|
+
|
6
|
+
it "should accept specified source name" do
|
7
|
+
hh = EvalHook::HookHandler.new
|
8
|
+
|
9
|
+
begin
|
10
|
+
hh.evalhook("raise '0'",binding, "sourcename", 1)
|
11
|
+
rescue Exception => e
|
12
|
+
e.backtrace.join.include? ("sourcename").should be == true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
class NilClass
|
5
|
+
def strip
|
6
|
+
""
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe EvalHook::HookHandler, "hook handler defaults" do
|
11
|
+
it "should throw exception when call evalhook with no parameters" do
|
12
|
+
hook_handler = EvalHook::HookHandler.new
|
13
|
+
|
14
|
+
lambda {
|
15
|
+
hook_handler.evalhook
|
16
|
+
}.should raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
exprlist = ["1+1", "[1,2,3].map{|x| x*2}"]
|
20
|
+
|
21
|
+
exprlist.each do |expr|
|
22
|
+
it "should eval expresion '#{expr}' same as eval" do
|
23
|
+
hook_handler = EvalHook::HookHandler.new
|
24
|
+
|
25
|
+
expected = eval(expr)
|
26
|
+
hook_handler.evalhook(expr).should be == expected
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should allow reference to global variables" do
|
32
|
+
hook_handler = EvalHook::HookHandler.new
|
33
|
+
|
34
|
+
$global_variable_test = 5
|
35
|
+
hook_handler.evalhook("$global_variable_test").should be == $global_variable_test
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should allow reference to constants" do
|
39
|
+
hook_handler = EvalHook::HookHandler.new
|
40
|
+
|
41
|
+
CONSTANTTEST = 5
|
42
|
+
hook_handler.evalhook("CONSTANTTEST").should be == CONSTANTTEST
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow reference to local variables" do
|
46
|
+
hook_handler = EvalHook::HookHandler.new
|
47
|
+
|
48
|
+
a = 5
|
49
|
+
hook_handler.evalhook("a", binding).should be == a
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class N
|
54
|
+
def foo(hook_handler)
|
55
|
+
@a = 5
|
56
|
+
hook_handler.evalhook("@a", binding)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
it "should allow reference to instance variables" do
|
62
|
+
hook_handler = EvalHook::HookHandler.new
|
63
|
+
N.new.foo(hook_handler).should be == 5
|
64
|
+
end
|
65
|
+
|
66
|
+
class X
|
67
|
+
def foo
|
68
|
+
3
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should allow method calls" do
|
73
|
+
hook_handler = EvalHook::HookHandler.new
|
74
|
+
hook_handler.evalhook("X.new.foo").should be X.new.foo
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should capture method calls" do
|
78
|
+
hook_handler = EvalHook::HookHandler.new
|
79
|
+
|
80
|
+
hook_handler.should_receive(:handle_method).with(X.class,X,:new)
|
81
|
+
hook_handler.should_receive(:handle_method).with(X,anything(),:foo)
|
82
|
+
|
83
|
+
hook_handler.evalhook("X.new.foo")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should capture constant assignment" do
|
87
|
+
hook_handler = EvalHook::HookHandler.new
|
88
|
+
|
89
|
+
hook_handler.should_receive(:handle_cdecl).with(Object,:TEST_CONSTANT,4)
|
90
|
+
hook_handler.evalhook("TEST_CONSTANT = 4")
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should capture global assignment" do
|
95
|
+
hook_handler = EvalHook::HookHandler.new
|
96
|
+
|
97
|
+
hook_handler.should_receive(:handle_gasgn).with(:$test_global_variable,4)
|
98
|
+
hook_handler.evalhook("$test_global_variable = 4")
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should capture system exec with backsticks" do
|
103
|
+
hook_handler = EvalHook::HookHandler.new
|
104
|
+
|
105
|
+
hook_handler.should_receive(:handle_xstr).with("echo test")
|
106
|
+
hook_handler.evalhook("`echo test`")
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should capture system exec with backsticks and dynamic strings" do
|
111
|
+
hook_handler = EvalHook::HookHandler.new
|
112
|
+
|
113
|
+
hook_handler.should_receive(:handle_xstr).with("echo test")
|
114
|
+
hook_handler.evalhook("`echo \#{}test`")
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should capture system exec with %x" do
|
119
|
+
hook_handler = EvalHook::HookHandler.new
|
120
|
+
|
121
|
+
hook_handler.should_receive(:handle_xstr).with("echo test")
|
122
|
+
hook_handler.evalhook("%x[echo test]")
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
module B
|
127
|
+
|
128
|
+
end
|
129
|
+
module A
|
130
|
+
module B
|
131
|
+
class C
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should allow define base_namespace" do
|
139
|
+
hook_handler = EvalHook::HookHandler.new
|
140
|
+
|
141
|
+
hook_handler.base_namespace = :A
|
142
|
+
hook_handler.evalhook("::B").should be == A::B
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should allow define base_namespace (const)" do
|
146
|
+
hook_handler = EvalHook::HookHandler.new
|
147
|
+
|
148
|
+
hook_handler.base_namespace = A
|
149
|
+
hook_handler.evalhook("::B").should be == A::B
|
150
|
+
end
|
151
|
+
|
152
|
+
class C1
|
153
|
+
def foo
|
154
|
+
"C1#foo"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
module A1
|
158
|
+
class C1
|
159
|
+
def foo
|
160
|
+
"A1::C1#foo"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should allow define base_namespace (class)" do
|
166
|
+
hook_handler = EvalHook::HookHandler.new
|
167
|
+
|
168
|
+
hook_handler.base_namespace = A1
|
169
|
+
hook_handler.evalhook("class ::C1
|
170
|
+
def foo
|
171
|
+
'A1::C1#foo at evalhook'
|
172
|
+
end
|
173
|
+
end")
|
174
|
+
|
175
|
+
C1.new.foo.should be == "C1#foo" # C1#foo class remains unchanged
|
176
|
+
A1::C1.new.foo.should be == "A1::C1#foo at evalhook" # A1::C1#foo changes
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
class C2
|
181
|
+
def foo
|
182
|
+
"C2#foo"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should default base_namespace to Object" do
|
187
|
+
hook_handler = EvalHook::HookHandler.new
|
188
|
+
|
189
|
+
hook_handler.evalhook("
|
190
|
+
|
191
|
+
module Z
|
192
|
+
class ::C2
|
193
|
+
def foo
|
194
|
+
'::C2#foo at evalhook'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
")
|
200
|
+
|
201
|
+
C2.new.foo.should be == "::C2#foo at evalhook"
|
202
|
+
end
|
203
|
+
|
204
|
+
module A1
|
205
|
+
module A2
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
it "should allow define base_namespace (3 levels)" do
|
212
|
+
hook_handler = EvalHook::HookHandler.new
|
213
|
+
|
214
|
+
hook_handler.base_namespace = A1::A2
|
215
|
+
hook_handler.evalhook("class ::C1
|
216
|
+
def foo
|
217
|
+
'A1::A2::C1#foo at evalhook'
|
218
|
+
end
|
219
|
+
end")
|
220
|
+
|
221
|
+
C1.new.foo.should be == "C1#foo" # C1#foo class remains unchanged
|
222
|
+
A1::A2::C1.new.foo.should be == "A1::A2::C1#foo at evalhook" # A1::C1#foo changes
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should use current binding when not specified" do
|
226
|
+
a = 9
|
227
|
+
EvalHook::HookHandler.new.evalhook("a").should be == 9
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
describe EvalHook::HookHandler, "hook handler hooks" do
|
5
|
+
|
6
|
+
class X2
|
7
|
+
def foo
|
8
|
+
9
|
9
|
+
end
|
10
|
+
|
11
|
+
def bar
|
12
|
+
4
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should intercept and change method calls" do
|
17
|
+
hh = EvalHook::HookHandler.new
|
18
|
+
def hh.handle_method(klass, recv, method_name)
|
19
|
+
redirect_method(klass, recv, :bar)
|
20
|
+
end
|
21
|
+
|
22
|
+
x = X2.new
|
23
|
+
hh.evalhook("x.foo", binding).should be == 4
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should intercept and change global variable assignments" do
|
27
|
+
hh = EvalHook::HookHandler.new
|
28
|
+
def hh.handle_gasgn(global_id, value)
|
29
|
+
redirect_gasgn(global_id, 7)
|
30
|
+
end
|
31
|
+
|
32
|
+
hh.evalhook("$test_global_variable = 9")
|
33
|
+
$test_global_variable.should be == 7
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should intercept and change constant assignments" do
|
37
|
+
hh = EvalHook::HookHandler.new
|
38
|
+
def hh.handle_cdecl(context, name, value)
|
39
|
+
redirect_cdecl(context,name,7)
|
40
|
+
end
|
41
|
+
|
42
|
+
hh.evalhook("TEST_CONSTANT = 9")
|
43
|
+
TEST_CONSTANT.should be == 7
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
describe EvalHook::MultiHookHandler, "multiple hook handler redirect" do
|
5
|
+
|
6
|
+
class X
|
7
|
+
def self.foo
|
8
|
+
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
handles = [
|
13
|
+
[:handle_method, "X.foo"],
|
14
|
+
[:handle_cdecl, "TEST_CONSTANT = 4"],
|
15
|
+
[:handle_gasgn, "$test_global_variable = 4"] ]
|
16
|
+
|
17
|
+
handles.each do |handle|
|
18
|
+
it "should recall #{handle[0]} in correct order" do
|
19
|
+
hook_handler = EvalHook::MultiHookHandler.new
|
20
|
+
|
21
|
+
nested_handler_1 = EvalHook::HookHandler.new
|
22
|
+
nested_handler_2 = EvalHook::HookHandler.new
|
23
|
+
hook_handler.add nested_handler_1
|
24
|
+
hook_handler.add nested_handler_2
|
25
|
+
|
26
|
+
nested_handler_1.should_receive(handle[0])
|
27
|
+
nested_handler_2.should_receive(handle[0])
|
28
|
+
|
29
|
+
hook_handler.evalhook(handle[1])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class X_
|
34
|
+
def foo
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def bar
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it "should handle redirects on recall of handle_method" do
|
45
|
+
hook_handler = EvalHook::MultiHookHandler.new
|
46
|
+
|
47
|
+
nested_handler_1 = EvalHook::HookHandler.new
|
48
|
+
nested_handler_2 = EvalHook::HookHandler.new
|
49
|
+
hook_handler.add nested_handler_1
|
50
|
+
hook_handler.add nested_handler_2
|
51
|
+
|
52
|
+
x = X_.new
|
53
|
+
|
54
|
+
def nested_handler_1.handle_method(klass, recv, m )
|
55
|
+
RedirectHelper::Redirect.new(klass, recv, :bar)
|
56
|
+
end
|
57
|
+
|
58
|
+
nested_handler_2.should_receive(:handle_method).
|
59
|
+
with(X_, x, :bar).
|
60
|
+
and_return(nil)
|
61
|
+
|
62
|
+
hook_handler.evalhook("x.foo", binding)
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|