CSC/ECE 517 Fall 2007/wiki1b 5 b4
Assignment
AspectR is a very useful Ruby module, but it is not easy to find documentation on it that is appropriate for students taking this class. Find, or construct, documentation that explains what it does without presuming previous knowledge of AspectJ, that describes many or all methods of the module and how they work. Also find or produce an easy-to-understand example that does not involve logging.
Aspect-Oriented Programming and AspectR
AspectR is a Ruby module that allows you to easily and concisely use the Aspect-Oriented Programming (AOP) paradigm in your Ruby applications.
What is Aspect-Oriented Programming?
When designing and developing a software system, you must account for and handle the many concerns of the system. A concern is a requirement or action that must be performed by the system. For example, concerns for a banking system include making deposits, making withdrawals, handling account inquiries, transferring funds between accounts, authenticating and authorizing the user, and synchronizing and logging all transactions. The concerns at the beginning of the list are the primary, or core, concerns of the banking system, and would likely be handled by separate modules or classes. The concerns at the end of the list are secondary, and are required across many of the core modules. For this reason they are referred to in AOP as cross-cutting concerns.
The standard example used to demonstrate the benefits of AOP involves logging. A well-designed object-oriented system would have a separate class for logging, and that class would be utilized by all other classes in the system that require logging. By creating a separate logging class, the code that actually formats the logged data and writes it to file only needs to be written once. However, the code that invokes the logging module is still repeated and scattered throughout the system. As a result, the code is less readable, takes longer to develop, and is much more difficult and expensive to maintain. With AOP, you can weave the logging code into the rest of the system without actually modifying all of the other classes that need to use it. This would be done by creating a logging aspect and using special APIs to specify which classes and methods need to be logged. In this way, the logging concern is kept separate from all other concerns in the system.
Other concerns that are good candidates for AOP include security, database transactions, synchronization, and profiling. benefit: separation of concerns - function can be easilty added or deleted
You want to "wrap" a method with new code, so that calling the method triggers some new feature in addition to the original code.
You can arrange for code to be called before and after a method invocation by using method aliasing and metaprogramming, but it's simpler to use the glue gem or the AspectR third-party library. The latter lets you define "aspect" classes whose methods are called before and after other methods.
readme: Essentially it allows you to wrap code around existing methods in your classes.
Topics: What is AspectR? What is AOP? Benefits to using.
Downloading and Installing AspectR
In order to take advantage of the features of AspectR, you must first download and install it.
- Go to http://aspectr.sourceforge.net/ and download the tarball aspectr-0-3-5.tar.gz.
- Unpack the tarball. If you are on Windows, this can be unzipped using Winzip.
- From the newly created directory aspectr-0-3-5, execute "ruby install.rb".
These steps will place the AspectR module (aspectr.rb) in your Ruby library.
AspectR RubyDoc
add some verbage here
wrap
Arguments: target, pre, post, *args
Adds the advice specified by pre and post to all methods specified in args on the class or object target. To specify more than one method, pass a regular expression to args.
Example:
MyAspect.new.wrap(MyClass, :log_entry, :log_exit, /method/)
In this case, all calls to methods on MyClass with names beginning with "method" will be wrapped by calls to log_entry and log_exit.
unwrap
Arguments: target, pre, post, *args
Removes the advice specified by pre and post from all methods specified in args on the class or object target. To specify more than one method, pass a regular expression to args.
Example:
MyAspect.new.unwrap(MyClass, :log_entry, :log_exit, /method/)
In this case, the advice log_entry and log_exit will no longer be executed when calls to methods on MyClass with names beginning with "method" are made.
wrap_with_code
Arguments: target, preCode, postCode, *args
Wraps the code in preCode and postCode around the methods specified in args on the class or object target. This method results in faster execution than the wrap method and cannot be undone (i.e. there is no unwrap_with_code).
Example:
class SomeClass def method1(str) puts "executing SomeClass.method1 #{str}" end end Aspect.new.wrap_with_code(SomeClass, %q{puts "entering method"}, %q{puts "exiting method"}, /method/) SomeClass.new.method1 "test"
Output:
entering method executing SomeClass.method1 test exiting method
add_advice
Arguments: target, joinpoint, method, advice
Adds the advice specified by advice to the method method on the class or object target. Use a joinpoint of Aspect::PRE if the advice should be executed prior to the base method; otherwise, use Aspect::POST to execute the advice after the base method returns.
Example:
MyAspect.new.add_advice(MyClass, Aspect::PRE, :method1, :log_entry)
In this case, the advice log_entry will be executed just before the code in MyClass.method1.
remove_advice
Arguments: target, joinpoint, method, advice
Removes the advice specified by advice from the method method on the class or object target. Use the same value for joinpoint that was specified when add_advice was called (Aspect::PRE or Aspect::POST).
Example:
MyAspect.new.remove_advice(MyClass, Aspect:PRE, :method1, :log_entry)
In this case, the advice log_entry will no longer be executed just before the code in MyClass.method1.
disable_advice_dispatching
do this one?? what about Aspect.dispatch? all of these? http://rubyfurnace.com/docs/aspectr-0.3.7/classes/AspectR/Aspect.html
Example: Profiler Aspect
One common use of AOP is profiling. Profiling refers to the collection of data about the dynamic behavior of a system. It is done to determine a breakdown of where time and memory are being consumed at runtime and make optimizations based on the results. In the example below, which is based off a sample found in make a link -- Ruby Developer's Guide, we create an aspect that will determine the number of (milli)seconds spent in a single method. It is kept very simple to clearly illustrate the functionality of AspectR; a full-featured profiler would need to account for non-serialized and nested method invocations, aggregate the results, etc.
The Profiler class, which inherits Aspect, contains two methods: profiler_enter and profiler_exit. The before advice, profiler_enter, records the time immediately before the method is invoked. The after advice, profiler_exit, records the time immediately after the method exits and prints out the duration of the method invocation.
require 'aspectr' include AspectR class Profiler < Aspect def profiler_enter(method, object, exitstatus, *args) @enter_time = Time.now puts "#{@enter_time.strftime('%Y-%m-%d %X')} #{self.class}##{method}: #{args.inspect}" end def profiler_exit(method, object, exitstatus, *args) @exit_time = Time.now print "#{@exit_time.strftime('%Y-%m-%d %X')} #{self.class}##{method}: exited " if exitstatus.kind_of?(Array) print "normally returning #{exitstatus[0].inspect} " elsif exitstatus == true print "with exception '#{$!}' " else print "normally " end puts "after #{@exit_time.to_f - @enter_time.to_f} seconds" end end
A user of Profiler would need to wrap all methods that she is interested in profiling. The program below uses the Profiler aspect to calculate the amount of time spent in the SomeClass.method1 method call.
class SomeClass def method1(str) puts "entering SomeClass.method1 #{str}" sleep 5.3 puts "exiting SomeClass.method1" end end Profiler.new.wrap(SomeClass, :profiler_enter, :profiler_exit, :method1) SomeClass.new.method1 "test"
This program generates the following output:
2007-09-30 22:05:51 Profiler#method1: ["test"] entering SomeClass.method1 test exiting SomeClass.method1 2007-09-30 22:05:56 Profiler#method1: exited normally returning nil after 5.29699993133545 seconds
You can see from the Profiler output that SomeClass.method1 took approximately 5.3 seconds to execute, as expected.
Note: A profiler for Ruby programs, RbProf, is included in the AspectR distribution.