Rails support for HTML and XHTML is less than exemplary. For some reason,
the 1.1.1 release considers the application/xhtml+xml mime type to be a synonym
for text/html. Umm….why?
And it gets stranger still. If you’ve ever looked at the HTML that Rails
generates you’ll see markup that looks like this:
<head> <link ... /> </head> <input type="text" value="foo" name="bar" /><br />
Notice the extra “/>” characters? This usage comes from
none other than the W3C which recommends it
if you want older browsers (at this point only
Internet Explorer) to be able to render your XHTML as if it was HTML. Whether
this is a good idea or not is long-standing
religious debate that’s
not worth rehashing.
What is worth asking though is why is Rails doing this? Right now it provides
the worst of all worlds. First, it serves quasi XHTML to browsers as HTML.
This is wrong for Firefox, Safari and Opera which all understand XHTML. And
it serves XHTML to Internet Explorer which doesn’t understand it. In addition,
this quasi XHTML masquerading as HTML doesn’t validate against the HTML 4.01
strict standard or the XHTML standard.
Wouldn’t it be a whole lot easier to generate HTML 4.01 and
be done with it? The alternative solution is to implement content
negotiation so that XHTML is served to modern browsers and HTLM to Internet
Explorer. However, before you can even take the first step in this direction
you have to teach Rails that XHTML is in fact not HTML. Here is the code:
module ActionController module MimeResponds class Responder DEFAULT_BLOCKS = { :xhtml => 'Proc.new { render }' } for mime_type in %w( xhtml ) eval <<-EOT def #{mime_type}(&block) custom(Mime::#{mime_type.upcase}, &block) end EOT end end end end module Mime # Fix HTML #HTML = Type.new "text/html", :html, %w( application/xhtml+xml ) HTML = Type.new "text/html", :html # Add XHTML XHTML = Type.new "application/xhtml+xml", :xhtml # Fix xhtml lookup LOOKUP["application/xhtml+xml"] = XHTML end
Once you’ve done that, you need to change the ActionView::TagHelper::so
that it doesn’t add in the “/>” characters. However, at this point you run
into a problem. To figure out whether to close the tag or not requires knowing
the response content type, which is on the response object. Unfortunately,
the tag method is mixed into ActionView::Base as well as ActionView::Helpers::InstanceTag
(and maybe others classes also). The problem is that getting the the response
object is different in each case. It would be nice if there was a global
place to get it from. More about this in a future post.