rails models are views?

Rails appears pretty strict about separation of the abstraction layers that make up its notion of a web application: models, view and controllers. If you were to suggest calling a presentation method, such as url_for, in your model, the stoic Rails advocate will have an allergic reaction. However, Rails thinks nothing of rendering a model directly as a view, such as:

format.json { render :json => @products }

Now, one might argue that this is controller code and the controller is allowed to interpret the model as a view. The controller’s job is to mediate this interaction. However, I feel that it is a dangerous shortcut, made even more so by how hard to seems to be to override. Perhaps the json implementation is simply incomplete.

In xml, this strange controller pattern is easily corrected by providing an xml view. The xml builder syntax is particularly readable, and it is easy to design your XML API effectively.

I haven’t found an equivalent for json. I tried to use a JSON API today to no avail. My model included image data which breaks when auto-rendered in JSON. What I really wanted was to include a URL instead of the image data, which I implement neatly in my xml.builder view:

xml.instruct!
xml.products("type"=>"array") do
  @products.each do |product|
    xml.product do
      xml.sku product.sku
      xml.name product.name
      xml.brand product.brand
      xml.img_url url_for(:controller => :products, :action => :show, :format=>:png, :id => product.id, :only_path => false)
    end
  end
end

The problem is that I want a similar view in JSON. The to_json API leads me to put this logic in my model (gasp!). In fact, the ActiveRecord::Serialization docs give an example of providing a method to generate JSON instead of a literal attribute. The example is of a “permalink” which seem suspiciously like something that belongs is the view layer.

  konata.to_json(:methods => :permalink)
  # => {"id": 1, "name": "Konata Izumi", "age": 16,
        "created_at": "2006/08/01", "awesome": true,
        "permalink": "1-konata-izumi"}

Today’s solution was to go back to using my comfortable old XML API, but I would prefer to consume JSON from the other side. I wonder if anyone is working on a JSON builder or if there is some clear solution that I haven’t yet stumbled upon.

This entry was posted in code. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

3 Comments

  1. Posted August 31, 2009 at 9:31 pm | Permalink

    I agree wholeheartedly about the inappropriateness of the to_json and to_xml methods. As soon as you want to start messing with them, you’re putting view code into the model layer, which is just wrong. I was surprised there wasn’t a JSON builder/template handler, so I hacked one out. It’s up on github at http://github.com/jbr/argonaut. I’m not certain that I feel the builder pattern is more elegant than just building up a Ruby hash and calling to_json on it, but it was certainly fun to build. Yay Rails!

  2. Posted August 31, 2009 at 9:35 pm | Permalink

    It definitely feels like a glaring omission that there is not a JSON builder similar to xml builder. The way I’ve gotten around this in an app where I had a similar issue was to create an erb template (i.e. index.json.erb) and build the json response inside by hand. Then just render the template in the format.json block instead. Seems like a kludge though.

  3. seth
    Posted September 1, 2009 at 12:00 am | Permalink

    fresh out of rails rumble, a haml-like json generation language: http://rozenbom.r09.railsrumble.com/tequila.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*