ruby-on-railsrubyalias-methodalias-method-chain

alias_method, alias_method_chain, and self.included


I'm having a little difficulty understanding alias_method/alias_method_chain. I have the following code:

module ActionView::Helpers
  module FormHelper

    alias_method :form_for_without_cherries, :form_for

    def form_for(record, options = {}, &proc)
      output = 'with a cherry on top'.html_safe
      output.safe_concat form_for_without_cherries(record, options = {}, &proc)
    end
  end
end

This does exactly what I want to it to - append "with a cherry on top" to the top of any form_for call.

But my understanding is this isn't good code for a few reasons. Firstly, it won't chain in any other overrides of form_for(?) so if I were to write a second form_for method which appended "and yet another cherry on top", it wouldn't show. And secondly, alias_method and alias_method_chain are sorta outdated solutions, and I should be using self.included & sending to the module instead.

But I can't get self.included to call this form_for method - it just keeps calling the parent one. Here's what I'm trying:

module CherryForm
  def self.included(base)
    base.extend(self)
  end

  def form_for(record, options = {}, &proc)
    output = 'with a cherry on top'.html_safe
    output.safe_concat super(record, options = {}, &proc)
  end 
end

ActionView::Helpers::FormHelper.send(:include, CherryForm)

My above solution works, but I have a suspicion it's the wrong way of doing things. If any ruby veterans could show me a more elegant way of doing this - and/or why the second solution isn't being called - I'd be appreciative.


Solution

  • When you monkey patch something you must redefine the method, because there's no super to inherit from (so you can't use the second code excerpt).

    Copying the current method implementation and adding your own is just asking for trouble, so this is where alias_method comes in.

    alias_method :form_for_without_cherries, :form_for
    

    It actually creates a clone to the original method, which you can use instead of super. The fact that you can't chain monkey patches is not a bug, it's a feature.

    The rails alias_method_chain method was indeed deprecated, but only because it was a poor idea from the start. However alias_method is a pure ruby method, and when used correctly can provide an elegant way of monkey patching your code, that's why I'm pretty sure it's not going away any time soon.