nesquena / rabl

General ruby templating with json, bson, xml, plist and msgpack support
http://blog.codepath.com/2011/06/27/building-a-platform-api-on-rails/
MIT License
3.65k stars 335 forks source link

Forcing a specific :format using Rabl::Renderer.json w/ :scope option set. #315

Open veloper opened 12 years ago

veloper commented 12 years ago

I was wondering if there was a way to force the underlying renderer to use a specific format when using Rabl::Renderer when the :scope option is specified.

My views are currently named like so xxxxx.json.rabl and as such need to have a specific :format of json

My views also make use of a helper method defined in the application controller called include? which reads params[:_include] and figures out if a relationship should be included as child node.

Scenario

URL

/api/v1/clients.csv?_include=2

Controller

class ApplicationController < ActionController::Base

  helper_method :include?
  before_filter :catch_csv

  def catch_csv
    return unless params[:format] == 'csv'
    json = Rabl::Renderer.json(@collection, "#{params[:controller]}/index", :view_path => 'app/views', :scope => self)
    ... parse and turn into CSV 
  end

  def include?
    # logic to determine include depth
  end

end

Problem

With this example I get the following exception...

ActionView::MissingTemplate (Missing partial api/v1/clients/show with {:locale=>[:en], :formats=>[nil], :handlers=>[:erb, :builder, :rabl]}

You'll notice this part :formats=>[nil] -- as "csv" is not a valid format type in (https://github.com/nesquena/rabl/blob/master/lib/rabl/engine.rb#L6)

Looking at the Engine class it appears to set the format here: https://github.com/nesquena/rabl/blob/master/lib/rabl/engine.rb#L28 ...which calls this method here: https://github.com/nesquena/rabl/blob/master/lib/rabl/engine.rb#L211 seemingly checking the scope i passed in.

The problem is that I need the include? helper method to be available to the RABL views, but I don't want the controllers :format to be used in a manual rendering call.

The only solution I've found seems very ugly and wrong to me... but it works...

Ugly Solution

class RablControllerWrapper
  attr_reader :controller
  def initialize(controller)
    @controller = controller
  end

  def include?
    @controller.include?
  end
end

json = Rabl::Renderer.json(@collection, "#{params[:controller]}/index", 
  :view_path => 'app/views', 
  :scope => RablControllerWrapper.new(self)
)   

Pointers, thoughts, opinions?

Thanks :)

nesquena commented 12 years ago

I see, it looks like we might need to tweak rabl to adhere to an explicitly specified format more closely? Since you specified:

Rabl::Renderer.json(@collection, "#{params[:controller]}/index", :view_path => 'app/views', :scope => self)

it follows that it should try and render the format as JSON rather than CSV. I will have to dig in more later to see if this can be fixed easily. Thanks for raising the issue.

veloper commented 12 years ago

Thanks for the quick reply. For now I'll continue on with a wrapper class.

nesquena commented 12 years ago

Yeah that was a clever (albeit unfortunate) workaround. Hopefully we can fix this soon.

veloper commented 12 years ago

Out of curiosity, and if you know off the top of your head, what method/attributes is my wrapper class blocking the library from accessing where it determines the format?

databyte commented 12 years ago

If you want to make a failing test for it, I can tweak it to work because by the looks of it, I would've guessed you can pass in the format and it won't use the request_format method.

Rabl.render(object, template, :view_path => 'app/views', :format => :csv)
mateusmaso commented 12 years ago

Getting the same problem here :/

databyte commented 11 years ago

Anyone want to make a failing test or sample app for me to fix?

sockmonk commented 11 years ago

I'm seeing a very similar issue. It looks like Rabl::Engine#request_format is relying on request_params / context_scope.params to choose the format, rather than @_options[:format] that stores the format the user passed in. Then in Rabl::Partials#fetch_rails_source it uses that for the source_format, with the result that rabl partials aren't found when I try to use them via gon.

I think that Rabl::Engine#request_format should take @_options[:format] into consideration if it's been set. Hope this gets someone closer to fixing this.

nesquena commented 11 years ago

Thanks for tracking that down, hopefully one of us can take a look soon. In the mean time, any pull requests greatly appreciated :)