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 >