The link_to helper can accept either an options hash or a string to determine the href attribute when generating a link.
Since the options hash is passed through url_for, the ampersands in a query string containing multiple values are properly escaped to use the entity reference & instead of the '&' symbol.
But when the url is provided as a String, Rails simply sets the href attribute to the string value without checking for ampersands, which can cause an (x)html validation to fail if the user does not remember to escape the ampersands him/herself.
Solution: Since url_for is already equipped to handle an argument that is either a hash or a string, why not also pass string urls through url_for and ensure that ampersands will always be properly escaped?
A test showing that ampersands in string urls are left unchanged:
app/views/any/index.html.erb
<% link_hash = {:host => 'example.com', :controller => '/', :one => 'true', :two => 'true'} %>
<!-- This link created using url_for already converts ampersands to their entity references: -->
<%= link_to nil, url_for(link_hash), {:class => 'urlfor'} %>
<!-- Passing the hash directly also works: -->
<%= link_to nil, link_hash, {:class => 'hash'} %>
<!-- This link created by passing a string directly to link_to should do the same: -->
<%= link_to nil, 'http://example.com/?one=true&two=true', {:class => 'string'} %>
<!-- And since url_for protects against escaping twice, this should work too: -->
<%= link_to nil, 'http://example.com/?one=true&two=true', {:class => 'escaped'} %>
app/test/functional/any_controller_test.rb
require File.dirname(__FILE__) + '/../test_helper'
class AnyControllerTest < ActionController::TestCase
def test_for_escaped_ampersands
get :index
url_with_escaped_ampersand = 'http://example.com/?one=true&two=true'
['urlfor', 'hash', 'string', 'escaped'].each do |c|
assert_tag :tag => 'a', :attributes => { :href => url_with_escaped_ampersand, :class => c }
end
end
end