Aspect4r Usage and Public API
Since most features I planned have been implemented, now is the time to document Aspect4r's public facing APIs.
Installation
gem install aspect4r
Usage
require 'aspect4r'
Add 'include Aspect4r' to class or module where advices will be defined.
API
When Aspect4r is included, four singleton methods are available for defining advices on target class/module: before, before_filter, after, around. They have same method signature below and process arguments similarly.
advice_type(methods, options={}, &advice_block)
- methods: a list of methods that will be advised. If no block is given, then the last method contains the advice logic and is applied to other methods. For example, 'before :test1, :test2, :do_something' means run do_something before test1 and test2.
- options [optional]: All advices can take method_name_arg and name option. Each type of advice can potentially take options only applicable to that type. Those options will be covered later in this document. If method_name_arg is true, then the advice method or block's first argument is the target method name so that advice logic can be customized for different methods, e.g.
before :test1, :test2, :method_name_arg => true do |method, *args|
if method == 'test1'
else
end
end
name option is used to uniquely identifiy an advice on a method. A method can not have 2 advices with same name. Advice method name is used as advice name if name option is not set. Example:
before :test, :name => 'preprocess' do
end
- advice_block [optional]: contains advice logic. If it is missing, methods should contain at least 2 methods, last one contains the advice logic.
before(methods, options={}, &advice_block)
Define an advice which runs before target methods. Currently there is no special option can be used for this type of device.
Normally, before advice will not halt execution of advices defined later and the wrapped method. However, there is a special wrapper class Aspect4r::ReturnThis. When the result of a before advice is an instance of ReturnThis, execution will stop and the result wrapped inside will be returned, e.g.
before :test do |input|
return Aspect4r::ReturnThis.new('default') if input.nil?
end
instance.test(nil) # => 'default'
before_filter(methods, options={}, &advice_block)
Define an advice which runs before target methods and if this advice returns false or nil, execution will halt. This is a special case of before advice. Currently there is no special option can be used for this type of device. Example:
before_filter :test do |input|
input >= 0
end
after(methods, options={}, &advice_block)
Define an advice which runs after target methods. After advice can take a result_arg option which is set to true by default. When it is true, the result of wrapped method or previous after advice is passed as the first argument to the advice, also the result of the after advice will be used as the method result. When result_arg is set to false, result will not be passed into advice method, and the advice result will not be used as the method result. Example
after :test do |result, input|
"<b>" + result + "</b>" # => new result
end
after :test, :result_arg => false do |input|
cleanup # will not change what test will return
end
around(methods, options={}, &advice_block)
Define an advice which runs around target methods. A proxy object which represents the wrapped method (and advices if any) is passed as the first argument to the advice method or block. The advice logic can decide whether and when the proxy is invoked. A special method a4r_invoke is provided to invoke the proxy object. Currently there is no special option can be used for this type of device. Example:
around :test do |proxy, input|
return if input.nil?
a4r_invoke proxy, input
end
Aspect4r::Classic
Because before, before_filter and after are common method names used by several Ruby libraries/frameworks (e.g. Rails, RSpec), to avoid confliction, Aspect4r provides a different set of methods (a4r_before, a4r_before_filter, a4r_after, a4r_around) with same functionalities. Here is how you use those instead.
class A
include Aspect4r::Classic
a4r_before :test do
end
a4r_after :test do
end
end
Test
See here for a example. I'll add more detail here later.
With above explanation and the advice execution model detailed in my other post, plus examples in the code base, you should be able to grasp the idea and use Aspect4r to write better organized code. Any comments / suggestions to the project and my posts are very welcome.


