codahale.com٭blog

This is my old blog. My current writing is here: codahale.com

Rails Plugin: xhtml_content_type

xhtml_content_type allows you to set the default MIME type for rendered .rhtml views to application/xhtml+xml if the client supports it, and only falling back to text/html for older clients.

For more information as to why this is good behavior, read this: http://hixie.ch/advocacy/xhtml

Installation

If your project is source-controlled by Subversion (which it should be, really), the easiest way to install this is via Rails’ plugin script:

./script/plugin install -x http://svn.codahale.com/xhtml_content_type

If you’re not using Subversion, or if you don’t want it adding
svn:externals in your project, remove the -x switch:

./script/plugin install http://svn.codahale.com/xhtml_content_type

Alternatively, you can just check the trunk out from the repository, if you’re super-DIY.

cd path_to_rails_app
cd vendor/plugins
svn co http://svn.codahale.com/xhtml_content_type

Usage

xhtml_content_type is super easy to use. Add the method sends_xhtml_with_correct_content_type to a specific controller, or to ApplicationController to make all controllers send XHTML properly:

  class ApplicationController < ActionController::Base
    sends_xhtml_with_correct_content_type
  end

sends_xhtml_with_correct_content_type also accepts standard filter-style conditions, if you need them:

  class MySpecialController < ApplicationController
    sends_xhtml_with_correct_content_type :except => [:seriously_weird_action]
  end
  class MyOtherSpecialController < ApplicationController
    sends_xhtml_with_correct_content_type :only => [:the_only_regular_action]
  end

You can also explicitly specify an XHTML content type using this plugin:

  def xhtml_only_action
  	render :content_type => :xhtml
  end

Have fun!

