Aspectory
Callbacks for Ruby.
How it works
Basically, you get three methods: before
, after
and around
. Each of
these takes a method name, then a splat args of symbols and/or a block. The
symbols/block will be called before/after the method you specified.
The around
callback gets passed a proc, in the form of an unnamed block
for handlers that are methods and a block argument for handlers that are
blocks. You must yield
or call
that block in order for the original
method to be called.
Simple Example
require 'rubygems' require 'aspectory' require 'spec' class Something include Aspectory::Hook attr_reader :results around :foo, :round before :foo, :setup after :foo, :teardown before :bar do @results << :before end around :bar do |fn| @results << :start fn.call @results << :finish end after :bar do @results << :after end def initialize @results = [] end def foo; @results << :foo; :foo end def bar; @results << :bar; :bar end def round @results << :start yield @results << :finish end def setup @results << :setup end def teardown @results << :teardown end end something = Something.new p something.foo # => :foo p something.results # => [:setup, :start, :foo, :finish, :teardown] something = Something.new p something.bar # => :bar p something.results # => [:before, :start, :bar, :finish, :after]
something = Something.new something.foo # => :foo something.results # => [:setup, :foo, :teardown] something = Something.new something.bar # => :bar something.results # => [:before, :bar, :after]
Calling Methods without Callbacks
You can use the #__PRISTINE__
method to call your methods without any
callbacks, or you can just call method_name_without_callbacks
. Here’s an
example with the same example class we used above:
something = Something.new something.__PRISTINE__(:foo) something.results # => [:foo] something.bar_without_callbacks something.results # => [:foo, :bar]
Preventing a method from being called
If a before
callback returns false
, then the original method will
not be called. If you want to halt the method being called, but still
want to provide a return value, you can throw
the name of the method:
class Something before :foo do throw :foo, "from the callback" end def foo "from the method" end end Something.new.foo # => "from the callback"
after
callbacks get the results of the method call
Your after
callbacks will be passed whatever the original method
call returned:
class Something attr_reader :name after :foo do |result| @name = result.to_s.capitalize end def foo :foo end end
something = Something.new something.name # => nil something.foo # => :foo something.name # => "Foo"
Observing method definitions
If you ever want to see when a method is defined in a class, you can register
observers using the observe
method. It can take either a symbol or a regular
expression, then a callback block will be called when the method is defined. The
callback block will be passed the name of the method defined.
To observe class method definitions, you must pass the :meta
option.
class Framework include Aspectory # Using a symbol observe :admin? do puts "Warning! Overriding the admin method can be dangerous." end # Using a regular expression observe(/^_/) do |method_id| puts "Warning! The #{method_id} is not part of the public API!" end # Observing a class method definition observe(:find_by_name, :meta => true) do puts "The method :find_by_name already exists in the framework." end # Observing multiple occurrences of a method definition observe(/^show_by_/, :times => true) do |method_id| puts "dynamic showing defined: #{method_id}" end end
Why?
Why not?
Requirements
-
nakajima
gem install nakajima-nakajima --source=http://gems.github.com
TODO
- Filters (
:if
and/or:unless
) - Compilable callbacks (http://gist.github.com/50397)
- Maybe don’t worry about @instance_eval@’ing or @instance_exec@’ing callback blocks.
- Figure out a way to get it working with metaclasses
- Spec suite could definitely be more readable
Alternatives:
(c) Copyright 2008 Pat Nakajima, released under MIT License.