Rails named routes using complex parameter names

I need to create a nice route for an action expecting ugly nested parameter names (that's given, not something I might change at this time).

So instead of

http://domain.com/programs/search?program[type]=leader&program[leader_id]=12345

      

I want to

http://domain.com/programs/search/leader/12345

      

The problem is that Rails route definitions won't handle complex parameter names.

map.programs 'programs/:program[type]/:program[leader_id]', :controller=>..., :action=>...

      

Again, I cannot change the controller - it is configured to expect these parameter names based on a set of pre-existing search forms. I just want to create a more readable route for some predefined searches.

Of course, there must be a way to create a route that passes input to parameters with more complex names than single-word-downcase-alpha.

+2


source to share


2 answers


From what I am gathering from your post you need a url like

http://domain.com/programs/search/leader/12345

to create a params hash like

:params => { programs => {:type => "search", :leader_id => "12345"}}

      

I don't think it can be done. As far as I can tell, you cannot create a nested hash from named_route. Workarounds are possible, but each must modify the controller in some way.

I find this is the least intrusive solution: Set up a simpler route first,

map.programs 'programs/:program_type/:program_leader_id',
  :controller=>..., :action=>...

      



Then I would like to make the link_to or url_for available as a helper so that it can be passed by a programmatic object and make the url correct.

def link_to_programs name, prog
  link_to name, programs_url(prog, 
     :programs =>{:program_type => prog[:type], :prog[:program_leader_id]})
end

      

Then I need some way to trick the controller into thinking that it has been passed a hash of deeper parameters.

In program_controller:

def fix_bad_params
  params.merge!({:programs => {:type => params[:program_type],
    :leader_id => params[:program_leader_id])
end

before_filter :fix_bad_params, :only => :action_in_named_route

      

NB. The link_to switch I listed is incomplete. I used it for a simple demonstration. If you plan on passing other variations, or even using it in a different way of doing things (ex: link_to (@project), you will need to rewrite it. Look at the link_to source to see how best to do it.

+2


source


I'm afraid the built-in routing engines won't do what you're looking for. You can certainly disarm the routing engine, but that seems like a lot of work and can lead to other errors.

Have you ever thought about a pre-filter combined with a prettier route? For example:



# in config/routes.rb:
map.connect '/programs/:program_type/leader/:program_leader_id', ...

# in config/initializers/translate_pretty_program_routes.rb
module TranslatePrettyProgramRoutes
  def self.included(base)
    base.send :include, TranslatePrettyProgramRoutes::InstanceMethods
    base.send :prepend_before_filter, :translate_pretty_program_route_params
  end

  module InstanceMethods
    def translate_pretty_program_route_params
      params[:program] ||= {}
      params[:program][:type] ||= params[:program_type]
      params[:program][:leader_id] ||= params[:program_leader_id]
    end
  end
end

# in app/controllers/programs_controller.rb:
class ProgramsController < ApplicationController
  include TranslatePrettyProgramRoutes
end

      

Meta-programming TranslatePrettyProgramRoutes

is all about getting around class overloading issues in design mode. If it weren't for the class overload, you could just define a method and add it as before_filter

directly in the initializer.

+2


source







All Articles