Rails: comment fonctionne le bloc respond_to?

211

Je suis en train de parcourir le guide Mise en route avec Rails et je me suis confondu avec la section 6.7. Après avoir généré un échafaudage, je trouve le bloc généré automatiquement suivant dans mon contrôleur:

def index
  @posts = Post.all

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

Je voudrais comprendre comment fonctionne réellement le bloc respond_to. Quel type de variable est format? Les méthodes .html et .json sont-elles de l'objet format? La documentation pour

ActionController::MimeResponds::ClassMethods::respond_to

ne répond pas à la question.

Cole
la source
Ce serait bien si je pouvais créer un lien vers la documentation d'ActionController :: MimeResponds :: ClassMethods :: respond_to mais api.rubyonrails.org ne semble pas aimer les hyperliens directs ...
Cole
respond_to prend la fin de l'appel (par exemple blah.html, blah.json, etc.) et correspond à la vue spécifiée. D'autres réponses peuvent être XML, CSV et bien d'autres selon l'application.
ScottJShea
5
Comment "correspond-il à la vue spécifiée?"
Cole
Je ne pense pas que l'extension (xml, html, etc.) correspond à une vue. Si vous choisissez le rendu par défaut ( format.html- pas d'argument), il utilisera des conventions (basées sur l'URL et le verbe HTTP) pour choisir une vue (qui devrait être HTML). Le répondeur (format) est chargé ici de rendre les URL se terminant par .json en sérialisant en json, au lieu d'utiliser des vues et des conventions.
Craig Celeste

Réponses:

189

Je suis nouveau sur Ruby et je suis resté bloqué sur ce même code. Les parties sur lesquelles j'ai raccroché étaient un peu plus fondamentales que certaines des réponses que j'ai trouvées ici. Cela peut ou non aider quelqu'un.

  • respond_to est une méthode sur la superclasse ActionController .
  • il faut un bloc, qui est comme un délégué. Le bloc est de dojusqu'à end, avec |format|comme argument pour le bloc.
  • respond_to exécute votre bloc, en passant un Répondeur dans l' formatargument.

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

  • Le Responderne contient PAS de méthode pour .htmlou.json , mais nous appelons ces méthodes quand même! Cette partie m'a jeté pour une boucle.
  • Ruby a une fonctionnalité appelée method_missing. Si vous appelez une méthode qui n'existe pas (comme jsonou html), Ruby appelle la method_missingméthode à la place.

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

  • La Responderclasse utilise son method_missingcomme une sorte d'enregistrement. Lorsque nous appelons «json», nous lui disons de répondre aux demandes avec l'extension .json en sérialisant vers json. Nous devons appeler htmlsans argument pour lui dire de gérer les requêtes .html par défaut (en utilisant les conventions et les vues).

Il pourrait être écrit comme ceci (en utilisant un pseudocode de type 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)

Cette partie a confondu le diable hors de moi. Je trouve toujours cela peu intuitif. Ruby semble utiliser un peu cette technique. La classe entière ( responder) devient l'implémentation de la méthode. Afin de tirer partimethod_missing , nous avons besoin d'une instance de la classe, nous sommes donc obligés de passer un rappel dans lequel ils passent l'objet de type méthode. Pour quelqu'un qui a codé en langage C depuis 20 ans, c'est très arriéré et peu intuitif pour moi. Pas que ce soit mauvais! Mais c'est quelque chose que beaucoup de gens avec ce genre d'expérience ont besoin de trouver la tête, et je pense que c'est peut-être ce que le PO recherchait.

ps noter que dans RoR 4.2 a respond_toété extrait dans la gemme répondeurs .

Craig Celeste
la source
Merci Craig, ce lien contenait également une tonne d'informations utiles, je ne savais pas à quel point il était possible de le faire method_missing, étant donné que vous pouvez lui passer des arguments et un bloc!
Aditya MP du
2
Meilleure réponse pour expliquer l'utilisation de method_missing () comme mécanisme d'enregistrement dans la classe Répondeur! J'étais également très confus avec ce code.
Alan Evangelista
1
Les générateurs d'échafaudage Rails 6 semblent produire du code avec respond_toles contrôleurs, sans la gemme des répondeurs présente dans le Gemfile. Peut-être que le fait d' respond_toêtre extrait dans la gemme du répondant a été changé?
Qasim
106

Il s'agit d'un bloc de code Ruby qui tire parti d'une méthode d'assistance Rails. Si vous n'êtes pas encore familier avec les blocs, vous les verrez beaucoup dans Ruby.

respond_toest une méthode d'assistance Rails attachée à la classe Controller (ou plutôt à sa super classe). Il fait référence à la réponse qui sera envoyée à la vue (qui va au navigateur).

Le bloc dans votre exemple est le formatage des données - en passant un paramètre "format" dans le bloc - à envoyer du contrôleur à la vue chaque fois qu'un navigateur fait une demande de données html ou json.

Si vous êtes sur votre ordinateur local et que votre échafaudage Post est configuré, vous pouvez aller à http://localhost:3000/postset vous verrez tous vos messages au format html. Mais, si vous tapez ceci:, http://localhost:3000/posts.jsonalors vous verrez tous vos messages dans un objet json envoyé par le serveur.

C'est très pratique pour créer des applications lourdes javascript qui doivent passer json dans les deux sens depuis le serveur. Si vous le souhaitez, vous pouvez facilement créer une API json sur le back-end de vos rails et ne transmettre qu'une seule vue, comme la vue d'index de votre contrôleur Post. Ensuite, vous pouvez utiliser une bibliothèque javascript comme Jquery ou Backbone (ou les deux) pour manipuler les données et créer votre propre interface. On les appelle des interfaces utilisateur asynchrones et elles deviennent très populaires (Gmail en est un). Ils sont très rapides et offrent à l'utilisateur final une expérience plus semblable à celle d'un ordinateur de bureau sur le Web. Bien sûr, ce n'est qu'un avantage du formatage de vos données.

La façon d'écrire ceci de Rails 3 serait la suivante:

    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

En plaçant respond_to :html, :xml, :jsonen haut de la classe, vous pouvez déclarer tous les formats que vous souhaitez que votre contrôleur envoie à vos vues.

Ensuite, dans la méthode du contrôleur, tout ce que vous avez à faire est de répondre_ avec (@wthing_object_you_have)

Il simplifie simplement votre code un peu plus que ce que Rails génère automatiquement.

Si vous voulez en savoir plus sur le fonctionnement interne de cette ...

D'après ce que je comprends, Rails introspecte les objets pour déterminer quel sera le format réel. La valeur des variables «format» est basée sur cette introspection. Les rails peuvent faire beaucoup de choses avec un peu d'informations. Vous seriez surpris de voir jusqu'où irait un simple @post ou: post.

Par exemple, si j'avais un fichier partiel _user.html.erb qui ressemblait à ceci:

_user.html.erb

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

Ensuite, cela seul dans ma vue d'index permettrait à Rails de savoir qu'il devait trouver le partiel "utilisateurs" et parcourir tous les objets "utilisateurs":

index.html.erb

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

permettrait à Rails de savoir qu'il avait besoin de trouver le partiel «utilisateur» et de parcourir tous les objets «utilisateurs»:

Vous pouvez trouver cet article de blog utile: http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

Vous pouvez également consulter la source: https://github.com/rails/rails

PhillipKregg
la source
1
Belle astuce sur les rails3 way. J'essaie toujours d'aller au bas du bloc respond_to, et ce que l'argument du bloc | format | est passé.
Cole
4
Bonne réponse mais ne dit rien de spécifique sur la variable de format transmise dans le bloc. Dans l'exemple donné, il y a format.html et format.json - ces deux éléments sont-ils transmis à respond_to, puis respond_to décide quoi faire avec eux?
Anthony
quand a été respond_toet respond_withintroduit? J'utilise les rails 2.3.5 et je reçoisNoMethodError (undefined method respond_to)
Abbood
10

D'après ce que je sais, respond_to est une méthode attachée à l'ActionController, vous pouvez donc l'utiliser dans chaque contrôleur, car tous héritent de l'ActionController. Voici la méthode respond_to de Rails:

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

Vous passez un bloc , comme je le montre ici:

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

Le | format | partie est l'argument que le bloc attend, donc à l'intérieur de la méthode respond_to nous pouvons l'utiliser. Comment?

Eh bien, si vous remarquez que nous passons le bloc avec un préfixe & dans la méthode respond_to, et nous le faisons pour traiter ce bloc comme un Proc. Puisque l'argument a le ".xml", ".html", nous pouvons l'utiliser comme méthode à appeler.

Ce que nous faisons essentiellement dans la classe respond_to, ce sont des méthodes d'appel ".html, .xml, .json" à une instance d'une classe Responder.

Nobita
la source
1
La source de respond_to dans les documents de l'API est différente de la source que vous avez incluse et me déconcertait. Votre extrait de code m'indique plus clairement que l'argument de bloc de format passe un objet Responder. La documentation du Répondant semble répondre à la question, en la lisant maintenant.
Cole
7

Je voudrais comprendre comment fonctionne réellement le bloc respond_to. Quel type de variable est format? Les méthodes .html et .json sont-elles de l'objet format?

Afin de comprendre ce qui formatest, vous pouvez d'abord regarder la source respond_to, mais rapidement vous constaterez que ce que vous devez vraiment regarder est le code de retrieve_response_from_mimes .

À partir d'ici, vous verrez que le bloc qui a été passé à respond_to(dans votre code) est en fait appelé et passé avec une instance de Collector (qui dans le bloc est référencée comme format). Collector génère essentiellement des méthodes (je pense au démarrage de Rails) basées sur ce que les types MIME connaissent.

Donc, oui, les méthodes .htmlet .jsonsont définies (au moment de l'exécution) sur la formatclasse Collector (aka ).

rnicholson
la source
2

La méta-programmation derrière l'enregistrement du répondeur (voir la réponse de Parched Squid) vous permet également de faire des trucs astucieux comme ceci:

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 ligne csv provoquera l'appel de to_csv sur chaque publication lorsque vous visitez /posts.csv. Cela facilite l'exportation de données au format CSV (ou tout autre format) à partir de votre site de rails.

La ligne js entraînera le rendu / l'exécution d'un fichier javascript /posts.js (ou /posts.js.coffee). J'ai trouvé que c'était un moyen léger de créer un site compatible Ajax à l'aide de fenêtres contextuelles jQuery UI.

Catharz
la source
1

Quel type de variable est format?

A partir d'un POV java, le format est une implémentation d'une interface anonyme. Cette interface a une méthode nommée pour chaque type MIME. Lorsque vous appelez l'une de ces méthodes (en lui passant un bloc), si rails estime que l'utilisateur souhaite ce type de contenu, il invoquera votre bloc.

La torsion, bien sûr, est que cet objet de collage anonyme n'implémente pas réellement d'interface - il intercepte les appels de méthode de manière dynamique et détermine s'il s'agit du nom d'un type MIME qu'il connaît.

Personnellement, je pense que ça a l'air bizarre: le bloc que vous passez est exécuté . Il serait plus logique pour moi de passer un hachage d'étiquettes de format et de blocs. Mais - c'est comme ça que ça se fait dans RoR, semble-t-il.

PaulMurrayCbr
la source
1

C'est un peu dépassé, par Ryan Bigg fait un excellent travail en expliquant cela ici:

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

En fait, cela pourrait être un peu plus détaillé que vous ne le cherchiez. En fait, il se passe beaucoup de choses en arrière-plan, y compris un besoin de comprendre comment les types MIME sont chargés.

idStar
la source
0

"Format" est votre type de réponse. Peut être json ou html, par exemple. C'est le format de la sortie que votre visiteur recevra.

Rafael Nascimento
la source
0

Il y a encore une chose que vous devez savoir - MIME.

Si vous devez utiliser un type MIME et qu'il n'est pas pris en charge par défaut, vous pouvez enregistrer vos propres gestionnaires dans config / initializers / mime_types.rb:

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


la source