rolling your own AOP

Discussion in 'Ruby' started by Volkmann, Mark, Aug 6, 2003.

  1. 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--
     
    Volkmann, Mark, Aug 6, 2003
    #1
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Stefan Siegl
    Replies:
    1
    Views:
    784
  2. KPB
    Replies:
    0
    Views:
    983
  3. Replies:
    12
    Views:
    732
  4. c676228
    Replies:
    3
    Views:
    185
    c676228
    Sep 14, 2006
  5. David Filmer
    Replies:
    17
    Views:
    279
    J. Romano
    Aug 18, 2004
Loading...

Share This Page