rolling your own AOP

V

Volkmann, Mark

This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.

------_=_NextPart_001_01C35C4C.BF86E246
Content-Type: text/plain; charset="iso-8859-1"

I've been experimenting to see how easy it is to do AOP kinds of things with
Ruby. I know I could use the AspectR package and probably should do that,
but I wanted to see what was involved. Here's an example of how you could
specify code that you want to run at the beginning of each method in some
class. It just prints "calling foo" where foo is the name of the method
being executed. Feel free to suggest ways I could improve this code.

--

class Demo
def methodA
puts 'methodA is running'
end

def methodB
puts 'methodB is running'
end
end

module AOP
def AOP.addBeforeAdvice(klass, &advice)
# Add the advice code block as a class variable
# of the class being instrumented.
klass.class_eval "@@beforeAdvice = advice"

# For each instance method in the class, excluding inherited methods ...
for method_name in klass.instance_methods(false) do
new_name = "__#{method_name}"
klass.class_eval <<-HERE
# Create an alias for the method.
alias_method :#{new_name}, :#{method_name}

# Redefine the method.
def #{method_name}
# Execute the advice code.
@@beforeAdvice.call(self)
# Execute the original method.
#{new_name}
end
HERE
end
end
end

def currentMethodName
# Get 2nd to last entry in call stack,
# take the last word and remove the quotes around it.
caller[-2].split.last[1..-2]
end

if $0 == __FILE__
AOP.addBeforeAdvice(Demo) {
puts "calling #{currentMethodName}"
}

o = Demo.new
o.methodA
o.methodB
end

---

The output is
calling methodA
methodA is running
calling methodB
methodB is running


-------------------------------------------------------------------------------------------------------------------
A.G. Edwards & Sons' outgoing and incoming e-mails are electronically
archived and subject to review and/or disclosure to someone other
than the recipient.

-------------------------------------------------------------------------------------------------------------------

------_=_NextPart_001_01C35C4C.BF86E246
Content-Type: text/html; charset="iso-8859-1"

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>RE: adding methods to an existing class</TITLE>

<META content="MSHTML 5.50.4913.1100" name=GENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=#ffffff>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>I've been experimenting to see how easy it is to do AOP kinds of things
with Ruby.&nbsp; I know I could use the AspectR package and probably should do
that, but I wanted to see what was involved.&nbsp; Here's an example of how you
could specify code that you want to run at the beginning of each method in some
class.&nbsp; It just prints "calling foo" where foo is the name of the method
being executed.&nbsp; Feel free to suggest ways I could improve this
code.</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2></FONT></SPAN>&nbsp;</DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>--</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2></FONT></SPAN>&nbsp;</DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>class Demo<BR>&nbsp; def methodA<BR>&nbsp;&nbsp;&nbsp; puts 'methodA is
running'<BR>&nbsp; end</FONT></SPAN></DIV>
<DIV>&nbsp;</DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>&nbsp; def methodB<BR>&nbsp;&nbsp;&nbsp; puts 'methodB is
running'<BR>&nbsp; end<BR>end</FONT></SPAN></DIV>
<DIV>&nbsp;</DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>module AOP<BR>&nbsp; def AOP.addBeforeAdvice(klass,
&amp;advice)<BR>&nbsp;&nbsp;&nbsp; # Add the advice code block as a class
variable</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>&nbsp;&nbsp;&nbsp; # of the class being
instrumented.<BR>&nbsp;&nbsp;&nbsp; klass.class_eval "@@beforeAdvice =
advice"<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; # For each instance method
in the class, excluding inherited methods ...<BR>&nbsp;&nbsp;&nbsp; for
method_name in klass.instance_methods(false)
do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new_name =
"__#{method_name}"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; klass.class_eval
&lt;&lt;-HERE<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Create an alias
for the method.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alias_method
:#{new_name}, :#{method_name}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Redefine the
method.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def
#{method_name}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #
Execute the advice
code.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
@@beforeAdvice.call(self)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# Execute the original
method.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
#{new_name}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
end<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HERE<BR>&nbsp;&nbsp;&nbsp; end<BR>&nbsp;
end<BR>end<BR>&nbsp; <BR>def currentMethodName<BR>&nbsp; # Get 2nd to last entry
in call stack,<BR>&nbsp; # take the last word and remove the quotes around
it.<BR>&nbsp; caller[-2].split.last[1..-2]<BR>end<BR>&nbsp; <BR>if $0 ==
__FILE__<BR>&nbsp; AOP.addBeforeAdvice(Demo) {<BR>&nbsp;&nbsp;&nbsp; puts
"calling #{currentMethodName}"<BR>&nbsp; }</FONT></SPAN></DIV>
<DIV>&nbsp;</DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>&nbsp; o = Demo.new<BR>&nbsp; o.methodA<BR>&nbsp;
o.methodB<BR>end</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2></FONT></SPAN>&nbsp;</DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>---</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2></FONT></SPAN>&nbsp;</DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>The output is</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>calling methodA</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>methodA is running</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>calling methodB</FONT></SPAN></DIV>
<DIV><SPAN class=851535318-06082003><FONT face="Courier New" color=#0000ff
size=2>methodB is running</FONT></SPAN></DIV><CODE><FONT SIZE=3><BR>
<BR>
-------------------------------------------------------------------------------------------------------------------<BR>
A.G. Edwards & Sons' outgoing and incoming e-mails are electronically<BR>
archived and subject to review and/or disclosure to someone other <BR>
than the recipient.<BR>
<BR>
-------------------------------------------------------------------------------------------------------------------<BR>
</FONT></CODE></BODY></HTML>

------_=_NextPart_001_01C35C4C.BF86E246--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top