Spec-Zone.ru › Ruby on Rails 5.1

модуль ActionController::MimeResponds

Публичные методы экземпляров

respond_to(*mimes) { |collector| ... } Показать исходный код
# File actionpack/lib/action_controller/metal/mime_responds.rb, line 191
def respond_to(*mimes)
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?

  collector = Collector.new(mimes, request.variant)
  yield collector if block_given?

  if format = collector.negotiate_format(request)
    _process_format(format)
    _set_rendered_content_type format
    response = collector.response
    response.call if response
  else
    raise ActionController::UnknownFormat
  end
end

Без поддержки веб-сервисов действие, собирающее данные для отображения списка людей, может выглядеть примерно так:

def index
  @people = Person.all
end

Это действие неявно отвечает на все форматы, но форматы также могут быть разрешены:

def index
  @people = Person.all
  respond_to :html, :js
end

Вот то же действие с интегрированной поддержкой веб-сервисов:

def index
  @people = Person.all

  respond_to do |format|
    format.html
    format.js
    format.xml { render xml: @people }
  end
end

Это означает: «если клиент хочет HTML или JS в ответ на это действие, ответьте так, как мы делали раньше, но если клиент хочет XML, верните ему список людей в формате XML». (Rails определяет желаемый формат ответа по заголовку HTTP Accept, отправленному клиентом.)

Предположим, у вас есть действие, добавляющее нового человека, при необходимости создавая его компанию (по имени), если она ещё не существует. Без веб-сервисов это может выглядеть так:

def create
  @company = Company.find_or_create_by(name: params[:company][:name])
  @person  = @company.people.create(params[:person])

  redirect_to(person_list_url)
end

Вот то же действие с интегрированной поддержкой веб-сервисов:

def create
  company  = params[:person].delete(:company)
  @company = Company.find_or_create_by(name: company[:name])
  @person  = @company.people.create(params[:person])

  respond_to do |format|
    format.html { redirect_to(person_list_url) }
    format.js
    format.xml  { render xml: @person.to_xml(include: @company) }
  end
end

Если клиент хочет HTML, мы просто перенаправляем его обратно на список людей. Если он хочет JavaScript, то это запрос Ajax, и мы отображаем JavaScript-шаблон, связанный с этим действием. Наконец, если клиент хочет XML, мы отображаем созданного человека как XML, но с одной особенностью: мы также включаем компанию человека в отображаемый XML, что-то вроде этого:

<person>
  <id>...</id>
  ...
  <company>
    <id>...</id>
    <name>...</name>
    ...
  </company>
</person>

Обратите внимание, однако, на дополнительную часть вверху этого действия:

company  = params[:person].delete(:company)
@company = Company.find_or_create_by(name: company[:name])

Это связано с тем, что входящий XML-документ (если обрабатывается запрос веб-сервиса) может содержать только один корневой узел. Поэтому нам нужно перегруппировать данные, чтобы запрос выглядел так (кодировка URL):

person[name]=...&person[company][name]=...&...

И так (кодировка XML):

<person>
  <name>...</name>
  <company>
    <name>...</name>
  </company>
</person>

Другими словами, мы делаем запрос так, чтобы он работал с данными одного человека. Затем в действии мы извлекаем данные о компании из запроса, находим или создаём компанию, а затем создаём нового человека с оставшимися данными.

Обратите внимание, что вы можете определить свой собственный парсер XML-параметров, который позволит описывать несколько сущностей в одном запросе (например, обернув их все в один корневой узел), но если вы просто последуете за умолчаниями Rails, жизнь будет намного проще.

Если вам нужно использовать тип MIME, который по умолчанию не поддерживается, вы можете зарегистрировать свои собственные обработчики в config/initializers/mime_types.rb следующим образом.

Mime::Type.register "image/jpg", :jpg

Метод respond_to также позволяет указать общий блок для разных форматов, используя any:

def index
  @people = Person.all

  respond_to do |format|
    format.html
    format.any(:xml, :json) { render request.format.to_sym => @people }
  end
end

В приведённом выше примере, если формат xml, он отобразит:

render xml: @people

Или, если формат json:

render json: @people

Форматы могут иметь разные варианты.

Вариант запроса — специализация формата запроса, например :tablet, :phone, или :desktop.

Часто нам нужно отображать разные шаблоны html/json/xml для телефонов, планшетов и настольных компьютеров. Варианты упрощают это.

Вы можете установить вариант в before_action:

request.variant = :tablet if request.user_agent =~ /iPad/

Обрабатывайте варианты в действии так же, как обрабатываете форматы:

respond_to do |format|
  format.html do |variant|
    variant.tablet # renders app/views/projects/show.html+tablet.erb
    variant.phone { extra_setup; render ... }
    variant.none  { special_setup } # executed only if there is no variant set
  end
end

Предоставьте отдельные шаблоны для каждого формата и варианта:

app/views/projects/show.html.erb
app/views/projects/show.html+tablet.erb
app/views/projects/show.html+phone.erb

Когда вы не используете общий код внутри формата, вы можете упростить определение вариантов, используя встроенную синтаксическую конструкцию:

respond_to do |format|
  format.js         { render "trash" }
  format.html.phone { redirect_to progress_path }
  format.html.none  { render "trash" }
end

Варианты также поддерживают общий any/all блок, который имеют форматы.

Это работает как для встроенного:

respond_to do |format|
  format.html.any   { render html: "any"   }
  format.html.phone { render html: "phone" }
end

так и для синтаксиса блока:

respond_to do |format|
  format.html do |variant|
    variant.any(:tablet, :phablet){ render html: "any" }
    variant.phone { render html: "phone" }
  end
end

Вы также можете установить массив вариантов:

request.variant = [:tablet, :phone]

Это будет работать аналогично согласованию форматов и типов MIME. Если вариант :tablet не объявлен, будет выбран вариант :phone:

respond_to do |format|
  format.html.none
  format.html.phone # this gets rendered
end

© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API