15 Responses to “Rails Plugin: xhtml_content_type”

  1. recumbentJ Says:

    I’ve installed the the plugin OK and Firefox is now seeing my app as xml, trouble is it is complaining that:

    “This XML file does not appear to have any style information associated with it. The document tree is shown below.”

    I’m using the stylesheet_link_tag link helper which seems to like appending random query strings to the end of the stylesheet url, which I guess is to get around user agent caching. E.g.:

    I’ve removed the meta tag that defined the content as text/html but that hasn’t seemed to help.

    Any ideas or am I just being a muppet?

    J.

  2. recumbentJ Says:

    Style sheet link didn’t get escaped in last comment, maybe this will work - a preview option would be nice :)

    <link href=”/stylesheets/default.css?1145397395″ media=”screen” rel=”Stylesheet” type=”text/css”/>

  3. recumbentJ Says:

    Yeah, I was being a muppet.

    Forgot to define the xml namespace. For those interested, using the following got Firefox to play ball:

    <html xml:lang=”en” lang=”en” xml‎ns=”http://www.w3.org/1999/xhtml”>

  4. Jason Barnabe Says:

    I’m trying to use this, it didn’t work at first, then after I restart I get

    ./script/../config/../vendor/plugins/xhtml_content_type/lib/xhtml_content_type.rb:45:in `remove_const’: constant Mime::HTML not defined (NameError)
    from ./script/../config/../vendor/plugins/xhtml_content_type/lib/xhtml_content_type.rb:45
    from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:21:in `require’
    from /usr/lib/ruby/gems/1.8/gems/activesupport-1.2.5/lib/active_support/dependencies.rb:214:in `require’
    from script/../config/../vendor/plugins/xhtml_content_type/init.rb:1:in `load_plugin’
    from /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/initializer.rb:311:in `load_plugin’
    from /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/initializer.rb:311:in `silence_warnings’
    from /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/initializer.rb:311:in `load_plugin’
    from /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/initializer.rb:145:in `load_plugins’
    … 10 levels…
    from /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/commands/server.rb:28
    from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:21:in `require’
    from /usr/lib/ruby/gems/1.8/gems/activesupport-1.2.5/lib/active_support/dependencies.rb:214:in `require’
    from script/server:3

    and now the server won’t start.

  5. Coda Says:

    Jason: You’re using Rails 1.0. This plugin was developed for 1.1.2. Either uninstall the plugin or upgrade Rails to 1.1.2 (which, incidentally, will provide you with a myriad of other goodies, such as RJS, nested includes, and rich HABTM associations).

  6. Doug Dupory Says:

    Hello…

    I like to try it out, but have trouble installing the plugin:

    script/plugin install -x http://svn.codahale.com/xhtml_content_type
    svn: PROPFIND request failed on ‘/xhtml_content_type’
    svn: PROPFIND of ‘/xhtml_content_type’: 301 Moved Permanently (http://svn.codahale.com)

    Could the problem be the following? http://subversion.tigris.org/faq.html#301-error

  7. Coda Says:

    Sorry about that, Doug. Fixed now.

  8. Aurélien Says:

    This plugin breaks the respond_to behaviour. When I deactivate JS on my firefox, the called block is wants.js, and if not present, I get an nil.call exception.

  9. Seth A. Roby Says:

    I, too, am seeing the respond_to breakage; it appears to be a difference between the @order mime_type having the synonym for xhtml and the Mime::HTML not having it; the @responses[priority].call line in mime_responds.rb (line 166 in rev 5154) hits the nil; the [] lookup fails to find anything, but a @responses[Mime:HTML] finds the proper block. So it seems that either the @responses should be populated with the same mime-type as @mime_type_priority is populated with, or Mime::Type needs to have a better-defined equality relationship for the hash.

  10. Seth A. Roby Says:

    Okay, I’ve gotten it working. The key to the puzzle is that response.accepts builds it accept list using Mime::Type.lookup, which still pulls the old Mime::HTML from before the plugin loads, which means that it has the application/xhtml+xml synonym.

    This can be found with the new test I added to xhtml_content_type_test.rb:


    def test_mime_lookup
    const = Mime::HTML
    lookup = Mime::Type.lookup "text/html"
    assert_equal const, lookup
    end

    So we add a single line into the plugin proper:


    module Mime #:nodoc:
    remove_const('HTML') # oh we're so polite
    HTML = Mime::Type.new "text/html", :html
    LOOKUP["text/html"] = HTML
    XHTML = Mime::Type.new XHTMLContentTypeNegotiation::XHTML_MIME_TYPE, :xhtml
    LOOKUP[XHTMLContentTypeNegotiation::XHTML_MIME_TYPE] = XHTML
    end

    And now test_mime_lookup passes. But what about respond_to? Let’s add a method to our test ApplicationController:


    def with_respond_to
    respond_to do |wants|
    wants.html {render :text => 'Ich ben ein HTML'}
    wants.js {render :text => 'Ich ben ein ECMA', :content_type => :js}
    end
    end

    And a test for it:


    def test_with_respond_to
    make_controller :conditionless

    get_with_accepts :with_respond_to, 'text/html'
    assert_content_type 'text/html'

    get_with_accepts :with_respond_to, 'application/xhtml+xml,*/*'
    assert_content_type "application/xhtml+xml"

    get_with_accepts :with_respond_to, 'text/javascript'
    assert_content_type :js

    xhr :get, :with_respond_to
    assert_content_type :js
    end

    Run our tests… and they pass! Hooray for one-line fixes!

  11. Seth A. Roby Says:

    So the above worked on my Windows box, but my Mac wants one more line added to the plugin proper; LOOKUP[:html] = HTML makes it happy.

  12. Coda Says:

    Seth, thanks for the fix, and thanks for reminding me why writing software for other programmers is so much more fun. ;-)

    I’ve updated the SVN repository with your fix.

  13. toupeira Says:

    Hi, great plugin, but I noticed that it removes the charset from the Content-Type header, which my apache installation adds automatically. I worked around it by just appending it after the XHTML_MIME_TYPE, but maybe this could be made dynamic?

  14. Rob Says:

    Great plugin! Thanks.

  15. Paul Groves Says:

    Having trouble with respond_to in Rails 2.0.2 - always outputs xml when enabled. . . would appreciate any help as this is a great plugin.

    Thanks,

    Paul