Rails: ¿Cómo funciona el bloque respond_to?

211

Estoy revisando la guía Comenzando con Rails y me confundí con la sección 6.7. Después de generar un andamio, encuentro el siguiente bloque autogenerado en mi controlador:

def index
  @posts = Post.all

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

Me gustaría entender cómo funciona realmente el bloque respond_to. ¿Qué tipo de variable es el formato? ¿Son los métodos .html y .json del objeto de formato? La documentación para

ActionController::MimeResponds::ClassMethods::respond_to

no responde la pregunta

Col
fuente
Sería bueno si pudiera vincular a la documentación de ActionController :: MimeResponds :: ClassMethods :: respond_to pero api.rubyonrails.org no parece tener hipervínculos directos ...
Cole
respond_to toma el final de la llamada (por ejemplo, blah.html, blah.json, etc.) y coincide con la vista especificada. Otras respuestas pueden ser XML, CSV y muchas más dependiendo de la aplicación.
ScottJShea
55
¿Cómo "coincide con la vista especificada?"
Cole
No creo que la extensión (xml, html, etc.) se asigne a una vista. Si elige la representación predeterminada ( format.htmlsin argumento), usará convenciones (basadas en el verbo URL y HTTP) para elegir una vista (que se espera sea HTML). El respondedor (formato) se instruye aquí para representar las URL que terminan en .json serializando a json, en lugar de usar vistas y convenciones.
Craig Celeste

Respuestas:

189

Soy nuevo en Ruby y me quedé atrapado en este mismo código. Las partes en las que me colgué fueron un poco más fundamentales que algunas de las respuestas que encontré aquí. Esto puede o no ayudar a alguien.

  • respond_to es un método en la superclase ActionController .
  • toma un bloque, que es como un delegado. El bloque es de dohasta end, con|format| un argumento para el bloque.
  • respond_to ejecuta su bloque, pasando un respondedor al formatargumento.

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

  • ¡ ResponderNO contiene un método para .htmlo .json, pero de todos modos llamamos a estos métodos! Esta parte me lanzó a un bucle.
  • Ruby tiene una característica llamada method_missing. Si llama a un método que no existe (como jsono html), Ruby llama al method_missingmétodo en su lugar.

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

  • La Responderclase usa su method_missingcomo una especie de registro. Cuando llamamos 'json', le estamos diciendo que responda a las solicitudes con la extensión .json serializando a json. Necesitamos llamar htmlsin argumentos para decirle que maneje las solicitudes .html de la manera predeterminada (usando convenciones y vistas).

Podría escribirse así (usando un pseudocódigo similar a 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)

Esta parte me confundió muchísimo. Todavía lo encuentro poco intuitivo. Ruby parece usar esta técnica bastante. Toda la clase ( responder) se convierte en la implementación del método. Para aprovecharmethod_missing , necesitamos una instancia de la clase, por lo que estamos obligados a pasar una devolución de llamada a la que pasan el objeto tipo método. Para alguien que ha codificado en lenguajes tipo C durante 20 años, esto es muy atrasado y poco intuitivo para mí. ¡No es que sea malo! Pero es algo que mucha gente con ese tipo de experiencia necesita entender, y creo que podría ser lo que buscaba el OP.

ps nota que en RoR 4.2 respond_tose extrajo en gema respondedores .

Craig Celeste
fuente
Gracias Craig, ese enlace también tenía mucha información útil, no me di cuenta de cuánto es posible method_missing, ¡considerando que puedes pasarle argumentos y un bloque!
Aditya MP
2
¡La mejor respuesta para explicar el uso de method_missing () como mecanismo de registro en la clase Responder! También estaba muy confundido con este código.
Alan Evangelista el
1
Los generadores de andamios Rails 6 parecen producir código con respond_tolos controladores, sin la gema de respuesta presente en el Gemfile. ¿Quizás respond_toha cambiado la parte de ser extraído en la gema del respondedor?
Qasim
106

Este es un bloque de código Ruby que aprovecha el método auxiliar de Rails. Si aún no estás familiarizado con los bloques, los verás mucho en Ruby.

respond_toes un método auxiliar de Rails que se adjunta a la clase Controller (o mejor dicho, su superclase). Hace referencia a la respuesta que se enviará a la Vista (que se dirige al navegador).

El bloque en su ejemplo está formateando datos, pasando un parámetro de 'formato' en el bloque, para ser enviados desde el controlador a la vista cada vez que un navegador solicita datos html o json.

Si está en su máquina local y tiene configurado el andamio Post, puede ir a http://localhost:3000/postsy verá todas sus publicaciones en formato html. Pero, si escribe esto:, http://localhost:3000/posts.jsonverá todas sus publicaciones en un objeto json enviado desde el servidor.

Esto es muy útil para hacer aplicaciones pesadas de JavaScript que necesiten pasar json de un lado a otro desde el servidor. Si lo desea, puede crear fácilmente una API json en el back-end de sus rieles y solo pasar una vista, como la vista de índice de su controlador Post. Luego, podría usar una biblioteca de JavaScript como Jquery o Backbone (o ambas) para manipular datos y crear su propia interfaz. Estos se llaman IU asíncronas y se están volviendo muy populares (Gmail es uno). Son muy rápidos y le dan al usuario final una experiencia más similar a la de un escritorio en la web. Por supuesto, esta es solo una ventaja de formatear sus datos.

La forma de escribir Rails 3 sería:

    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

Al colocarse respond_to :html, :xml, :jsonen la parte superior de la clase, puede declarar todos los formatos que desea que su controlador envíe a sus vistas.

