The thought has been bugging me since I was looking at the url_for code a few days ago... The knowledge of how URLs are set up for a Rails site is split between the Apache conf and the Rails code. What if the mod_rewrite rules just sent the entire path into dispatch.*, rather than parsing the controller, action, and id parts? Then the same Rails code could be responsible for translating to and from Rails params. Something like (totally untested, don''t actually use this): RewriteRule ^([-_a-zA-Z0-9]+/.*)$ /dispatch.cgi?path=$1 [QSA, L] Just the duplication of knowledge is smelly, but it seemed like the UrlRewriter code was less sure of what it was doing than the rest of the Rails code I''ve seen. I fixed a bug in UrlRewriter relating to the "index" action being the default, and guess where that policy is also expressed: mod_rewrite rules. Could be a coincidence, I suppose. Seems to me like there''s a missing abstraction here. Or am I all wet? -- Ryan Platte
> Seems to me like there''s a missing abstraction here. Or am I all wet?We are. It was one of the first wishes to make it on the list: http://www.rubyonrails.org/show/DispatcherRewritingWish I''m yet undecided on exactly how to best deal with the situation, but it''s obvious that it would be really nice to do just that. If nothing else, to make sure that webrick can use custom url schemes without hacking. -- David Heinemeier Hansson, http://www.basecamphq.com/ -- Web-based Project Management http://www.rubyonrails.org/ -- Web-application framework for Ruby http://macromates.com/ -- TextMate: Code and markup editor (OS X) http://www.loudthinking.com/ -- Broadcasting Brain
On Friday 19 November 2004 09:22, Ryan Platte wrote:> The thought has been bugging me since I was looking at the url_for > code a few days ago... > > The knowledge of how URLs are set up for a Rails site is split between > the Apache conf and the Rails code. What if the mod_rewrite rules just > sent the entire path into dispatch.*, rather than parsing the > controller, action, and id parts? Then the same Rails code could be > responsible for translating to and from Rails params. > > Something like (totally untested, don''t actually use this): > RewriteRule ^([-_a-zA-Z0-9]+/.*)$ /dispatch.cgi?path=$1 [QSA, L] > > Just the duplication of knowledge is smelly, but it seemed like the > UrlRewriter code was less sure of what it was doing than the rest of > the Rails code I''ve seen. I fixed a bug in UrlRewriter relating to the > "index" action being the default, and guess where that policy is also > expressed: mod_rewrite rules. Could be a coincidence, I suppose. > > Seems to me like there''s a missing abstraction here. Or am I all wet?Dispatching to mod_ruby, fcgi or plain cgi on basis of URL is quite nice though. So maybe just three rewrite rules, instead of the current set. Also, using mod_rewrite in a per directory setting is evil, and should, according to both Apache and mod_rewrite docs, be used only if there is absolutely no other alternative. I can''t use virtual hosts, so I''ve rewritten the .httpaccess rules to rewrite based only on the first element of the path of the URL. An advantage is that this can be dropped in /etc/httpd/conf.d without altering the main httpd.conf. You just have to be a bit more careful with absolute and relative URL''s in your templates. Cheers, Han Holl
I personally like the mod_rewrite solution because it gives you low level total control. For example you can map subdomains to parameters for things like "xal.mygallery.com". However this is a special case and the rails philosophy is to make common tasks easy, so i agree that we should look into moving it into the framework. As for mod rewrite: One good option to avoid having 3 sets of rules is to use the RewriteBase parameter. This also helps with non virtual hosting ( aliases ). example : RewriteBase /dispatch.fcgi RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)$ ?controller=$1&action=$2&id=$3 [QSA,L] RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)$ ?controller=$1&action=$2 [QSA,L] RewriteRule ^([-_a-zA-Z0-9]+)/$ ?controller=$1&action=index [QSA,L] This can easily changed into RewriteBase /my/sub/directory/dispatch.rb if required.> Also, using mod_rewrite in a per directory setting is evil, and should, > according to both Apache and mod_rewrite docs, be used only if there is > absolutely no other alternative.For which reason, Performance ? -- Tobi http://blog.leetsoft.com
On Friday 19 November 2004 17:18, Tobias Luetke wrote:> > Also, using mod_rewrite in a per directory setting is evil, and should, > > according to both Apache and mod_rewrite docs, be used only if there is > > absolutely no other alternative. > > For which reason, Performance ?Yes, mainly. Per directory is too late, really, so after rewriting the new URL is done again in a subrequest. And apache has to look at all .htaccess files in all ancestors of the home directory. Cheers, Han Holl
David Heinemeier Hansson wrote:> We are. It was one of the first wishes to make it on the list: > http://www.rubyonrails.org/show/DispatcherRewritingWishI''ve discussed an approach with David shortly on IRC, and promised to put up a full example. So here it is: # Suggestion for a Rails Rewriting Engine. # By Marten Veldthuis, 20041120 def rewrite # Given a raw url /path/to/somewhere, url.<name> compares from the # start of the currently remaining url (less a "/" prefix if it # exists), and if it matches: # # * it sets @params[<name>] to the matched string unless the second # argument +dont_set_param+ to the call url.<name> is +true+. # Second argument defaults to false. # * removes the matched string from the remaining url # * executes the given block, passing the MatchData url.section_name(/([-_a-zA-Z0-9]+)/) do |match| # (match contains an instance of MatchData) # In this case, my requirement is to match a given section name # to it''s type. For instance, some section may be called "weblog" # and be of the "journal" type, but another could be "see-also", # and of the "links" type. So to find out what controller to use # I must either pre-define all possible names and their types # (which is what I do now by writing a .htaccess automatically), # or I could look up the section here. # # I can fully understand if David finds this too smelly to allow it # in Rails. # @active_section would be passed into the controller#action (and # later the view) just like passing instance vars from action to # view. @active_section = Section.find_by_name(match[0]) # This sets what controller to use. Eg. <tt>vendor_journal</tt> or # <tt>vendor_links</tt>. Action @controller = "vendor_" + @active_section.type @action = "index" url.year(/\d{4}/) do # If a year is given, we want to display the archive instead. So, # we override the action. @action = "archive_year" url.month(/\d{1,2}/) do @action = "archive_month" url.day(/\d{1,2}/) do @action = "archive_day" url.entry_name(/([-_a-zA-Z0-9]+)/) do @action = "archive_entry" end end end end end # This is reachable only if the outmost match (url.section_name) # failed. Ofcourse, the same applies to any url.<name> call. url.admin(/admin/, false) do url.section(/sections\/([-_a-zA-Z0-9]+)/, false) do |match| @params["section_name"] = match[0] @active_section = Section.find_by_name(match[0]) # Notice how I''m ending this without having set a controller # anywhere. If by the end no controller or action has been set, # autodetection is attempted at the remaining url, following the # normal basic rules. If it fails, a HTTP 404 would occur. end end # Equivalent for the default rewriting rules @controller = "weblog" @action = "index" # No match has ever been made. At this point autodetection is started # trying to fill in the controller, action and id. If the url is # blank, the defaults from above are used, otherwise if it fails a 404 # occurs. If it''s successful in detecting at least a controller # (action would default to "index") obviously the controller is # called. end Comments? -- Marten Veldthuis