There's currently a bug in Safari, or more likely somewhere in the OS X HTTP API that breaks HTTP Authentication when there is a semicolon in the path part of the URL. This means it's impossible to use HTTP Authentication when you're building a RESTful Rails app that needs to work in Safari.
Right now this still seems to be broken in Leopard. It would be great if somebody who knows the right people inside Apple could try to raise awareness of this issue. It would be somewhat ironic if Leopard Server ships with Rails while this is still broken.
Great idea to try escaping the semicolon!
For those interested, you can work around this issue by adding the following to your ApplicationController in app/controllers/application.rb:
# make HTTP Authentication work on Safari for RESTful Rails
alias_method :orig_url_for, :url_for
def url_for(options = {}, *parameters_for_method_reference)
result = orig_url_for(options, parameters_for_method_reference)
if request.env['HTTP_USER_AGENT'].to_s.include? 'AppleWebKit'
result.is_a?(String) ? result.gsub(';', '%3B') : result
else
result
end
end
Very nice, Thijs. A minor nitpick, and nothing to do with the actual solution: when overriding a method in a subclass, you can just call 'super' to get at the original. You don't actually need to alias the original. The alias trick is only needed when you are altering a method of the current class. For instance, if you were monkeypatching a new url_for implementation into ActionController::Base itself, you'd need to use alias there to preserve the original url_for.
At first I tried to call super as Tim did, but I ended up with an url without the ';edit' part while using the url helpers generated by ActionController::Resources.
I've looked into this again and it seems that I need to explicitly pass the parameters to super to make it work. Like this:
def url_for(options = {}, *parameters_for_method_reference)
result = super(options, parameters_for_method_reference)
if request.env['HTTP_USER_AGENT'].to_s.include? 'AppleWebKit'
result.is_a?(String) ? result.gsub(';','%3B') : result
else
result
end
end
My understanding of super was that it passed the original method's parameters, but maybe not in this case.