Luego, en el método del controlador, todo lo que tiene que hacer es responder_con (@whatever_object_you_have)

Simplemente simplifica su código un poco más de lo que Rails genera automáticamente.

Si quieres saber sobre el funcionamiento interno de esto ...

Por lo que entiendo, Rails introspectiva los objetos para determinar cuál será el formato real. El valor de las variables 'formato' se basa en esta introspección. Los rieles pueden hacer mucho con un poco de información. Te sorprendería hasta dónde llegará un simple @post o: post.

Por ejemplo, si tuviera un archivo parcial _user.html.erb con el siguiente aspecto:

_user.html.erb

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

Entonces, esto solo en mi vista de índice le permitiría a Rails saber que necesitaba encontrar los 'usuarios' parciales e iterar a través de todos los objetos de 'usuarios':

index.html.erb

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

le haría saber a Rails que necesitaba encontrar el 'usuario' parcial e iterar a través de todos los objetos 'usuarios':

Puede encontrar útil esta publicación de blog: http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

También puede leer detenidamente la fuente: https://github.com/rails/rails

PhillipKregg
fuente
1
Buen consejo sobre los rieles3 camino. Todavía estoy tratando de llegar al final del bloque respond_to, y cuál es el argumento del bloque | formato | se pasa.
Cole
44
Buena respuesta, pero no dice nada específico sobre la variable de formato que se pasa al bloque. En el ejemplo dado, hay format.html y format.json: ¿se pasan ambos a respond_to y luego respond_to decide qué hacer con ellos?
Anthony
cuando fue respond_toe respond_withintroducido? Estoy usando rails 2.3.5 y estoy obteniendoNoMethodError (undefined method respond_to)
abbood
10

Por lo que sé, respond_to es un método adjunto al ActionController, por lo que puede usarlo en cada controlador, porque todos heredan del ActionController. Aquí está el método Rails respond_to:

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

Lo estás pasando un bloque , como lo muestro aquí:

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

El | formato | parte es el argumento que el bloque está esperando, por lo que dentro del método respond_to podemos usar eso. ¿Cómo?

Bueno, si notas que pasamos el bloque con un prefijo & en el método respond_to, y lo hacemos para tratar ese bloque como un Proc. Como el argumento tiene el ".xml", ".html", podemos usarlo como métodos para llamar.

Lo que básicamente hacemos en la clase respond_to es llamar a los métodos ".html, .xml, .json" a una instancia de una clase Respondedor.

Nobita
fuente
1
La fuente de respond_to en los documentos de la API es diferente de la fuente que incluiste, y me estaba descartando. Su fragmento me deja más claro que el argumento del bloque de formato se pasa como un objeto Respondedor. La documentación del Respondedor parece responder a la pregunta, leyendo eso ahora.
Cole
7

Me gustaría entender cómo funciona realmente el bloque respond_to. ¿Qué tipo de variable es el formato? ¿Son los métodos .html y .json del objeto de formato?

Para entender qué formates, primero puede buscar la fuente respond_to, pero rápidamente encontrará que lo que realmente necesita es el código para retrieve_response_from_mimes .

A partir de aquí, verá que el bloque que se pasó respond_to(en su código), en realidad se llama y se pasa con una instancia de Collector (que dentro del bloque se hace referencia como format). El recopilador básicamente genera métodos (creo que en el inicio de Rails) en función de los tipos mime que conoce rails.

Entonces, sí, los métodos .htmly .jsonson definidos (en tiempo de ejecución) en la formatclase Collector (aka ).

rnicholson
fuente
2

La metaprogramación detrás del registro del respondedor (ver la respuesta de Parched Squid) también te permite hacer cosas ingeniosas como esta:

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

La línea csv hará que se llame a to_csv en cada publicación cuando visite /posts.csv. Esto facilita la exportación de datos como CSV (o cualquier otro formato) desde su sitio de rieles.

La línea js hará que se procese / ejecute un archivo javascript /posts.js (o /posts.js.coffee). He descubierto que es una forma ligera de crear un sitio habilitado para Ajax utilizando las ventanas emergentes de jQuery UI.

Catharz
fuente
1

¿Qué tipo de variable es el formato?

Desde un punto de vista de Java, el formato es una implementación de una interfaz anónima. Esta interfaz tiene un método nombrado para cada tipo mime. Cuando invocas uno de esos métodos (pasando un bloque), entonces si rails siente que el usuario quiere ese tipo de contenido, invocará tu bloque.

El giro, por supuesto, es que este objeto anónimo de pegamento en realidad no implementa una interfaz: captura las llamadas de método dinámicamente y determina si es el nombre de un tipo MIME que conoce.

Personalmente, creo que se ve raro: el bloque que pasas se ejecuta . Tendría más sentido para mí pasar un hash de etiquetas y bloques de formato. Pero, así es como se hace en RoR, parece.

PaulMurrayCbr
fuente
1

Esto está un poco desactualizado, por Ryan Bigg hace un gran trabajo explicando esto aquí:

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

De hecho, podría ser un poco más de detalle de lo que estaba buscando. Resulta que están sucediendo muchas cosas detrás de escena, incluida la necesidad de comprender cómo se cargan los tipos MIME.

idStar
fuente
0

"Formato" es su tipo de respuesta. Podría ser json o html, por ejemplo. Es el formato de la salida que recibirá su visitante.

Rafael Nascimento
fuente
0

Hay una cosa más que debes tener en cuenta: MIME.

Si necesita usar un tipo MIME y no es compatible de forma predeterminada, puede registrar sus propios controladores en config / initializers / mime_types.rb:

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


fuente