开发者

JSON Templating

As a bit of background, I'm in the early design phases of a large RoR project for the company I work for and we're planning on sticking with Rails for the next 5-10 years. We're at this point in the project we're trying to get some good practices and customize the framework to work for what we're going to be needing need. We've considered the pros/cons of going with a full blown web-app (Backbone.js style) vs. a static limited JS based Rails app. While we would have loved to go with a webapp, I've determined that going with Backbone.js would cause:

  • Complete duplication of views and routes (a maintenance issue in the long run)
  • Difficulty tracking polymorphic associations and their routes
  • Wouldn't allow us to use the view helpers on the client side (maintenance issue)
  • Duplication of some of the model information
  • Slow client-end computers (beyond our control), which would have difficulty running it

To try and reduce this, we're decided to go with a more hybrid approach but to push more of the logic back to the server side while still maintaining a RESTFUL architecture as an API as best as possible. We're planning on having the initial page loads done normally using Rails, and then any manipulations on the page send the request via AJAX and receiving back a JSON object which contains the rendered partial, and some additional information (ie. flash updates, JSON representation of the object, additional JS includes if necessary, etc.)

The problem we're running into, is that from my understanding, we cannot maintain the RESTFUL architecture and have multiple partials/views for that same data. As an example of this, if I wanted to request a list of Orders from the server via orders/index, it would return only one view of the data. If I had an extended table, a list, or some other view/partial/widget which I wanted th开发者_如何学Ce requested partial to be used for.

To that end, I've added a params[:partial] (which is either an add-on on the route, or a url parameter) that provides the server both with the information that is being requested, as well as the view which I am expecting it to come in. This would work well on it's own, however I'd also like to have any additional information sent down from the server at the same time (ie. flash updates, JSON representation of the data, etc.). Ideally we want this to be flexible, fast, and sent down from the server in JSON format.

To that end, I've overriden the existing JSON responder as follows, and you may notice the hack I've had to use to have it render the HAML/ERB templates inside of the JSON:

module ExtraFunctions
 def partial_options
   params.include?(:partial) ? { :partial => params[:partial] } : {}
 end

 def flash_options
   { :file => 'layouts/_flash.html.haml' }
 end
end

ApplicationController.send :include, ExtraFunctions

#I would love to be using the ERB templating and views
#instead of this kludge.  But this will have to do.

ActionController::Renderers.add :json do |json, options|

 #Change the format so we can render the html templates
 self.formats = [:html]
 options = { :layout => false }

 partial_opt = options.merge(self.respond_to?(:partial_options) ? self.partial_options : {})
 flash_opt   = options.merge(self.respond_to?(:flash_options) ? self.flash_options : {})

 obj = {
   json: json.as_json,
   partial: render_to_string(partial_opt),
   flash: flash,
   flash_partial: render_to_string(flash_opt),
   user: @current_user
   #js-includes: #Working on this
 }


 #Change the format back
 self.formats = [:json]

 json = obj.to_json(options) unless obj.kind_of?(String)
 json = "#{options[:callback]}(#{json})" unless options[:callback].blank?

 self.content_type ||= Mime::JSON
 json
end

You'll notice there is a spot in there for js-includes, the reason for that is that I want the ability to have dynamic includes late in the game using head.js. This would be for easier dependency management, as the initial view would not be including some of the partials that are requested via AJAX (ie. if I grabbed a form to enter a new address, and that form has some AJAX checking on it as included in the top of it using a content_for tag). Ideally, what I'd really like this to be is a JSON layout to look something like (though with a more sugar coated syntax I would hope):

** json/application.json.erb **
{
   json: <%= json.as_json %>,
   partial: <%= render_to_string(partial_opt) %>,
   flash: <%= flash %>,
   flash_partial: <%= render_to_string(flash_opt) %>,
   user: <%= @current_user =>,
   js-includes: <%= yield :js_includes %>,
   <%= yield =>
}

I've been working on these considerations/issues for a while, and my questions are two:

1) Is there anything blatantly screamingly stupid about what we're doing? Is there a better or more standard solution?

2) Is there a way to have ERB render the templates for JSON?

Thanks!


Bottom line, this is a common problem that there isn't yet a strong opinion on solving.

David Heinemeier Hansson talked about this problem a bit at RailsConf 2011 when he introduced the new Asset Pipeline in Rails 3.1, noting that they've started using what they call pjax to handle refreshing parts of a page and suggested it'd be part of Rails in time.

37signals has also built Cinco (all-js mobile-app framework) and was planning on open sourcing it, but I believe they've started growing away from some of the ideas they were building it with, so I don't know what the status is.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