Aspect Orientation Made Simple

Hooked lets you transparently aspectify your methods and blocks.

Build status

Getting Started

By including Hooked into a class you basically say "everything can attach code to methods of this class' objects".

require "hooked"

class Foo
  include Hooked

  def breakfast
    puts "No milk?!"
  end
end

class BetterFoo
  def shower
    puts "Oooh..."
  end

  def fuuu(inner)
    puts "FUUU!"
    inner.call
    puts "FUUU!"
  end
end

Foo.extend Hooked
Foo.before :new do
  puts "Creating object..."
end

foo, better_foo = Foo.new, BetterFoo.new

foo.before :breakfast, better_foo.method(:shower)
foo.after :breakfast do
  puts "Mmmh..."
end
foo.around :breakfast, better_foo.method(:fuuu), :before => [better_foo.method(:shower)]

foo.breakfast

This will output:

Creating object...
Oooh...
FUUU!
No milk?!
FUUU!
Mmmh...

You can also aspectify instance methods of classes in order to avoid the need of aspectifying the respective methods of each new object.

class Foo
  include Hooked

  def breakfast; end
end

Foo.instance_after :breakfast do
  puts "ZOMG hooked!1"
end

Foo.new.breakfast

This will not affect objects that have been created before including Hooked.

Execution Model

Hooked converts a method's aspects into a Directed Acyclic Graph, flattens it using TSort and finally forms a Chain of Responsibility with the original method at its end. As you can see in the below ASCII art, the aspect type (before, after, around) doesn't determine sorting order. Instead it determines if the aspect executes its code on the way into or out of the chain.

-->-| #1 before |-->-|           |-->-| #3 before |-->-|          |-->-|                 |
    |           |    | #2 around |    |           |    |          |    | original method |
-<--|           |-<--|           |-<--|           |-<--| #4 after |-<--|                 |

Before / After / Around

TODO

Execution Order of Aspects

TODO

Possible Use Cases

You can use the Hook class to programmatically build graphs of aspects.

TODO

Dependencies / Compatibility

Specs pass on MRI 1.9.2, Rubinius 1.2.3, JRuby 1.6.1 and MRI 1.8.7.

To do & Ideas

  • Visualization of hook flow
  • Make backtraces more readable

Contributing

  1. Fork at github.com/lgierth/hooked
  2. Create a new branch
  3. Commit, commit, commit!
  4. Open a Pull Request

You can also open an issue for discussion first, if you like.

License

Hooked is subject to an MIT-style license (see LICENSE).