简体   繁体   中英

Rails: respond_to vs “case” conditional

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:

  1. Is part of the standard Rails Way™

  2. Takes care of rendering the correct file

  3. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM