Rails and HTML and XHTML

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.

Leave a Reply

Your email address will not be published.

Top