Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Internal Redirects with Rails 3

I am trying to implement a generic vanity url system in Rails 3. Generic in the sense that the vanity url isn't tied to a specific model. It is similar to the Vanities gem where I have a VanityUrlController that is hit from all the vanity urls. The difference is that I don't want to do an external redirect from foo.com/username to foo.com/users/1 or foo.com/product-name to foo.com/products/1. I want the vanity url to to stick around and have the the VanityUrlContoller do an internal redirect that mimics the corresponding show action.

I know what controller and action I want to dispatch the internal redirect to, but I am having problems with actually dispatching it. This is where I am at the moment:

TargetController.new.process("show", request.env)

It seems to start processing the new "request," but there are key pieces missing... like the actual request object.

Any thoughts or pointers would be very appreciated.

Update:

I ran across the dispatch method in ActionController which seems to get me a little farther.

TargetController.new.dispatch("show", request)

I have two problems with this, 1) it is listed as a private api method so if there is another way to do this, I'd rather that, and 2) even though it is rendering the show template for the TargetController, it is complaining about "Missing template vanity_urls/show."

UPDATE

Here is the basics of the solution we came up with. We do some other things like forcing encodings and checking some other application specific stuff, but this should be all you need to get going.

This goes at the very bottom of your routes.rb file so your vanity routes don't clobber your other named routes.

# Vanity routes.
match ':id', :as => 'vanity', :to => proc { |env|
  id = env["action_dispatch.request.path_parameters"][:id]

  vain_object = <method to find the object you want to display>
  if vain_object.nil?
    # render your 404 page
    'application#404'
  else
    model = vain_object.class.model_name
    # figure out the controller you want to go to 
    controller = [model.pluralize.camelize,"Controller"].join.constantize
    # reset the :id parameter with the id of the object to be displayed
    env["action_dispatch.request.path_parameters"][:id] = vain_object.id
    # do your internal redirect
    controller.action("show").call(env)
  end
}

You'll also want to be careful while creating your vanity routes, so they don't collide with your other controllers. Some other useful things to know about are:

Rails.application.routes.routes.any? { |r| r.requirements[:controller] == vanity_url }

Which tells you if your vanity_url has the same name as a current controller.

Rails.application.routes.recognize_path("/#{vanity_url}", :method => :get)

Which tells you if this maps to anything already.

Sure, there are a couple of hacks along the way, but it works like a charm.

like image 212
HMCFletch Avatar asked Feb 14 '11 20:02

HMCFletch


1 Answers

Try the FriendlyId plugin. It seems to do exactly what you want. Video tutorial: http://railscasts.com/episodes/314-pretty-urls-with-friendlyid

like image 58
FranticRock Avatar answered Sep 28 '22 16:09

FranticRock