The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Rails and Content Negotiation Revisited

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page


Posts: 201
Nickname: cfis
Registered: Mar, 2006

Charlie Savage
Rails and Content Negotiation Revisited Posted: Mar 31, 2006 2:18 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by .
Original Post: Rails and Content Negotiation Revisited
Feed Title: cfis
Feed URL: http://cfis.savagexi.com/articles.rss
Feed Description: Charlie's Blog
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by
Latest Posts From cfis

Advertisement

Let's revisit the subject of content negotiation in Rails. Now that I've upgraded to Rails 1.1 I've had a chance to play with the new responds_to functionality. To get some feedback, I also posted a comment to Jamis' blog and a message to the REST microformat list.

My conclusion is that adding content negotiation to Rails would be valuable. The new responds_to functionality is useful when you want to override the default behavior. Having content negotiation would be useful for making the default behavior more intelligent.

Templates already work in a similar manner. For example, say you write this code:

class MyController < ApplicationController
  def list
  end
end
Rails will assume that you want to render a template called list.rhtml. However, you can override this behavior as needed like this:
class MyController < ApplicationController
  def list
    render(:template => 'not_the_default_template')
  end
end

Content negotiation would work in a similar manner. It would load the appropriate template based on a client's request as specified in the HTTP accept header.You can then override that behavior when needed using responds_to.

The way I propose this works is that Content Types are mapped to templates via their file extensions. For instance:

Content Type Template Name
text/html list.rhtml
application/xhtml+xml list.rxhtml
text/javascript, application/javascript list.rjs
application/atom+xml list.ratom
application/atom+rss list.rss
application/x-yaml list.ryaml

 

One key thing to understand - the template extension does not dictate the template type. Thus, list.rxhtml could be an ERB template or a Builder template or any other sort of template.

Right now Rails doesn't support such a scheme because template extensions are hard-coded to template types. This is done in ActionView::Base::create_template_source:

def create_template_source(extension, template, render_symbol, locals)
  if template_requires_setup?(extension)
    body = case extension.to_sym
    when :rxml
      "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
      "@controller.headers['Content-Type'] ||= 'application/xml'\n" +
      template
    when :rjs
      "@controller.headers['Content-Type'] ||= 'text/javascript'\n" +
      "update_page do |page|\n#{template}\nend"
   end
  else
    body = ERB.new(template, nil, @@erb_trim_mode).src
  end
  
  @@template_args[render_symbol] ||= {}
  locals_keys = @@template_args[render_symbol].keys | locals
  @@template_args[render_symbol] = locals_keys.inject(
                                    {}) { |h, k| h[k] = true; h }
  locals_code= ""
  locals_keys.each do |key|
    locals_code << "#{key} = local_assigns[:#{key}]
                         if local_assigns.has_key?(:#{key})\n"
  end
  
  "def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"
end

It would be easy to work around this code since the template parameter includes the full source of the template. With a little regular expression magic, you could figure out what type of template has been loaded and then run the appropriate code.

However - what if we did something more clever? What if every template started with an comment that specified its type - similar to a Unix shebang or XML processing instruction. Maybe something like this:

For ERB:
<!-- template_type :erb -->

For RJS:
# template_type :rjs

For Builder:
<!-- template_type :erb -->

It would then be easy to extract out the template type when it was loaded. If the template comment did not exist (i.e., for all existing templates) then you could revert to regular expression magic.

This solution has one very nice property - it would make it really easy to add additional template types to Rails (not that its that hard now).

To recap, the idea is:

  • Implement default content negotiation by mapping content types to template names via file extensions (.rhtml, .rxthml, .rjs., .ratom, etc.)
  • Add code to Rails so that it determine a template's type when the template is loaded
  • Use responds_to to override the default behavior as needed.

 

What do people think of this idea? If there is interest, I'll update my content negotiation plugin and add this functionality to Rails 1.1.

Read: Rails and Content Negotiation Revisited

Topic: The Perl That Saved Me Previous Topic   Next Topic Topic: Rails 1.1: Selective Fixture Loading

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use