I have a controller that responds_to html and png ( I load the image dynamically and render it as text ). That makes the controller code messy and today I found respond_with, which looks very cool, but I can't found out how to make it work with formats, different than html, json and xml ( like the png )
I expected that this would work, but it stills tries to find a template file and ignores my method :(
models/user.rb
class User < ActiveRecord::Base
def to_png
File.read("some_file.png")
end
end
controllers/users_controller.rb
class UsersController < ApplicationController
respond_to :html, :png
# GET /users/1
def show
#user = User.find(params[:id])
respond_with(#user)
end
end
Try to add in file [YOUR_APP]/config/initializers/mime_types.rb:
Mime::Type.register "image/png", :png
and restart your application
if you need to use a MIME type which isn’t supported by default, you
can register your own handlers in environment.rb as follows.
Mime::Type.register "image/jpg", :jpg
http://apidock.com/rails/ActionController/MimeResponds/InstanceMethods/respond_to
in environment.rb
Mime::Type.register "image/png", :png
then
respond_to do |format|
format.png do
#do stuff here
end
end
or
respond_with #user do |format|
format.png do
#do stuff here
end
end
Related
My objective is generate every month a PDF.
I am using Prawn Gem.
Likes this:
../Generatepdf/July2015.pdf
../Generatepdf/August2015.pdf
../Generatepdf/Setember2015.pdf
etc...
So my problem is how to define the urls?
I got a controller "Generatepdf" with a view "index.html"
I only can access of my PDF via:
../generatepdf/index.pdf
Controller:
class GeneratepdfController < ApplicationController
def index
respond_to do |format|
format.html
format.pdf do
pdf = ConsumptionsPdf.new
send_data pdf.render, filename: "lol2.pdf",
type: "application/pdf",
disposition: "inline"
end
end
end
end
Class:
class ConsumptionsPdf < Prawn::Document
def initialize
super
text "example"
end
end
I want to respond with json to all formats.
I can force the render format to json so the action will render show.json despite the accept header:
def show
render formats: :json
end
How I can set render format for all actions of the controller?
Something like this:
class GalleriesController < ApplicationController
formats :json
end
As a summary of all comments to the questions and readability for future users, you can do, as mentioned here:
before_filter :default_format_json
def default_format_json
request.format = "json"
end
In your controller:
def my_action
formats.clear
formats << :json
end
(I've only tested this in Rails 4.2 and 3.2.)
formats returns an Array of format symbols. It is delegated to #_lookup_context, which is an instance of ActionView::LookupContext.
Overwrite the response content type. Read more about the response object here: http://guides.rubyonrails.org/action_controller_overview.html#the-response-object
before_filter :force_json
def force_json
response.content_type = Mime[:json]
end
With respond_to:
def action
respond_to do |format|
format.any(:html, :js, :json) { render json: #object.to_json }
end
end
Environment
Rails 3.2.6
Ruby 1.9.3p194
What I found
class ThemesController < ApplicationController
def show
end
end
This setting will always render the /views/themes/show.html.erb page no matter what URL extension is. For example:
http://localhost/themes/1.json
http://localhost/themes/1.xxx
http://localhost/themes/1.custom_ext
...
Encounter
I want to run render :json=>#theme when extension is json, otherwise, render the show.html.erb page, so I changed my code:
respond_to do |format|
format.json { render :json => #theme}
format.any {render}
end
It will correctly run render :json=>#theme when URL extension is .json, and render show.html.erb in .xml, .html, etc.
However, I got 406 Not Acceptable in .xxx, .ooo, .custom_ext, and I found it is because only supported MIME type were allowed.
Temporary solution
class ThemesController < ApplicationController
def show
if params[:format].present? && params[:format] == "json"
render :json => #theme
end
end
end
It works fine, and when serving more then 2 formats, such as .xml, .json, .yaml, etc:
class ThemesController < ApplicationController
def show
case params[:format]
when "json" then render :json => #theme
when "xml" then render :xml => #theme
...
else render
end
end
end
It looks clean and not worse than respond_to style :D
Question
I am wondering if there is another better solution?
If case statement can do every thing respond_to can do while respond_to can't, why should I use respond_to?
Quite useful stuff, as it is usually happens, can be found in API docs.
Pay attention to the note there: Note that we used Mime::CSV for the csv mime type as it comes with Rails. For a custom renderer, you’ll need to register a mime type with Mime::Type.register.
You have to put this stuff in config/initializers/mime_types.rb. You'll find there few examples of registering non-default types.
Predefined types are here.
I was wondering is there any way to define default respond_to for index & show actions in Application Controller with an ability to override in other controllers that need some customization.
I think it's going to be easier with an example.
I'm using InheritedResources, CanCan/Authlogic and WickedPDF gems to generate my pdf and authorize users. I was wondering if I can dry up my code.
Here is what I have
class ProductsController < InheritedResources::Base
load_and_authorize_resource
respond_to :html, :xml, :json, :pdf
def index
#products = Product.page(params[:page])
index! do |format|
format.pdf do
render :pdf => pdf_file_name,
:show_as_html => params[:debug].present?
end
end
end
def show
show! do |format|
format.pdf do
render :pdf => pdf_file_name,
:show_as_html => params[:debug].present?
end
end
end
end
class CustomersController < InheritedResources::Base
def index
index! do |format|
format.pdf do
render :pdf => pdf_file_name,
:show_as_html => params[:debug].present?
end
end
end
def show
show! do |format|
format.pdf do
render :pdf => pdf_file_name,
:show_as_html => params[:debug].present?
end
end
end
end
This works just fine. But it seems redundant that I need to define format.pdf in every controller that I'm want to generate pdf for. Is there any way to move this to application controller or specify somewhere using inherited resources, and then just override this on a per controller basis? Any ideas?
Thank you
Ok I came up with the following solution for anyone else interested.
I figured I can add a controller that will inherit from InheritedResources, which inherits from ApplicationController, and then have all my other controllers inherit from it ( except for a couple of special cases that will inherit directly from application controller ( like the HomeController, that doesn't have any actions other than index, and is not tied to any particular model) - this way I can define certain defaults - that I keep using in all my controllers such as respond_to, and still enjoy the benefits of InheritedResources gem.
class DefaultInheritedResourcesController < InheritedResources::Base
# For CanCan authorization - pretty much makes it application wide, without the need
# to define it in each controller. Can still override it in ability.rb by making
# a resource readable to all users with a session.
# if user
# can :read, [Product]
# end
# Also for controllers that need special treatment, you can just inherit from ApplicationController
# and override with skip_authorization_check - but for my app it's rare (only HomeController),
# most of controllers deal with some kind of resource - so this is a useful convention for 99% of use cases.
load_and_authorize_resource
respond_to :html, :json, :xml, :pdf
# format pdf needs to be redefined on those actions where index! or show! are called.
def index
super do |format|
format.pdf do
render :pdf => pdf_file_name,
:show_as_html => params[:debug].present?
end
end
end
def show
super do |format|
format.pdf do
render :pdf => pdf_file_name,
:show_as_html => params[:debug].present?
end
end
end
end
Then in my ProductController I can do this ( notice where my ProductController is inheriting from.
class ProductsController < DefaultInheritedResourcesController
def index
#products = Product.page(params[:page])
super
end
end
Hope this is going to help someone.
What is the advantage of using respond_to in Rails instead of a case statement?
I have several instance variables that I want to set same way for some formats, but not for HTML. This does not seem to work:
respond_to do |format|
format.html do
# ...
end
format.any(:csv, :xml) do
# common stuff
end
format.csv do
# ...
end
format.xml do
# ...
end
end
I think I'll end up using a couple of case request.format and not using respond_to at all:
case request.format
when 'html'
# ...
when 'csv', 'xml'
# common stuff
end
# more common stuff
case request.format
when 'html'
# render
when 'csv'
# custom render csv
when 'xml'
# render xml with a template
end
So I wonder what is a good use case for respond_to, where case request.format wouldn't look better?
respond_to is not just a way to find out what type of response the client expected but also a way to tell rails what type of response you are willing to provide.
For example a simple scenario where we have this controller:
class SimpleController < ApplicationController
def index
respond_to :html, :json
end
end
A client sends a request expecting xml response
curl -H "Accept: application/xml" \
-H "Content-Type: application/xml" \
-X GET "host:port/simple/index"
Rails will response with 406
Completed 406 Not Acceptable in 0ms (ActiveRecord: 0.0ms)
However, if you simply filter request.format using case like in your example the client will receive a 500 error because rails cannot find a corresponding template to the request format.
Of course you can also call respond_to on class level as well as specify respond format in routes.rb
Dive into rails source code and api documentation if you want to get more in-depth explanation to this.
First of all, the reason the respond_to block does not work is because you are using format.any(:csv, :xml) in conjunction with format.csv. You can only use one of these.
This should be a clear indication that you are trying to do too much in your controller. For example, if you are doing common stuff for csv and xml responses, then maybe you should create a class in your lib directory:
# lib/foo_serializer.rb
class FooSerializer
def initialize(bar)
#bar = bar
end
def do_stuff
#bar.to_s
end
end
And then call one of its method for each type of response:
respond_to do |format|
format.html do
# ...
end
format.csv do
FooSerializer.new(data).do_stuff
# ...
end
format.xml do
FooSerializer.new(data).do_stuff
# ...
end
end
The respond_to syntax:
Is part of the standard Rails Way™
Takes care of rendering the correct file
Not only looks better but is also more maintainable
You should try to stick with it.
The request.format field can be useful where the respond_to block is not appropriate.
For example, I'm using the sunspot gem for searching, and my controller looks like this:
def index
case request.format
when 'xlsx'
per_page: Person.count
when 'html'
per_page: 30
end
#search = Person.search do
with :name, params[:name]
fulltext params[:search_term]
paginate page: params[:page], per_page: per_page
end
#people = #search.results
respond_to do |format|
format.xlsx {send_file #people.to_xlsx, filename: 'people.xlsx'}
format.html
end
end
I've been working with Rails for a while, and I've always seen respond_to in this form:
respond_to do |format|
format.js
format.html # index.html.erb
format.xml { render :xml => #posts }
end
I don't know where you would've seen something with the nested do's.
EDIT:
For pagination, see
https://github.com/mislav/will_paginate
EDIT 2:
format.html { render :html => Post.paginate(:page => params[:page]) }
or something like that.