Wednesday, January 13, 2010

Undoing Alias Method Chaining

This is more of a quick rant on AliasMethodChain in Rails than a tutorial... Metaprogramming in Rails is powerful. It is a wonderful way to build onto a framework which itself is built on top of Ruby via metaprogramming. I've seen and heard many arguments describing metaprogramming as the root of all evil. The chaos that it can cause when not structured is a terrible thing to behold, certainly.

I won't say much about how we handle our system extensions at Groupon, except, that it is very structured with a centralized place to discover all of the extensions to Rails internals in a single place (expect a more formal description of our process later from our Team!). Also, the extension points that we chose to use have been the parts of Rails which are the least subject to change (and have demonstrated the greatest resiliency and consistency over the last 3-4 years of active development).

Anyway, this morning, I ran into one of the (very few) cases where testing truly required assertions at various points in an alias_method_chain stack. As you can imagine, if you are alias_method_chaining ActiveRecord::Base#find, for example, you'll most likely have architected your system in such a way, that every link in the chain is standard, unalterable behavior (we absolutely do not muck with #find, btw. It is already an atrocious method). For these cases, I employ this little hack...


ActiveSupport::CoreExtensions::Module.class_eval do
def alias_method_chain_unlink(target, feature)

# Strip out punctuation on predicates or bang methods since
# e.g. target?_without_feature is not a valid method name.
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
yield(aliased_target, punctuation) if block_given?

with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"

alias_method target, without_method

case
when public_method_defined?(without_method)
public target
when protected_method_defined?(without_method)
protected target
when private_method_defined?(without_method)
private target
end
end
end

Notice its a natural inversion of 'alias_method_chain' implemention. This method would probably never fly with the core team, so it is merely a re-implementation/repurposing rather than an attempt to DRY the aliasing code.


As an example of what I'm talking about, imagine that you have this chain..

class User
def cry
puts "cry"
end
end

User.new.cry
User.class_eval do
def cry_with_emotion
puts "WAH WAH WAH"

cry_without_emotion
end

alias_method_chain :cry, :emotion
end

User.new.cry



Let us say that you want to alter the emotive nature of that extension in the same ruby session

User.class_eval do
def cry_with_emotion # slightly less emotive
puts "Wah Wah Wah"

cry_without_emotion
end
end

User.new.cry

User.class_eval do
define_method :cry_with_emotion do
puts "Wah Wah Wah"
end
end
User.new.cry # no luck again eh??


As you can see, redefining an interstitial method does not automatically make it part of the chain! Try this on for size (after redefining your method).


User.class_eval do
alias_method_chain_unlink :cry, :emotion

alias_method_chain :cry, :emotion
end
User.new.cry


That works! If you can imagine it, this hack can serve to either allow you to remove something out of a long alias_method_chain, or, allow redefinition of chain link (for lack of a better term)! I might have more to say about metaprogramming in Ruby later, but for now, may this approach save someone as much time as it saved me!

8 comments:

  1. Genial fill someone in on and this enter helped me alot in my college assignement. Gratefulness you seeking your information.

    ReplyDelete
  2. Nice dispatch and this mail helped me alot in my college assignement. Thank you on your information.

    ReplyDelete
  3. Amiable dispatch and this enter helped me alot in my college assignement. Say thank you you for your information.

    ReplyDelete
  4. Amiable fill someone in on and this post helped me alot in my college assignement. Gratefulness you on your information.

    ReplyDelete
  5. nice topic , hunt this from blogsearch and good luck for you.just tally up the rss feed to my reader,keep bring up to date!

    ReplyDelete
  6. Genial dispatch and this fill someone in on helped me alot in my college assignement. Thanks you for your information.

    ReplyDelete
  7. Sorry for my bad english. Thank you so much for your good post. Your post helped me in my college assignment, If you can provide me more details please email me.

    ReplyDelete