Created
October 13, 2011 04:50
-
-
Save ryanlecompte/1283413 to your computer and use it in GitHub Desktop.
Providing an ActiveRecord-like before_filter capability to arbitrary Ruby classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# First the end result of what we want: | |
class Foo | |
before_hook :whoa | |
before_hook :amazing | |
def test | |
puts "This is kinda cool!" | |
end | |
def amazing | |
puts "Pretty amazing!" | |
end | |
def whoa | |
puts "Whoa!" | |
end | |
end | |
Foo.new.test | |
# OUTPUT BELOW | |
Whoa! | |
Pretty amazing! | |
This is kinda cool! | |
# And this is how I implemented it: | |
# Demonstrates how to provide ActiveRecord-like before_filter | |
# hooks for arbitrary classes. | |
module ExecutionHooks | |
# this method is invoked whenever a new instance method is added to a class | |
def method_added(method_name) | |
# do nothing if the method that was added was an actual hook method, or | |
# if it already had hooks added to it | |
return if hooks.include?(method_name) || hooked_methods.include?(method_name) | |
add_hooks_to(method_name) | |
end | |
# this is the DSL method that classes use to add before hooks | |
def before_hook(method_name) | |
hooks << method_name | |
end | |
# keeps track of all before hooks | |
def hooks | |
@hooks ||= [] | |
end | |
private | |
# keeps track of all currently hooked methods | |
def hooked_methods | |
@hooked_methods ||= [] | |
end | |
def add_hooks_to(method_name) | |
# add this method to known hook mappings to avoid infinite | |
# recursion when we redefine the method below | |
hooked_methods << method_name | |
# grab the original method definition | |
original_method = instance_method(method_name) | |
# re-define the method, but notice how we reference the original | |
# method definition | |
define_method(method_name) do |*args, &block| | |
# invoke the hook methods | |
self.class.hooks.each do |hook| | |
method(hook).call | |
end | |
# now invoke the original method | |
original_method.bind(self).call(*args, &block) | |
end | |
end | |
end | |
# Make it available to all classes. | |
Class.send(:include, ExecutionHooks) |
Thanks a lot, it helps me track of execution order of methods call. Just modified line 72 to method(hook).call(method_name)
and set before_hook : print_executed_action
Thanks.. very inspiring
I have created a version for a simple around_action
like functionality:
Thanks for this solution.
Any idea on how can we extend this before_hook
for Superclass as well? This before_hook
works when hook is added to a method of any class and method is called directly but doesn't work when the hook is added to a method of its superclass.
Thanks in advance!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this!
Any suggestions on passing a variable to the
before_hook
?e.g.:
before_hook :refresh_user_token(user)