Rails: Как работает блок response_to?

Я просматриваю руководство Начало работы с Rails и запутался в разделе 6.7. После генерации скаффолда я обнаружил следующий автоматически сгенерированный блок в моем контроллере:

def index
  @posts = Post.all

  respond_to do |format|
    format.html  # index.html.erb
    format.json  { render :json => @posts }
  end
end

Я хотел бы понять, как на самом деле работает блок response_to. Какой тип переменной является форматом? Являются ли методы .html и .json объекта формата? документация для ActionController::MimeResponds::ClassMethods::respond_to не отвечает на вопрос.

Ответ 1

Я новичок в Ruby и застрял в этом же коде. Части, которые я повесил, были немного более фундаментальными, чем некоторые из ответов, которые я нашел здесь. Это может или не поможет кому-то.

  • respond_to - метод суперкласса ActionController.
  • требуется блок, который является делегатом. Блок находится от do до end, а |format| - как аргумент блока.
  • response_to выполняет ваш блок, передавая ответчик в аргумент format.

http://api.rubyonrails.org/v4.1/classes/ActionController/Responder.html

  • Responder НЕ содержит метод для .html или .json, но мы так или иначе называем эти методы! Эта часть бросила меня на цикл.
  • Ruby имеет функцию method_missing. Если вы вызываете метод, который не существует (например, json или html), Ruby вызывает вместо этого метод method_missing.

http://ruby-metaprogramming.rubylearning.com/html/ruby_metaprogramming_2.html

  • Класс Responder использует method_missing как своего рода регистрацию. Когда мы называем "json", мы говорим ему, чтобы он отвечал на запросы с расширением .json, сериализовывая json. Нам нужно вызвать html без аргументов, чтобы сообщить ему обрабатывать запросы .html по умолчанию (используя соглашения и представления).

Его можно было бы написать так (с использованием JS-подобного псевдокода):

// get an instance to a responder from the base class
var format = get_responder()

// register html to render in the default way
// (by way of the views and conventions)
format.register('html')

// register json as well. the argument to .json is the second
// argument to method_missing ('json' is the first), which contains
// optional ways to configure the response. In this case, serialize as json.
format.register('json', renderOptions)

Эта часть сбила меня с толку. Я все еще считаю это неинтуитивным. Кажется, Ruby использует эту технику совсем немного. Весь класс (Responder) становится реализацией метода. Чтобы использовать method_missing, нам нужен экземпляр класса, поэтому мы обязаны передать обратный вызов, в который они передают объект, подобный методу. Для кого-то, кто закодирован на C-подобных языках в течение 20 лет, это очень обратное и неинтуитивное для меня. Не то, чтобы это было плохо! Но это что-то такое, что многие люди с таким фоном нуждаются в том, чтобы опустить голову, и я думаю, что это может быть то, что было после OP.

p.s. обратите внимание, что в RoR 4.2 respond_to был извлечен responders gem.

Ответ 2

Это блок кода Ruby, который использует вспомогательный метод Rails. Если вы еще не знакомы с блоками, вы увидите их много в Ruby.

respond_to - это вспомогательный метод Rails, который привязан к классу Controller (или, вернее, к его суперклассу). Он ссылается на ответ, который будет отправлен в представление (которое будет в браузере).

Блок в вашем примере - это форматирование данных - путем передачи в блоке параметра "format" в блоке - для отправки от контроллера к просмотру всякий раз, когда браузер делает запрос на данные html или json.

Если вы находитесь на своем локальном компьютере, и у вас установлен свой эстакада Post, вы можете перейти на http://localhost:3000/posts, и вы увидите все свои сообщения в формате html. Но, если вы наберете это: http://localhost:3000/posts.json, вы увидите все свои сообщения в объекте json, отправленном с сервера.

Это очень удобно для создания тяжелых приложений javascript, которые должны передавать json туда и обратно с сервера. Если бы вы захотели, вы могли бы легко создать json api на своих рельсах back-end и пройти только один вид - например, индексный указатель вашего Post-контроллера. Затем вы можете использовать библиотеку javascript, например Jquery или Backbone (или оба) для управления данными и создания собственного интерфейса. Они называются асинхронными UI, и они становятся популярными (Gmail - один). Они очень быстрые и дают конечному пользователю больше возможностей для работы на рабочем столе в Интернете. Конечно, это всего лишь одно преимущество форматирования ваших данных.

В Rails 3 это можно записать так:

    class PostsController < ApplicationController
      # GET /posts
      # GET /posts.xml


      respond_to :html, :xml, :json

      def index
        @posts = Post.all

        respond_with(@posts)
      end

#
# All your other REST methods
#

end

Поместив respond_to :html, :xml, :json в начало класса, вы можете объявить все форматы, которые вы хотите, чтобы ваш контроллер отправлял ваши представления.

