aspectr 0.3.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/lib/aspectr.rb +309 -0
- data/tests/atest01_aspectr.rb +249 -0
- data/tests/runtests.rb +6 -0
- metadata +47 -0
data/lib/aspectr.rb
ADDED
@@ -0,0 +1,309 @@
|
|
1
|
+
# AspectR - simple Aspect-Oriented Programming (AOP) in Ruby.
|
2
|
+
# Version 0.3.6, 2006-03-11. NOTE! API has changed somewhat from 0.2 so beware!
|
3
|
+
# Last modification: Benjamin Alterauge Email: benjamin_alterauge@web.de
|
4
|
+
#
|
5
|
+
# Copyright (c) 2001 Avi Bryant (avi@beta4.com) and
|
6
|
+
# Robert Feldt (feldt@ce.chalmers.se).
|
7
|
+
#
|
8
|
+
# This library is free software; you can redistribute it and/or
|
9
|
+
# modify it under the terms of the GNU Library General Public
|
10
|
+
# License as published by the Free Software Foundation; either
|
11
|
+
# version 2 of the License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This library is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
16
|
+
# Library General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Library General Public
|
19
|
+
# License along with this library; if not, write to the Free Software
|
20
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
21
|
+
#
|
22
|
+
module AspectR
|
23
|
+
class AspectRException < Exception; end
|
24
|
+
|
25
|
+
class Aspect
|
26
|
+
PRE = :PRE
|
27
|
+
POST = :POST
|
28
|
+
|
29
|
+
def initialize(never_wrap = "^$ ")
|
30
|
+
@never_wrap = /^__|^send$|^object_id$|^class$|#{never_wrap}/
|
31
|
+
end
|
32
|
+
|
33
|
+
def wrap(target, pre, post, *args)
|
34
|
+
get_methods(target, args).each do |method_to_wrap|
|
35
|
+
add_advice(target, PRE, method_to_wrap, pre)
|
36
|
+
add_advice(target, POST, method_to_wrap, post)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def unwrap(target, pre, post, *args)
|
41
|
+
get_methods(target, args).each do |method_to_unwrap|
|
42
|
+
remove_advice(target, PRE, method_to_unwrap, pre)
|
43
|
+
remove_advice(target, POST, method_to_unwrap , post)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sticky and faster wrap (can't be unwrapped).
|
48
|
+
def wrap_with_code(target, preCode, postCode, *args)
|
49
|
+
prepare(target)
|
50
|
+
get_methods(target, args).each do |method_to_wrap|
|
51
|
+
target.__aop_wrap_with_code(method_to_wrap, preCode, postCode)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_advice(target, joinpoint, method, advice)
|
56
|
+
prepare(target)
|
57
|
+
if advice
|
58
|
+
target.__aop_install_dispatcher(method)
|
59
|
+
target.__aop_add_advice(joinpoint, method, self, advice)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def remove_advice(target, joinpoint, method, advice)
|
64
|
+
target.__aop_remove_advice(joinpoint, method, self, advice) if advice
|
65
|
+
end
|
66
|
+
|
67
|
+
@@__aop_dispatch = true
|
68
|
+
|
69
|
+
def Aspect.dispatch?
|
70
|
+
@@__aop_dispatch
|
71
|
+
end
|
72
|
+
|
73
|
+
def disable_advice_dispatching
|
74
|
+
begin
|
75
|
+
@@__aop_dispatch = false
|
76
|
+
yield
|
77
|
+
ensure
|
78
|
+
@@__aop_dispatch = true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_methods(target, args)
|
83
|
+
if args.first.is_a? Regexp
|
84
|
+
if target.kind_of?(Class)
|
85
|
+
methods = target.instance_methods(true)
|
86
|
+
else
|
87
|
+
methods = target.methods
|
88
|
+
end
|
89
|
+
methods = methods.grep(args.first).collect{|e| e.intern}
|
90
|
+
else
|
91
|
+
methods = args
|
92
|
+
end
|
93
|
+
methods.select {|method| wrappable?(method)}
|
94
|
+
end
|
95
|
+
|
96
|
+
def wrappable?(method)
|
97
|
+
method.to_s !~ @never_wrap
|
98
|
+
end
|
99
|
+
|
100
|
+
def prepare(target)
|
101
|
+
unless target.respond_to?("__aop_init")
|
102
|
+
target.extend AspectSupport
|
103
|
+
target.__aop_init
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module AspectSupport
|
108
|
+
|
109
|
+
def __aop_init
|
110
|
+
if self.is_a? Class
|
111
|
+
extend ClassSupport
|
112
|
+
else
|
113
|
+
extend InstanceSupport
|
114
|
+
end
|
115
|
+
@__aop_advice_methods = {}
|
116
|
+
end
|
117
|
+
|
118
|
+
def __aop_advice_list(joinpoint, method)
|
119
|
+
method = method.to_s
|
120
|
+
unless (method_hash = @__aop_advice_methods[joinpoint])
|
121
|
+
method_hash = @__aop_advice_methods[joinpoint] = {}
|
122
|
+
end
|
123
|
+
unless (advice_list = method_hash[method])
|
124
|
+
advice_list = method_hash[method] = []
|
125
|
+
end
|
126
|
+
advice_list
|
127
|
+
end
|
128
|
+
|
129
|
+
def __aop_add_advice(joinpoint, method, aspect, advice)
|
130
|
+
__aop_advice_list(joinpoint, method) << [aspect, advice]
|
131
|
+
end
|
132
|
+
|
133
|
+
def __aop_remove_advice(joinpoint, method, aspect, advice)
|
134
|
+
__aop_advice_list(joinpoint, method).delete_if do |asp, adv|
|
135
|
+
asp == aspect && adv == advice
|
136
|
+
end
|
137
|
+
# Reinstall original method if there are no advices left for this meth!
|
138
|
+
# - except that then we could have problems with singleton instances
|
139
|
+
# of this class? see InstanceSupport#aop_alias... /AB
|
140
|
+
end
|
141
|
+
|
142
|
+
def __aop_call_advice(joinpoint, method, *args)
|
143
|
+
__aop_advice_list(joinpoint, method).each do |aspect, advice|
|
144
|
+
begin
|
145
|
+
aspect.send(advice, method, *args)
|
146
|
+
rescue Exception
|
147
|
+
a = $!
|
148
|
+
raise AspectRException, "#{a.class} '#{a}' in advice #{advice}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def __aop_generate_args(method)
|
154
|
+
arity = __aop_class.instance_method(method).arity
|
155
|
+
if arity < 0
|
156
|
+
args = (0...(-1-arity)).to_a.collect{|i| "a#{i}"}.join(",")
|
157
|
+
args += "," if arity < -1
|
158
|
+
args + "*args,&block"
|
159
|
+
elsif arity != 0
|
160
|
+
((0...arity).to_a.collect{|i| "a#{i}"} + ["&block"]).join(",")
|
161
|
+
else
|
162
|
+
"&block" # could be a yield in there...
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def __aop_generate_syntax(method)
|
167
|
+
args = __aop_generate_args(method)
|
168
|
+
mangled_method = __aop_mangle(method)
|
169
|
+
call = "send(\"#{mangled_method}\".to_sym, #{args})"
|
170
|
+
return args, call, mangled_method
|
171
|
+
end
|
172
|
+
|
173
|
+
def __aop_advice_call_syntax(joinpoint, method, args)
|
174
|
+
"#{__aop_target}.__aop_call_advice(:#{joinpoint}, '#{method}', self, exit_status#{args.length>0 ? ',' + args : ''})"
|
175
|
+
end
|
176
|
+
|
177
|
+
def __aop_install_dispatcher(method)
|
178
|
+
args, call, mangled_method = __aop_generate_syntax(method)
|
179
|
+
return if __aop_private_methods.include? mangled_method
|
180
|
+
new_method = """
|
181
|
+
def #{method}(#{args})
|
182
|
+
return (#{call}) unless Aspect.dispatch?
|
183
|
+
begin
|
184
|
+
exit_status = nil
|
185
|
+
#{__aop_advice_call_syntax(PRE, method, args)}
|
186
|
+
exit_status = #{call}
|
187
|
+
return exit_status
|
188
|
+
rescue Exception
|
189
|
+
exit_status = true
|
190
|
+
raise
|
191
|
+
ensure
|
192
|
+
#{__aop_advice_call_syntax(POST, method, args)}
|
193
|
+
end
|
194
|
+
end
|
195
|
+
"""
|
196
|
+
__aop_alias(mangled_method, method)
|
197
|
+
__aop_eval(new_method)
|
198
|
+
end
|
199
|
+
|
200
|
+
def __aop_wrap_with_code(method, preCode, postCode)
|
201
|
+
args, call, mangled_method = __aop_generate_syntax(method)
|
202
|
+
return if __aop_private_methods.include? mangled_method
|
203
|
+
comma = args != "" ? ", " : ""
|
204
|
+
preCode.gsub!('INSERT_ARGS', comma + args)
|
205
|
+
postCode.gsub!('INSERT_ARGS', comma + args)
|
206
|
+
new_method = """
|
207
|
+
def #{method}(#{args})
|
208
|
+
#{preCode}
|
209
|
+
begin
|
210
|
+
#{call}
|
211
|
+
ensure
|
212
|
+
#{postCode}
|
213
|
+
end
|
214
|
+
end
|
215
|
+
"""
|
216
|
+
__aop_alias(mangled_method, method)
|
217
|
+
__aop_eval(new_method)
|
218
|
+
end
|
219
|
+
|
220
|
+
module ClassSupport
|
221
|
+
def __aop_target
|
222
|
+
"self.class"
|
223
|
+
end
|
224
|
+
|
225
|
+
def __aop_class
|
226
|
+
self
|
227
|
+
end
|
228
|
+
|
229
|
+
def __aop_mangle(method)
|
230
|
+
"__aop__#{self.object_id}_#{method.object_id}"
|
231
|
+
end
|
232
|
+
|
233
|
+
def __aop_alias(new, old, private = true)
|
234
|
+
alias_method new, old
|
235
|
+
private new if private
|
236
|
+
end
|
237
|
+
|
238
|
+
def __aop_private_methods
|
239
|
+
private_instance_methods
|
240
|
+
end
|
241
|
+
|
242
|
+
def __aop_eval(text)
|
243
|
+
begin
|
244
|
+
class_eval text
|
245
|
+
rescue Exception
|
246
|
+
puts "class_eval '#{text}'"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
module InstanceSupport
|
252
|
+
def __aop_target
|
253
|
+
"self"
|
254
|
+
end
|
255
|
+
|
256
|
+
def __aop_class
|
257
|
+
self.class
|
258
|
+
end
|
259
|
+
|
260
|
+
def __aop_mangle(method)
|
261
|
+
"__aop__singleton_#{method}"
|
262
|
+
end
|
263
|
+
|
264
|
+
def __aop_alias(new, old, private = true)
|
265
|
+
# Install in class since otherwise the non-dispatcher version of the class version of the method
|
266
|
+
# gets locked away, and so if we wrap a singleton before wrapping its class,
|
267
|
+
# later wrapping the class has no effect on that singleton /AB
|
268
|
+
# of course, this depends on exactly what behavior we want for inheritance... Decide for future release...
|
269
|
+
unless self.class.respond_to?("__aop_init")
|
270
|
+
self.class.extend AspectSupport
|
271
|
+
self.class.__aop_init
|
272
|
+
end
|
273
|
+
self.class.__aop_install_dispatcher(old)
|
274
|
+
eval "class << self; alias_method '#{new}', '#{old}'; end;"
|
275
|
+
eval "class << self; private '#{new}'; end" if private
|
276
|
+
end
|
277
|
+
|
278
|
+
def __aop_private_methods
|
279
|
+
private_methods
|
280
|
+
end
|
281
|
+
|
282
|
+
def __aop_eval(text)
|
283
|
+
instance_eval text
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# NOTE! Somewhat experimental so API will likely change on this method!
|
290
|
+
def wrap_classes(aspect, pre, post, classes, *methods)
|
291
|
+
classes = all_classes(classes) if classes.kind_of?(Regexp)
|
292
|
+
classes.each {|klass| aspect.wrap(klass, pre, post, *methods)}
|
293
|
+
end
|
294
|
+
module_function :wrap_classes
|
295
|
+
|
296
|
+
# TODO: Speed this up by recursing from Object.constants instead of sifting
|
297
|
+
# through all object in the ObjectSpace (might be slow if many objects).
|
298
|
+
# Is there a still faster/better way?
|
299
|
+
# NOTE! Somewhat experimental so API will likely change on this method!
|
300
|
+
def all_classes(regexp = /^.+$/)
|
301
|
+
classes = []
|
302
|
+
ObjectSpace.each_object(Class) do |c|
|
303
|
+
classes.push c if c.inspect =~ regexp
|
304
|
+
end
|
305
|
+
classes
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
|
@@ -0,0 +1,249 @@
|
|
1
|
+
class Logger < Aspect
|
2
|
+
def initialize(io = STDOUT)
|
3
|
+
@io = io
|
4
|
+
end
|
5
|
+
|
6
|
+
def _tick; "#{Time.now.strftime('%Y-%m-%d %X')}"; end
|
7
|
+
|
8
|
+
def enter(method, object, exitstatus, *args)
|
9
|
+
@io.puts "enter: #{_tick} #{object.class}##{method}: called with #{args.inspect}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def exit(method, object, exitstatus, *args)
|
13
|
+
@io.print "exit: #{_tick} #{object.class}##{method}: exited "
|
14
|
+
if exitstatus.kind_of?(Array)
|
15
|
+
@io.puts "normally returning #{exitstatus[0].inspect}"
|
16
|
+
elsif exitstatus == true
|
17
|
+
@io.puts "with exception '#{$!}'"
|
18
|
+
else
|
19
|
+
@io.puts 'normally'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def senter(method, object, exitstatus, *args)
|
24
|
+
@io.puts "senter: #{_tick} Entering #{object.object_id}##{method}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def sexit(method, object, exitstatus, *args)
|
28
|
+
@io.puts "sexit: #{_tick} Exiting #{object.object_id}##{method}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Counter < Aspect
|
33
|
+
attr_reader :incrementer_cnt
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@counters = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def wrap(target, *methods)
|
40
|
+
super(target, :inc, nil, *methods)
|
41
|
+
end
|
42
|
+
|
43
|
+
def unwrap(target, *methods)
|
44
|
+
super(target, :inc, nil, *methods)
|
45
|
+
end
|
46
|
+
|
47
|
+
def [](object, method)
|
48
|
+
method = method.id2name if method.kind_of?(Symbol)
|
49
|
+
@counters[object][method]
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def inc(method, object, *args)
|
54
|
+
begin
|
55
|
+
@counters[object][method] += 1
|
56
|
+
rescue NameError
|
57
|
+
@counters[object] = {} unless @counters[object]
|
58
|
+
@counters[object][method] = 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
class TestHello
|
67
|
+
attr_reader :tt
|
68
|
+
|
69
|
+
def say(hello)
|
70
|
+
return hello + " world"
|
71
|
+
end
|
72
|
+
|
73
|
+
def inspect; object_id.inspect; end
|
74
|
+
|
75
|
+
def sayz(*args)
|
76
|
+
raise NotImplementedError, 'NYI!'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class HistoryStdOut
|
81
|
+
attr_reader :history
|
82
|
+
def initialize
|
83
|
+
@history = Array.new
|
84
|
+
end
|
85
|
+
def puts(str)
|
86
|
+
if @last_was_print
|
87
|
+
@history[-1] += str
|
88
|
+
else
|
89
|
+
@history.push str
|
90
|
+
end
|
91
|
+
@last_was_print = false
|
92
|
+
end
|
93
|
+
def print(str)
|
94
|
+
@history.push str
|
95
|
+
@last_was_print = true
|
96
|
+
end
|
97
|
+
def last; @history.last; end
|
98
|
+
end
|
99
|
+
|
100
|
+
class CodeWrapper < Aspect
|
101
|
+
alias old_wrap wrap
|
102
|
+
def wrap(target, *methods)
|
103
|
+
wrap_with_code(target, "p 'pre'", "p 'post'", *methods)
|
104
|
+
end
|
105
|
+
def code_wrap(target, *methods)
|
106
|
+
wrap_with_code(target,"CodeWrapper.enter","CodeWrapper.leave", *methods)
|
107
|
+
end
|
108
|
+
@@counter = 0
|
109
|
+
def CodeWrapper.enter; @@counter += 1; end
|
110
|
+
def CodeWrapper.leave; @@counter += 1; end
|
111
|
+
def CodeWrapper.counter; @@counter; end
|
112
|
+
end
|
113
|
+
|
114
|
+
class T2; def m; end; end
|
115
|
+
|
116
|
+
class TestAspectR < Test::Unit::TestCase
|
117
|
+
# Just so that we can use previously written tests. Clean up when there is
|
118
|
+
# time!
|
119
|
+
@@logger = Logger.new(@@hso = HistoryStdOut.new)
|
120
|
+
@@counter = Counter.new
|
121
|
+
@@t, @@u = TestHello.new, TestHello.new
|
122
|
+
|
123
|
+
def setup
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_01_wrap
|
127
|
+
@@counter.wrap(TestHello, :say) # Wrap counter on TestHello#say
|
128
|
+
assert_equal('hello world', @@t.say('hello'))
|
129
|
+
assert_equal('hello world', @@u.say('hello'))
|
130
|
+
assert_equal(1, @@counter[@@t, :say])
|
131
|
+
assert_equal(1, @@counter[@@u, :say])
|
132
|
+
@@t.say("my")
|
133
|
+
assert_equal(2, @@counter[@@t, :say])
|
134
|
+
assert_equal(1, @@counter[@@u, :say])
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_02_class_wrapping
|
138
|
+
@@logger.wrap(TestHello, :enter, :exit, /sa/)
|
139
|
+
@@t.say('t: hello')
|
140
|
+
assert_match(/enter.*TestHello#say: called with \[.*\]/, @@hso.history[-2] )
|
141
|
+
assert_match(/exit.*TestHello#say: exited/, @@hso.history[-1] )
|
142
|
+
@@u.say('u: hello')
|
143
|
+
assert_match(/enter.*TestHello#say: called with \[.*\]/,@@hso.history[-2])
|
144
|
+
assert_match(/exit.*TestHello#say: exited/,@@hso.history[-1])
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_03_singleton_wrapping
|
148
|
+
@@logger.wrap(@@t, :senter, :sexit, :say)
|
149
|
+
@@t.say('t: hello')
|
150
|
+
assert_match( /senter/, @@hso.history[-4])
|
151
|
+
assert_match( /enter/, @@hso.history[-3])
|
152
|
+
assert_match( /exit/, @@hso.history[-2])
|
153
|
+
assert_match(/sexit/, @@hso.history[-1])
|
154
|
+
@@u.say('u: hello')
|
155
|
+
assert_match( /enter/, @@hso.history[-2])
|
156
|
+
assert_match(/exit/, @@hso.history[-1])
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_04_unwrapping
|
160
|
+
@@logger.unwrap(TestHello, :enter, :exit, /sa/)
|
161
|
+
@@t.say('t: hello') # senter and sexit should still be there...
|
162
|
+
assert_match(/senter/, @@hso.history[-2])
|
163
|
+
assert_match(/sexit/, @@hso.history[-1])
|
164
|
+
l = @@hso.history.length
|
165
|
+
@@u.say('u: hello')
|
166
|
+
assert_equal(l, @@hso.history.length) # Nothing printed!
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_05_dynamically_changing_advice
|
170
|
+
Logger.class_eval <<-'EOC'
|
171
|
+
def senter(*args)
|
172
|
+
@io.puts "senter version 2.0"
|
173
|
+
end
|
174
|
+
EOC
|
175
|
+
@@t.say('t: hello')
|
176
|
+
assert_match(/senter version 2\.0/, @@hso.history[-2])
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_06_partial_unwrap
|
180
|
+
@@logger.unwrap(@@t, :senter, nil, :say)
|
181
|
+
l = @@hso.history.length
|
182
|
+
@@t.say('t: hello') # Should use sexit but not senter
|
183
|
+
assert_equal(l+1, @@hso.history.length)
|
184
|
+
assert_match(/sexit/, @@hso.history[-1])
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_07_exception_in_method
|
188
|
+
@@logger.wrap(@@t, nil, :exit, :sayz)
|
189
|
+
l = @@hso.history.length
|
190
|
+
begin
|
191
|
+
@@t.sayz 1
|
192
|
+
rescue Exception; end
|
193
|
+
assert_equal(l+1, @@hso.history.length)
|
194
|
+
assert_match(/exit/, @@hso.history[-1])
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_08_counter
|
198
|
+
assert_equal(7, @@counter[@@t, :say])
|
199
|
+
@@counter.unwrap(TestHello, :say)
|
200
|
+
@@t.say('t: hello')
|
201
|
+
assert_equal(7, @@counter[@@t, :say])
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_09_transparency
|
205
|
+
assert_equal(1, TestHello.instance_method(:say).arity)
|
206
|
+
assert_equal(-1, TestHello.instance_method(:sayz).arity)
|
207
|
+
|
208
|
+
@@counter.wrap(TestHello, :tt)
|
209
|
+
assert_equal(0, TestHello.instance_method(:tt).arity)
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_10_special_methods
|
213
|
+
@@logger.wrap(Array, :enter, nil, :[], :[]=, :initialize)
|
214
|
+
a = Array.new(2)
|
215
|
+
assert_match(/enter/, @@hso.history.last); l = @@hso.history.length
|
216
|
+
a[1]
|
217
|
+
assert_equal(l+1, @@hso.history.length)
|
218
|
+
assert_match(/enter/, @@hso.history.last); l = @@hso.history.length
|
219
|
+
a[1] = 10
|
220
|
+
assert_equal(l+1, @@hso.history.length)
|
221
|
+
assert_match(/enter/, @@hso.history.last); l = @@hso.history.length
|
222
|
+
@@logger.unwrap(Array, :[], :[]=, :initialize)
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_11_code_wrapping
|
226
|
+
assert_equal(0, CodeWrapper.counter)
|
227
|
+
CodeWrapper.new.code_wrap(T2, :m)
|
228
|
+
T2.new.m
|
229
|
+
assert_equal(2, CodeWrapper.counter)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
class T3; def m; end; def m2; end; end
|
235
|
+
|
236
|
+
def time(n = 100_000, &block)
|
237
|
+
start = Process.times.utime
|
238
|
+
n.times{block.call}
|
239
|
+
Process.times.utime - start
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_overhead
|
243
|
+
puts "\nOverhead"
|
244
|
+
t = T3.new
|
245
|
+
puts "Time for unwrapped: #{tuw = time(50_000) {t.m}}"
|
246
|
+
CodeWrapper.new.code_wrap(T3, :m)
|
247
|
+
puts "Time for code-wrapped: #{tw = time(50_000) {t.m}}"
|
248
|
+
puts "A slowdown of #{tw/tuw}"
|
249
|
+
end
|
data/tests/runtests.rb
ADDED
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: aspectr
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.3.7
|
7
|
+
date: 2007-03-19 00:00:00 +01:00
|
8
|
+
summary: Aspect-oriented programming concepts to Ruby.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: bunny@kultkiste.de
|
12
|
+
homepage: http://aspectr.rubyforge.org/
|
13
|
+
rubyforge_project: aspectr
|
14
|
+
description: Essentially it allows you to wrap code around existing methods in your classes.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- lib/aspectr.rb
|
33
|
+
test_files:
|
34
|
+
- tests/atest01_aspectr.rb
|
35
|
+
- tests/runtests.rb
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
extra_rdoc_files: []
|
39
|
+
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
requirements: []
|
45
|
+
|
46
|
+
dependencies: []
|
47
|
+
|