RELEASE: Redirect Routing Plugin for Rails

by Lars Pind on July 18, 2006

Here’s another simple plugin hot on the heels of my previous ExceptionTextable plugin: A plugin to add redirects straight in your routes.rb file.

First, to install say:

script/plugin source http://svn.pinds.com/rails/plugins
script/plugin install redirect_routing

Then add this to your routes.rb:

map.redirect '', :controller => 'events'

And now the root of your site will redirect to the index action of the events controller.

Why not just map.connect? Because map.connect causes the URL in the browser to stay at /, which isn’t very RESTful.

Note that you can also pass a string:

map.redirect 'lars', 'http://pinds.com'

Now /lars will redirect to my blog.

See the README for more details. Enjoy!

{ 15 comments }

Pelle July 18, 2006 at 10:03 pm

Very useful. I will definitely use it.

Jarkko Laine July 18, 2006 at 10:03 pm

Very cool, Lars. That sounds extremely useful for supporting legacy url’s. Any plans to make it support variables/regexps?

Like:

<code><pre>
map.redirect ‘blog/:id’,
Proc.new{|id|
@post = Post.find(id)
{:day => @post.day, … , :title => @post.title}
}
</pre></code>

Ok, maybe that was a bit too involved… but it *would* make supporting textpattern-type url’s easy in Typo :-)

Lars Pind July 18, 2006 at 10:03 pm

Interesting idea.

I’m thinking the routes should stay declarative. They’re not meant as a replacement for controllers.

I could see how you’d want to do some simple regsubbing or use of parameters, but I think it ends there.

Thijs van der Vossen July 18, 2006 at 10:03 pm

Very nice! Would be great to have this in Rails by default. Why not try to get it in there for the next release?

Lars Pind July 18, 2006 at 10:03 pm

The only problem is that I’m creating a special controller behind the scenes and then leveraging the normal controller and response’s url rewriting and redirecting capabilities.

The rewriting part is actually handled by the Routing system, so that should be easy, and sending the redirect is trivial, so that could be done as well, but it wouldn’t be completely DRY.

If there’s interest from core I’ll be happy to explore.

robin July 18, 2006 at 10:03 pm

when i put this in my vendors/plugins I get the following…

… /dependencies.rb:123:in `const_missing’: uninitialized constant Mapper (NameError)
from /usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:133:in `const_missing’

rails don’t start..

something I’m missing?

Lars Pind July 18, 2006 at 10:03 pm

Did I mention this only works with edge and the rewritten routing implementation?

Daniel Butler July 18, 2006 at 10:03 pm

I get the same error as robin while I’m trying to set this up on a customized Typo installation, which is pegged to non-edge Rails. Anyone have any suggestions about how to accomplish redirects (or support for legacy URLs) without this plugin?

Craig Barber July 18, 2006 at 10:03 pm

I altered your plugin to support:

map.redirect ‘/old_controller/:action’,
:controller => ‘new_controller’

by adding the following to RedirectRoutingController:

def method_missing(action)
redirect_to params[:url_options].merge({ :action => action })
end

P.S. – I *really* like the automatic code detection. Is that part of typo?

Lee Iverson July 18, 2006 at 10:03 pm

OK, so I bit the (very soft) bullet and got this working with regexps in the redirect options. The changes are simple:

<pre>
module RedirectRouting
module Routes
def redirect(path, options={})
url_options = { }
connect_options = {
:controller => "redirect_routing", :action => "redirect",
:url_options => url_options }
options.each do |key,value|
if [:controller, :action].include?(key)
url_options[key] = value
else
connect_options[key] = value
end
end
connect path, connect_options
end
end
end
</pre>

and

<pre>
class RedirectRoutingController < ActionController::Base
def redirect
url_options = params.delete(:url_options)
redirect_to params.merge(url_options)
end
end
</pre>

Michael Hartl July 18, 2006 at 10:03 pm

Hi Lars,

Thanks a bunch for this plug-in. I just used it on the blog for a Rails book I wrote with Aurelius Prochazka, one of Philip Greenspun’s cofounders at ArsDigita. File under: small world. :-)

Cheers,

Michael

Lars Pind July 18, 2006 at 10:03 pm

Hi Michael

Yeah, that is funny. Small world. Aure did tell me that he was writing a book a while back. Congrats on getting it done!

//Lars

Ian Heggie July 18, 2006 at 10:03 pm

Hi, A variation on the theme to handle :action as well as keep other options:
{{{
class RedirectRoutingController < ActionController::Base
def redirect(action = nil)
args, options = params[:args] || [], {}
options = params[:args].pop if Hash === params[:args].last
response.headers["Status"] = "301 Moved Permanently" if options.delete(:permanent)
raise ArgumentError, "too many arguments" if args.size > 1
options.merge({ :action => action }) if action
redirect_to args.empty? ? options : args.first
end

def method_missing(action)
redirect(action)
end

end
}}}

Neil Smith July 18, 2006 at 10:03 pm

Hi there.

This is a nice plugin that I’m using for supporting some legacy urls. However, although everything worked fine in the ‘development’ environment, things were a bit odd in the ‘production’ environment (I’m running Rails 1.2.5 on Mongrel.)

I found that the first time I tried to redirect, everything worked fine – but on subsequent hits to the page that should redirect, I’d see "ActionController::RoutingError (no route found to match "/redirect_routing/redirect" with {:method=>:get})" – and the args being logged were blank, which was not the case on the first request.

After a little bit of digging, it appears to be related to the modification of params[:args]. For instance, this quick fix – of ‘dup’ing before we begin – appears to make things work for me in Production:

class RedirectRoutingController < ActionController::Base
def redirect
arguments_from_params = params[:args].dup

args, options = arguments_from_params || [], {}
options = arguments_from_params.pop if Hash === arguments_from_params.last
response.headers["Status"] = "301 Moved Permanently" if options.delete(:permanent)

raise ArgumentError, "too many arguments" if args.size > 1
redirect_to args.empty? ? options : args.first
end
end

But things still aren’t quite right: I’m using permanent redirects, and I get {:permanent => true} being passed through on the first request, but not on subsequent requests – this then generates me a 302 Moved Temporarily redirect from the 2nd time onwards.

(Again, this is only the case in the Production environment.)

Any thoughts on what might be causing this?

Many thanks again for the plugin!

Cheers,
Neil.

Neil Smith July 18, 2006 at 10:03 pm

(Rats. Sorry about the formatting.)

Comments on this entry are closed.

Previous post: Apple and Nike

Next post: Assign a keyboard shortcut to ReCSS in Firefox on OS X