Затем в методе контроллера все, что вам нужно сделать, это response_with (@whatever_object_you_have)

Это просто упрощает ваш код немного больше, чем автоматически генерирует Rails.

Если вы хотите узнать о внутренней работе этого...

Из того, что я понимаю, Rails инспектирует объекты, чтобы определить, каков будет фактический формат. Значение переменных "format" основано на этой интроспекции. Rails может сделать много с небольшим количеством информации. Вы будете удивлены тем, насколько удастся просто @post или: post.

Например, если у меня был частичный файл _user.html.erb, который выглядел так:

_user.html.erb

<li>    
    <%= link_to user.name, user %>
</li>

Тогда только это в моем индексном представлении позволит Rails знать, что ему нужно найти "частичную" пользователя и выполнить итерацию всех объектов "пользователей":

index.html.erb

 <ul class="users">
   <%= render @users %>     
 </ul>

позволит Rails знать, что ему нужно найти "пользовательский" частичный и повторить все объекты "пользователей":

Вы можете найти это сообщение в блоге полезным: http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

Вы также можете просмотреть источник: https://github.com/rails/rails

Ответ 3

Из того, что я знаю, response_to - это метод, прикрепленный к ActionController, поэтому вы можете использовать его в каждом отдельном контроллере, потому что все они наследуются от ActionController. Вот метод Rails response_to:

def respond_to(&block)
  responder = Responder.new(self)
  block.call(responder)
  responder.respond
end

Вы передаете его block, как показано здесь:

respond_to <<**BEGINNING OF THE BLOCK**>> do |format|
  format.html
  format.xml  { render :xml => @whatever }
end <<**END OF THE BLOCK**>>

Часть | format | - это аргумент, ожидаемый блоком, поэтому внутри метода response_to мы можем это использовать. Как?

Хорошо, если вы заметили, что мы передаем блок с префиксом и в методе response_to, и мы делаем это для обработки этого блока как Proc. Поскольку аргумент имеет ".xml", ".html", мы можем использовать его как методы для вызова.

Что мы в основном делаем в классе response_to, являются методы вызова ".html,.xml,.json" экземпляру класса ответчика.

Ответ 4

Я хотел бы понять, как работает блок response_to. Какие тип переменной - формат? Используются методы .html и .json объект?

Чтобы понять, что format, вы можете сначала посмотреть на источник для respond_to, но быстро вы обнаружите, что на самом деле вам нужно посмотреть код для retrieve_response_from_mimes.

Здесь вы увидите, что блок, который был передан в respond_to (в вашем коде), фактически вызывается и передается с экземпляром Collector (который в пределах блока ссылается как format). Коллекционер в основном генерирует методы (я верю в запуск Rails), основываясь на том, что mime-типы, о которых знает рельсы.

Итак, да, .html и .json - это методы, определенные (во время выполнения) в классе Collector (aka format).

Ответ 5

Мета-программирование за регистрацией ответчика (см. ответ Parched Squid) также позволяет вам делать такие отличные вещи, как это:

def index
  @posts = Post.all

  respond_to do |format|
    format.html  # index.html.erb
    format.json  { render :json => @posts }
    format.csv   { render :csv => @posts }
    format.js
  end
end

Строка csv вызовет to_csv для вызова в каждом сообщении при посещении /posts.csv. Это упрощает экспорт данных из CSV (или любого другого формата) из вашего сайта rails.

Строка js приведет к отображению/выполнению файла javascript/posts.js(или/posts.js.coffee). Я обнаружил, что это легкий способ создать сайт с поддержкой Ajax с использованием всплывающих окон jQuery.

Ответ 6

Это немного устарело, Райан Бигг отлично справляется с этим:

http://ryanbigg.com/2009/04/how-rails-works-2-mime-types-respond_to/

Фактически, это может быть немного более подробно, чем вы искали. Как оказалось, там происходит много за кулисами, в том числе необходимость понять, как загружаются типы MIME.

Ответ 7

Какой тип переменной является форматом?

Из java POV формат - это реализация анонимного интерфейса. Этот интерфейс имеет один метод, названный для каждого типа mime. Когда вы вызываете один из этих методов (передавая ему блок), тогда, если рельсы чувствуют, что пользователь хочет этот тип контента, он будет вызывать ваш блок.

Твист, конечно же, заключается в том, что этот анонимный объект клей фактически не реализует интерфейс - он ловит динамический вызов метода и работает, если его имя типа mime, о котором он знает.

Лично я считаю, что это выглядит странно: блок, который вы проходите, выполняется. Мне было бы разумнее передать хэш меток и блоков формата. Но - как это делается в RoR, кажется.

Ответ 8

Есть еще одна вещь, о которой вы должны знать - MIME.

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

Mime::Type.register "text/markdown", :markdown

Ответ 9

"Формат" - это ваш тип ответа. Например, может быть json или html. Это будет формат получения вашего посетителя.