CSC/ECE 517 Fall 2010/ch3 3d mr: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 75: Line 75:
  profiler.wrap_with_code(SomeClass, '@begin = Time.now', 'timeElapsed = Time.now - @begin; puts "took #{timeElapsed} secs"' , /some/)
  profiler.wrap_with_code(SomeClass, '@begin = Time.now', 'timeElapsed = Time.now - @begin; puts "took #{timeElapsed} secs"' , /some/)


To wrap methods from multiple target classes, the AspectR library defines a  <code>wrap_classes</code> method. This is an experimental method and the API is likely to change <ref>[[Edsger, Feldt]], AspectR Ruby documentation, AspectR module</ref>:
To wrap methods from multiple target classes, the AspectR library defines a  <code>wrap_classes</code> method. This is an experimental method and the API is likely to change <ref>AspectR Ruby documentation (RDoc), AspectR module</ref>:


  wrap_classes(profiler, :method_start, :method_end, /Some/, /some/)
  wrap_classes(profiler, :method_start, :method_end, /Some/, /some/)

Revision as of 13:21, 6 October 2010

Aspect-oriented programming and AspectR

AspectR is a programming library that enables Aspect-oriented programming for Ruby programs by providing ways to wrap code around existing methods in a program. It is simpler but similar in functionality to the AspectJ library for Java, but less well known. AspectR is distributed under the GNU General Public License.

Aspect-Oriented Programming

Aspect-Oriented Programming is an approach to modularizing concerns that cut across the core concerns or business logic, of a program.

Motivation

The model code in a typical program will often contain not only the business logic but also secondary concerns such as transactions, security, logging, etc. This clutters the code in the model, making it hard to distinguish the business logic from the secondary code, complicating maintenance and reuse. Furthermore, these secondary concerns cut across multiple core concerns in the program, which violates the separation of concerns. As a result, changing to a different transaction API, for example, results in code modifications throughout the program.

Terminology

  • Aspects - Classes that implement functionality needed in many parts of a program, but are not part of the business logic or core concern of the program.
  • Advice - Borrowed from AspectJ terminology, refers to Aspect methods or code to be applied around existing business logic.
  • Join Point - Places in the core business logic that advice should be applied or wrapped around.

The AspectR API

AspectR provides a simple mechanism for wrapping methods in a program. One begins by creating an "aspect" class that inherits from the AspectR Aspect class. For example, we define a new aspect class to implement a code profiler:

require aspectr.rb
include AspectR

class Profiler < Aspect
end

We then define the wrapper methods which will be called at the join points in the program. These methods are called Advice methods.

def method_start(method, object, exitstatus, *args)
  @begin = Time.now
end

def method_end(method, object, exitstatus, *args)
  timeElapsed = Time.now - @begin
  puts "#{object.class}.#{method} took #{timeElapsed} secs"
end

AspectR currently supports only two method join points, PRE and POST.

Given the simple example,

class SomeClass
  def some_method
    puts "hello"
    sleep 5
  end
end

One can add advice to be invoked before or after a single target method using the add_advice method, for example:

profiler = Profiler.new

profiler.add_advice(SomeClass, :PRE, :some_method, :method_start)
profiler.add_advice(SomeClass, :POST, :some_method, :method_end)

This advice can be removed using remove_advice:

profiler.remove_advice(SomeClass, :PRE, :some_method, :method_start)
profiler.remove_advice(SomeClass, :POST, :some_method, :method_end)

We can wrap both the before and after join points, and specify multiple target methods, for example the following code will wrap all the methods from the class SomeClass which match the regular expression "/some/" with our advice methods for the before and after join points:

profiler.wrap(SomeClass, :method_start, :method_end, /some/)

Similarly, this can be unwrapped:

profiler.unwrap(SomeClass, :method_start, :method_end, /some/)

Alternatively, you can have code directly injected into the beginning and ending of the target methods . This has performance advantages but cannot be unwrapped:

profiler.wrap_with_code(SomeClass, '@begin = Time.now', 'timeElapsed = Time.now - @begin; puts "took #{timeElapsed} secs"' , /some/)

To wrap methods from multiple target classes, the AspectR library defines a wrap_classes method. This is an experimental method and the API is likely to change <ref>AspectR Ruby documentation (RDoc), AspectR module</ref>:

wrap_classes(profiler, :method_start, :method_end, /Some/, /some/)

One can specify that one or more methods which should never be wrapped by passing in the beginning of the method name to the constructor. There is also a wrappable? method to test if a method can be wrapped. For example this code will result in the wrap being ignored and return true:

profiler = Profiler.new("some")

profiler.wrap(SomeClass, :method_start, :method_end, /some/)

profiler.wrappable?(:some_method)

Code can be executed with dispatching by the API disabled using the disable_advice_dispatching instance method on the aspect class. Although temporary, the effect is global to the API. For example the following code will NOT result in the advice methods being called:

 profiler.wrap(SomeClass, :method_start, :method_end, /some/)

 profiler.disable_advice_dispatching { SomeClass.new.some_method }

One can test to see if the API dispatching is disabled using the Aspect class method dispatch?, for example:

Aspect.dispatch?

Example

The following code examples implement a code profiler to measure the duration of method calls.

AspectR

require aspectr.rb
include AspectR

class Profiler < Aspect
  def method_start(method, object, exitstatus, *args)
    @begin = Time.now
  end
  def method_end(method, object, exitstatus, *args)
    timeElapsed = Time.now - @begin
    puts "#{object.class}.#{method} took #{timeElapsed} secs"
  end
end

#if $0 == __FILE__
  class SomeClass
    def some_method
      puts "hello"
      sleep 5
    end
  end

  Profiler.new.wrap(SomeClass, :method_start, :method_end, /some/)
  SomeClass.new.some_method
#end

AspectJ

References

External Links