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

211

Я изучаю руководство по началу работы с 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

не отвечает на вопрос.

капуста
источник
Было бы неплохо, если бы я мог ссылаться на документацию для ActionController :: MimeResponds :: ClassMethods :: response_to, но api.rubyonrails.org, похоже, не любит прямые гиперссылки ...
Коул,
response_to принимает конец вызова (например, blah.html, blah.json и т. д.) и соответствует указанному представлению. Другие ответы могут быть XML, CSV и многие другие в зависимости от приложения.
ScottJShea
5
Как это "соответствует указанному виду?"
Коул
Я не думаю, что расширение (xml, html и т. Д.) Отображается в виде. Если вы выбираете рендеринг по умолчанию ( format.html- без аргументов), он будет использовать условные обозначения (основанные на URL и глаголе HTTP), чтобы выбрать представление (ожидается, что это HTML). Респондент (формат) здесь проинструктирован отображать URL-адреса, заканчивающиеся на .json, путем сериализации в json вместо использования представлений и соглашений.
Крейг Селеста

Ответы:

189

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

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

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

  • ResponderНе содержит метод .htmlили .json, но мы называем эти методы так или иначе! Эта часть бросила меня в петлю.
  • Рубин имеет функцию под названием 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)

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

PS обратите внимание, что в RoR 4.2 respond_toбыл извлечен в ответ респондентов драгоценный камень.

Крейг Селеста
источник
Спасибо, Крейг, эта ссылка на самом деле содержала массу полезной информации, я не осознавал, насколько это возможно method_missing, учитывая, что вы можете передать ей аргументы и блок!
Адитья депутат
2
Лучший ответ для объяснения использования метода method_missing () в качестве механизма регистрации в классе Responder! Я также был очень смущен с этим кодом.
Алан Евангелиста
1
Похоже, что генераторы скаффолдов Rails 6 производят код respond_toв контроллерах, без гема респондентов в Gemfile. Возможно, немного о том, respond_toчтобы быть извлеченным в жемчужину респондентов, было изменено?
Касим
106

Это блок кода 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 на своей стороне рельсов и передавать только одно представление - как представление индекса вашего контроллера Post. Затем вы можете использовать библиотеку javascript, такую ​​как Jquery или Backbone (или обе), чтобы манипулировать данными и создавать свой собственный интерфейс. Они называются асинхронными пользовательскими интерфейсами и становятся очень популярными (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 (@whwhat_object_you_have)

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

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

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

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

_user.html.erb

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

Затем, только это в моем представлении индекса позволит Rails знать, что ему нужно найти частичную часть «users» и выполнить итерацию по всем объектам «users»:

index.html.erb

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

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

Вы можете найти этот пост полезным: 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

PhillipKregg
источник
1
Хороший совет на пути rails3. Я все еще пытаюсь добраться до конца блока response_to, и что такое аргумент блока | format | проходит
Коул
4
Хороший ответ, но не говорится ничего конкретного о переменной формата, передаваемой в блок. В приведенном примере есть format.html и format.json - оба они передаются в response_to, а затем response_to решает, что с ними делать?
Энтони
когда был respond_toи respond_withвведен? Я использую рельсы 2.3.5 и получаюNoMethodError (undefined method respond_to)
abbood
10

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

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

Вы передаете ему блок , как я показываю здесь:

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

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

Что ж, если вы заметили, мы передаем блок с префиксом & в методе response_to, и мы делаем это для обработки этого блока как Proc. Поскольку аргумент имеет «.xml», «.html», мы можем использовать его как вызываемые методы.

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

Nobita
источник
1
Источник для response_to в api docs отличается от источника, который вы включили, и выбрасывал меня. Ваш фрагмент проясняет мне, что аргументу блока формата передается объект Responder. Документация Ответчика, кажется, отвечает на вопрос, читая это сейчас.
Коул
7

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

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

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

Так, да, .htmlи .jsonметоды , определенные (во время выполнения) на коллектор ( так называемый formatкласс).

rnicholson
источник
2

Метапрограммирование за регистрацией респондента (см. Ответ 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.

Catharz
источник
1

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

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

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

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

PaulMurrayCbr
источник
1

Это немного устарело, Райан Бигг делает большую работу, объясняя это здесь:

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

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

idStar
источник
0

«Формат» - это ваш тип ответа. Например, может быть json или html. Это формат вывода, который получит ваш посетитель.

Рафаэль Насименто
источник
0

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

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

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


источник