If you are building a fullfledged application (versus just a web service), you may want to support both HTML and XForms clients (and possibly other clients such as WAP). This tip describes how to do this efficiently, in a way that is very much not the “standard Rails way” (but is much simpler).

Using A Common Client Checker Inside ApplicationController Class

Include the following code inside your parent application controller class (application.rb) which you use for all controllers in your application.

def checkClient
  if (@request.env['HTTP_ACCEPT']=="application/xml")
    @client="xf" 
    if @request.env['REQUEST_METHOD'] == "POST" 
      paramsFromXml(@request.env['RAW_POST_DATA'],'data')
    end 
  else
    @client="html" 
  end
end
def paramsFromXml(xml,parentname)
  doc=Document.new(xml)
  @params={}
  doc.root.elements.each do |x|
    @params[x.name]= x.text
  end
end

We will add this to the Rails application generator at some point. Note that the first method is the essential part: figuring out that the client is either “xf” (xforms) or “html”.

The second method handles the fact that Mobio’s XForms runner just populates XML in the POST data and doesn’t give the developer an @params hash to work with. paramsFromXml() populates the @params hash from the top level elements below the root XML element. This allows you to write a single controller action that handles data being available in an @params{} hash.

Note that if you wish to just manually parse the XML yourself then you may not need to use this second method. It is merely a “convenience” method for those of you that like to use a params hash in your controller actions

Hooking the Client Checker into Your Controller

Use the Rails filter capability in your controller to integrate the checkClient call to be called before each controller action. Specifically put the line “before filter :checkClient” at the top of each of your controllers. For example,

  class YourController < ApplicationController
   before_filter :checkClient
   yourMethod
      ...your method code....
   end
  end

We will be modifying our controller generator to include this call.

Render with the Appropriate Template

Put the following code at the end of each method.

   yourMethod
     ... your code
     render :action => "#{action_name}.r" + @client
   end

What this will do is either execute the ”.rhtml” template or the ”.rxf” template based on what client is accessing it. Note that you will need to follow the convention of calling your XForms templates ”.rxf” files. Again we will modify the controller generator to stub in these render calls

Note that this whole methodology is quite different from the “respond_to” mechanism proposed by Rails creator David Heinemeier Hanson. The method he proposes pollutes each controller action with several lines of code (versus adding ”@client” to the end of the render call as shown above).