How to dry Rails 3 controllers by overriding methods like response_with?

I am trying to create a JSONP API for my Rails 3 application. Right now, my controllers have a lot of actions that follow this pattern:

# This is from my users_controller.rb, as an example def index @users = User.all respond_with(@users, :callback => params[:callback]) end 

So far it works as if I would like to DRY it without repeating :callback => params[:callback] in every call to the respond_with . How can i do this?

Update:. One thing I realized that is ugly in relation to my code above is that the option :callback => params[:callback] will be passed for any response format, not just JSON. Most likely, the following code:

 def index @users = User.all respond_with(@users) do |format| format.json { render :json => @users, :callback => params[:callback]} end end 

There are several ways to solve this problem, but I cannot figure out how to make them work:

  • Override render (possibly in the application controller) so that it accepts the parameter :jsonp , which automatically includes the parameter :callback => params[:callback] . Thus, I could change the above code to the following, which is slightly shorter:
 def index @users = User.all respond_with(@users) do |format| format.json { render :jsonp => @users} end end 
  • Create a responder that overrides to_json to solve my problem. That way I could leave the block and just call respond_with(@users, :responder => 'MyResponder') to solve the problem. Or perhaps I could include this code in the application responder using a paying responder stone so that respond_with(@users) enough.
+7
source share
5 answers

Thanks to samuelkadolph for helping me on the IRC # rubyonrails channel. He provided a solution to this meaning , copied below for convenience:

 def custom_respond_with(*resources, &block) options = resources.extract_options! if options[:callback] old_block = block block = lambda do |format| old_block.call(format) if block_given? format.json { render :json => [] } end end respond_with(*(resources << options), &block) end 

I have not tried this in my application yet, but I see that it should work. He also confirmed that I could similarly override the respond_with method itself by simply changing the name of this method and changing the last line of the definition to super(*(resources << options), &block) .

I think this will work for me. However, I'm still interested in learning how to write a custom responder to do this job. (It would be a more elegant solution, IMHO.)

Update: I tried this in my application and it works with some minor changes. Here is the version that I am currently using in the private section of my ApplicationController designed to automatically provide an option :callback => params[:callback] for JSON requests:

 def custom_respond_with(*resources, &block) options = resources.extract_options! if params[:callback] old_block = block block = lambda do |format| old_block.call(format) if block_given? format.json { render :json => resources, :callback => params[:callback] } end end respond_with(*(resources << options), &block) end 

Note that I had to change if options[:callback] to if params[:callback] to make it work.

+1
source

Note that it is technically incorrect to display JSON with a callback parameter, since you are receiving a JavaScript response (calling a JSON-P callback function), and not a JSON result. Therefore, if you have

 render :json => my_object, :callback => params[:callback] 

and the request will appear /users?callback=func , Rails will respond

 func({…}) 

with the content type application/json , which is incorrect, since the above answer is clearly not JSON, but JavaScript.

The solution I am using is

 def respond_with_json(item) respond_with do |format| format.json { render :json => item } format.js { render :json => item, :callback => params[:callback] } end end 

which answers correctly with or without a callback. Applying this to the above solution, we get:

 def custom_respond_with(*resources, &block) options = resources.extract_options! if params[:callback] old_block = block block = lambda do |format| old_block.call(format) if block_given? format.js { render :json => resources[0], :callback => params[:callback] } end end respond_with(*(resources << options), &block) end 

Also, pay attention to the correction for resources[0] , otherwise you will end up wrapping the resources in an additional array as a result of the splat operator.

+8
source

This is the gem that can do this: rack-jsonp-middleware .

The installation instructions are pretty meager on the site, but I created a small Rails project that uses it - that you can take a look at the commits and see what I did to run the middleware.

https://github.com/rwilcox/rack_jsonp_example

+3
source

This is a bit β€œlow tech” compared to the review solution, but once you create a private method in your appliation_controller.rb to handle it. The params parameter will be available to him, and you can pass the @users object to it.

 #application_controller.rb private def jsonp(my_object) render :json => my_object, :callback => params[:callback] end #controller def index @users = User.all respond_with(@users) do |format| format.json { jsonp(@users)} end end 
+1
source

You can also find out about it. basically you can create a "default" response_to for your controller so that you can just make all your default actions a json response.

was what you asked?

0
source

All Articles