Christian Metts
2005-Feb-26 23:58 UTC
Pushing Routes to their limits (path based URLs for CMS).
Like a few others are doing, I''m working on a site that has a CMS
component that uses paths (that may contain one or more ''/''s)
as
database keys and in URLs. This allows for some very pretty URLs. I
want it to be able to use the controller/action/ url prefix normally
like: /page/edit/path/to/page
But also be able to work directly off the root so: /path/to/page
Would show the same thing as: /page/show/path/to/page.
It took some doing but I was able to get it to work just like this
under Routes. Here''s what I had to do.
Because there is no way to capture multiple parts of a path into one
parameter in Routes I had to tweak the mod_rewite rules a bit.
Instead of:
RewriteRule ^(.*)$ /dispatch.fcgi?$1 [QSA,L]
I now use this:
# because the page controller is the only one that needs path support I can
# restrict this to just urls that start with ''page'', this
puts the
path part I need
# in a parameter and passes it off to Routes to deal with the rest.
RewriteRule ^page/[-_a-zA-Z0-9]+/([-_a-zA-Z0-9/]+)$
/dispatch.fcgi?path=$1 [QSA] [L]
# This puts the entire path in a parameter and sends it along with
every request
RewriteRule ^(.+)$ /dispatch.fcgi?path=$1 [QSA] [L]
So now that I have my path we move on to the routes.rb. After all my
other routes I have these:
map.connect '':path'', :controller =>
''page'', :action => ''show''
map.connect '':a/:b/:c/:d/:e/:f'', :controller =>
''page'', :action => ''show'',
:a => nil, :b => nil, :c => nil, :d => nil, :e => nil,
:f => nil
map.connect ''page/:action/:path'', :controller =>
''page''
map.connect "page/:action/:a/:b/:c/:d/:e/:f", :controller =>
''page'',
:a => nil, :b => nil, :c => nil, :d => nil, :e => nil,
:f => nil
The fist version of each rule matches when doing URL generation.
The second one matches incoming requests and lets them through. It
also loads up a bunch of unnecessary parameters but that not really a
problem. It doesn''t assign a :path bit but that was taken care of at
the mod_rewrite step. It''s hacky and crufty and it''s not that
bad.
Which gets me 90% there. But because of the way Route.generate()
escapes URL components a url that should be /about/mission is written
as /about%2Fmission. On top of being ugly. It doesn''t work.
I ended up redefining ActionController::Routing::Route.generate() in
the bottom of my routes.rb to not apply CGI.escape to :path url
components. (By default it escapes all url components that aren''t
:controller). This is even more of a hack, and it''s a ~40 line method
so chances are good I''ll have to update this with any Rails update.
----
To anyone who needs to do this right now: This is a bit messy, but
it''s easier to work with than just using mod_rewrite as before.
Because url_for works properly now.
To Ulysses, DHH and any other Rails devs: Is there a way we can make
this not be so difficult?
It''s a bit of a fringe case and not worth adding alot of complexity
or options. So my vote would simply be for declaring :path a special
URL component . When recognizing it could eat up the entire rest of
the path. And when generating it wouldn''t escape the slashes.
-- Xian
Robert Williams
2005-Feb-27 00:28 UTC
Re: Pushing Routes to their limits (path based URLs for CMS).
I was messing around and found that you can do the following:
ActionController::Routing::Routes.draw do |map|
map.connect '''', :controller => ''page'',
:action => ''read'', :a => ''home''
map.connect '':controller/:action/:id'' #''
begin
map.recognise!
rescue # if all other mappings fail
path = '':a/:b/:c/:d/:e/:f'' #''
map.connect path, :controller => ''page'', :action =>
''read'', :a =>
''home'', :b => nil, :c => nil, :d => nil, :e =>
nil, :f => nil
end
end
Any help?
On Sat, 26 Feb 2005 17:58:16 -0600, Christian Metts
<mintxian.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
wrote:> Like a few others are doing, I''m working on a site that has a CMS
> component that uses paths (that may contain one or more
''/''s) as
> database keys and in URLs. This allows for some very pretty URLs. I
> want it to be able to use the controller/action/ url prefix normally
> like: /page/edit/path/to/page
>
> But also be able to work directly off the root so: /path/to/page
> Would show the same thing as: /page/show/path/to/page.
>
> It took some doing but I was able to get it to work just like this
> under Routes. Here''s what I had to do.
>
> Because there is no way to capture multiple parts of a path into one
> parameter in Routes I had to tweak the mod_rewite rules a bit.
>
> Instead of:
>
> RewriteRule ^(.*)$ /dispatch.fcgi?$1 [QSA,L]
>
> I now use this:
>
> # because the page controller is the only one that needs path support I
can
> # restrict this to just urls that start with ''page'',
this puts the
> path part I need
> # in a parameter and passes it off to Routes to deal with the rest.
> RewriteRule ^page/[-_a-zA-Z0-9]+/([-_a-zA-Z0-9/]+)$
> /dispatch.fcgi?path=$1 [QSA] [L]
> # This puts the entire path in a parameter and sends it along with
> every request
> RewriteRule ^(.+)$ /dispatch.fcgi?path=$1 [QSA] [L]
>
> So now that I have my path we move on to the routes.rb. After all my
> other routes I have these:
>
> map.connect '':path'', :controller =>
''page'', :action => ''show''
> map.connect '':a/:b/:c/:d/:e/:f'', :controller =>
''page'', :action => ''show'',
> :a => nil, :b => nil, :c => nil, :d => nil, :e
=> nil, :f => nil
>
> map.connect ''page/:action/:path'', :controller =>
''page''
> map.connect "page/:action/:a/:b/:c/:d/:e/:f", :controller =>
''page'',
> :a => nil, :b => nil, :c => nil, :d => nil, :e
=> nil, :f => nil
>
> The fist version of each rule matches when doing URL generation.
>
> The second one matches incoming requests and lets them through. It
> also loads up a bunch of unnecessary parameters but that not really a
> problem. It doesn''t assign a :path bit but that was taken care of
at
> the mod_rewrite step. It''s hacky and crufty and it''s not
that bad.
>
> Which gets me 90% there. But because of the way Route.generate()
> escapes URL components a url that should be /about/mission is written
> as /about%2Fmission. On top of being ugly. It doesn''t work.
>
> I ended up redefining ActionController::Routing::Route.generate() in
> the bottom of my routes.rb to not apply CGI.escape to :path url
> components. (By default it escapes all url components that aren''t
> :controller). This is even more of a hack, and it''s a ~40 line
method
> so chances are good I''ll have to update this with any Rails
update.
>
> ----
>
> To anyone who needs to do this right now: This is a bit messy, but
> it''s easier to work with than just using mod_rewrite as before.
> Because url_for works properly now.
>
> To Ulysses, DHH and any other Rails devs: Is there a way we can make
> this not be so difficult?
>
> It''s a bit of a fringe case and not worth adding alot of
complexity
> or options. So my vote would simply be for declaring :path a special
> URL component . When recognizing it could eat up the entire rest of
> the path. And when generating it wouldn''t escape the slashes.
>
> -- Xian
> _______________________________________________
> Rails mailing list
> Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org
> http://lists.rubyonrails.org/mailman/listinfo/rails
>