Rails and HTML and XHTML
Posted by Charlie Wed, 12 Apr 2006 08:47:00 GMT
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.
