Search code examples
ruby-on-railsalias-method-chain

Overring a Rails core mixin method, and still being able to call the old method


Ok title is confusing, Ill tell you my problem first:

the polymorphic_url method was changed in Rails 2.2.1 to include some extra functionality I need. However, I want to make the application still work in older versions of Rails, so I wanted to patch on the 2.2.1 behaviour if an older Rails version is running.

alias_method_chain to the rescue right? I cant get it to work.

def polymorphic_url_with_compat(*args)
  whatever...
  return polymorphic_url(*args)
end
alias_method_chain :polymorphic_url, :compat

Now I first tried putting this in the helper - alias_method_chain fails because polymorphic_url isnt defined by then. So I moved the same code into the controller, and it doesnt error, but it gets ignored. Then I tried patching it into ApplicationController::Base with a plugin - polymorphic_url is still not defined.

polymorphic_url is defined in the module ActionController::PolymorphicRoutes. I dont really know when/where it is mixed into the controller/view.

How can I wrap this method in the way i want to?


Solution

  • Before we proceed, are you sure that polymorphic_url existed in Rails version prior to 2.2.1?

    You're code is mostly right, you're forgetting to call the original version of the method. Which is renamed polymorphic_url_without_compat after the alias_method_chain call.

    class ActiveRecord::PolymorphicRoutes
      def polymorphic_url_with_compat(*args)
        whatever...
        return polymorphic_url_without_compat(*args)
      end
      alias_method_chain :polymorphic_url, :compat
    end
    

    You mentioned you tried to add it to a plugin, so the following may not be necessary if the previous bit fixed your problem.

    The best way to ensure it's loaded after the rails core, is to turn it into a plugin.

    $ script/generate plugin polymorphic_url_backport
    

    Will create a plugin stub. All directions from this point on are relevant to the created plugin directory.

    In the init.rb add

    if RAILS_GEM_VERSION < "2.2.1"
      require File.dirname(__FILE__) + '/lib/yournamespace'
      ActionController::PolymorphicRoutes.send(:include, YourNameSpace::ActionController::PolymorphicRoutes) 
    end
    

    Then paste the above code into your lib/yournamespace.rb

    module YourNameSpace
      class ActiveRecord::PolymorphicRoutes
        def included(base)
          base.class_eval %~
            def polymorphic_url_with_compat(*args)
              whatever...
              return polymorphic_url_without_compat(*args)
            end
            alias_method_chain :polymorphic_url, :compat
          ~
        end
      end
    end  
    

    Just make sure the plugin ships with your application and there should be no problems. You could alternately add this to the lib of your rails root, but I'm no sure exactly where you would place the require code to run it at the right time.