Skip to content

Instantly share code, notes, and snippets.

@paul
Created September 22, 2008 19:05
Show Gist options
  • Save paul/12096 to your computer and use it in GitHub Desktop.
Save paul/12096 to your computer and use it in GitHub Desktop.

RubyJson Merb Views

Given this model (DataMapper):

class Article
  include DataMapper::Resource

  property :id,       Serial
  property :uuid,     UUID
  property :title,    String
  property :text,     Text

  has n, :comments
end

Example Output

I want to be able to produce JSON web-service documents that look like this:

{
  "href":         "http://example.com/articles/1",
  "id":           "390072fc-c75e-4548-8431-937464a65c7c",
  "title":        "my-cool-post",
  "text":         "Loren Ipsum",
  "comments_href: "http://example.com/articles/1/comments"
}
{ 
  "href":   "http://example.com/articles",
  "item_count: 1,
  "items": [
    {
      "href":         "http://example.com/articles/1",
      "id":           "390072fc-c75e-4548-8431-937464a65c7c",
      "title":        "my-cool-post",
      "text":         "Loren Ipsum",
      "comments_href: "http://example.com/articles/1/comments"
    }
  ]
}

Important things to note about these documents:

  • They are highly customized. Obviously, the extensive customization of the attributes used in the JSON prevents me from doing this in the controller, and writing a special #to_json on the model is icky.
  • A single $items[*] element is identical to the entire document for that object.
  • The order, while techically insignificant, is extremely important for debugging. Its very useful to be able to always look in the same place in a document for an certain attribute.
  • These documents can be huge, containing 100s or 1000s of items. Speed is important.
  • The pretty-indenting and spacing of the document in Development mode. Additionally, these documents should be optimized json in production.

Idealized Templates

Here is how I'd ideally like to write the view templates for these documents:

/articles/show.json.rj

{
  :href           => absolute_uri(article),
  :id             => article.uuid,
  :title          => article.title,
  :text           => article.text,
  :comments_href  => absolute_uri(article.comments)
}

/articles/index.json.rj

{
  :href       => absolute_uri(:articles),
  :item_count => @articles.size
  :items => @articles.map { |article| partial(:article) }  
  # OR
  :items => partial(:article, :with => @articles)
}

Important stuff about this:

  • These are written ruby-hash style. Even more awesomeness would be using the 1.9-style: {href: absolute_uri(:articles), item_count: @articles.size}
  • The #partial needs to return an Array, not a string, to that the #to_json at the end works correctly.
  • The #partial needs to be able to find the non-underscored template. Its silly call the first one _article.json.rj then have show just be a single line of partial(:article).
    • I don't mind calling the first one article.json.rj instead, and just having the show action in the controller explicitly render the right template.

Additional Concerns

  • Because of the desire for fixed ordering and the new-style hash format, it may be desirable to write a custom parser, rather than eval'ing the document as a ruby hash. Yehuda mentioned the Johnson Ruby JS engine might be useful for this, so long as in the template, we don't have to wrap the attribute names in double-quotes.

  • It would be neat if the render method in #show controller actions would automatically look for #{controller_name.singularize}.template after looking for show.template and not finding it.

  • Have a way to add a partial template search path, so that something like this is possible:

    {
      :id => article.uuid,
      :author => partial(:person, :with => article.author), 
      ...
    }
    

    and I could set Articles controller with the additional template search path of /people to find the :person partial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment