Rails: come funziona il blocco respond_to?

Sto esaminando la guida Getting Started with Rails e mi sono confuso con la sezione 6.7. Dopo aver generato uno scaffold trovo il seguente blocco generato automaticamente nel mio controller:

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

Mi piacerebbe capire come funziona il blocco respond_to. Che tipo di variabile è il formato? I metodi .html e .json dell’object format sono? La documentazione di ActionController::MimeResponds::ClassMethods::respond_to non risponde alla domanda.

Sono nuovo di Ruby e sono rimasto bloccato a questo stesso codice. Le parti su cui sono stato appeso sono un po ‘più fondamentali di alcune delle risposte che ho trovato qui. Questo può o non può aiutare qualcuno.

  • respond_to è un metodo sulla superclass ActionController .
  • prende un blocco, che è come un delegato. Il blocco è da do end , con |format| come argomento per il blocco.
  • respond_to esegue il blocco, passando un risponditore nell’argomento di format .

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

  • Il Responder NON contiene un metodo per .html o .json , ma comunque chiamiamo questi metodi! Questa parte mi ha gettato per un giro.
  • Ruby ha una funzione chiamata method_missing . Se chiami un metodo che non esiste (come json o html ), Ruby chiama invece il metodo method_missing .

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

  • La class Responder usa la sua method_missing come una sorta di registrazione. Quando chiamiamo ‘json’, gli stiamo dicendo di rispondere alle richieste con l’estensione .json serializzando su json. Dobbiamo chiamare html senza argomenti per dirgli di gestire le richieste .html nel modo predefinito (usando convenzioni e viste).

Potrebbe essere scritto in questo modo (usando uno pseudocodice simile 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) 

Questa parte mi ha confuso. Lo trovo ancora non intuitivo. Ruby sembra usare questa tecnica un po ‘. L’intera class ( responder ) diventa l’implementazione del metodo. Per sfruttare method_missing , abbiamo bisogno di un’istanza della class, quindi siamo obbligati a passare un callback in cui passano l’object metodo-like. Per qualcuno che ha codificato in lingue simili a C per 20 anni, questo è molto arretrato e non intuitivo per me. Non che sia cattivo! Ma è qualcosa che un sacco di persone con quel tipo di background hanno bisogno di capire, e penso che potrebbe essere ciò che l’OP stava cercando.

ps nota che in RoR 4.2 respond_to stato estratto in responders gem.

Questo è un blocco di codice Ruby che sfrutta un metodo di supporto di Rails. Se non hai ancora familiarità con i blocchi, li vedrai molto in Ruby.

respond_to è un metodo helper di Rails che è collegato alla class Controller (o meglio, la sua super class). Fa riferimento alla risposta che verrà inviata alla vista (che sta andando al browser).

Il blocco nell’esempio è la formattazione dei dati – passando in un parametro “format” nel blocco – da inviare dal controller alla vista ogni volta che un browser effettua una richiesta di dati html o json.

Se sei sul tuo computer locale e hai impostato il tuo scaffold Post, puoi andare a http://localhost:3000/posts e vedrai tutti i tuoi post in formato html. Ma se digiti questo: http://localhost:3000/posts.json , vedrai tutti i tuoi messaggi in un object json inviato dal server.

Questo è molto utile per creare applicazioni javascript pesanti che devono passare json avanti e indietro dal server. Se lo si desidera, è ansible creare facilmente un json api sul binario back-end e passare solo una vista, ad esempio la vista indice del post controller. Quindi è ansible utilizzare una libreria javascript come Jquery o Backbone (o entrambi) per manipolare i dati e creare la propria interfaccia. Questi sono chiamati UI asincroni e stanno diventando molto popolari (Gmail è uno). Sono molto veloci e danno all’utente finale un’esperienza sul desktop più simile a quella del desktop. Naturalmente, questo è solo uno dei vantaggi della formattazione dei dati.

Il modo di scrivere Rails 3 sarebbe questo:

  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 

Mettendo respond_to :html, :xml, :json all'inizio della class, puoi dichiarare tutti i formati che vuoi che il controller invii alle tue visualizzazioni.

Quindi, nel metodo controller, tutto ciò che devi fare è answer_with (@whatever_object_you_have)

Semplifica il tuo codice un po 'di più di quello che Rails genera automaticamente.

Se vuoi sapere il funzionamento interno di questo ...

Da quanto ho capito, Rails introspe gli oggetti per determinare quale sarà il formato attuale. Il valore delle variabili 'format' si basa su questa introspezione. Rails può fare molto con un po 'di informazioni. Saresti sorpreso di quanto lontano un semplice post o: post andrà.

Ad esempio, se avessi un file parziale _user.html.erb che assomiglia a questo:

_user.html.erb

 
  • <%= link_to user.name, user %>
  • Quindi, questo da solo nella mia vista indice avrebbe permesso a Rails di sapere che era necessario trovare il partial 'user' e iterare attraverso tutti gli oggetti 'users':

    index.html.erb

      
      <%= render @users %>

    permetterebbe a Rails di sapere che aveva bisogno di trovare il 'utente' parziale e iterare attraverso tutti gli oggetti 'utenti':

    Potresti trovare utile questo post sul blog: http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

    Puoi anche consultare la fonte: https://github.com/rails/rails

    Da quello che so, respond_to è un metodo collegato ad ActionController, quindi puoi usarlo in ogni singolo controller, perché tutti ereditano da ActionController. Ecco il metodo response_to di Rails:

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

    Stai passando un blocco , come lo mostro qui:

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

    Il | formato | parte è l’argomento che il blocco si aspetta, quindi all’interno del metodo respond_to possiamo usarlo. Come?

    Bene, se noti che passiamo il blocco con un prefisso e nel metodo respond_to, e lo facciamo per trattare quel blocco come un Proc. Poiché l’argomento ha il “.xml”, “.html” possiamo usarlo come metodi da chiamare.

    Quello che fondamentalmente facciamo nella class respond_to sono i metodi di chiamata “.html, .xml, .json” a un’istanza di una class Responder.

    Mi piacerebbe capire come funziona il blocco respond_to. Che tipo di variabile è il formato? I metodi .html e .json dell’object format sono?

    Per capire quale sia il format , si può prima cercare la fonte di respond_to , ma in fretta si scoprirà che quello che veramente è necessario guardare è il codice per retrieve_response_from_mimes .

    Da qui, vedrai che il blocco che è stato passato a respond_to (nel tuo codice), viene effettivamente chiamato e passato con un’istanza di Collector (che all’interno del blocco viene fatto riferimento come format ). Il collezionista fondamentalmente genera metodi (credo all’avvio di Rails) basati su ciò che i binari dei tipi di mime conoscono.

    Quindi, sì, il .html e .json sono metodi definiti (in fase di esecuzione) nella class Collector (aka format ).

    La meta-programmazione dietro la registrazione del risponditore (vedi la risposta di Parched Squid) ti permette anche di fare cose belle come questa:

     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 linea csv farà sì che to_csv venga chiamato su ogni post quando visiti /posts.csv. Ciò semplifica l’esportazione dei dati come CSV (o qualsiasi altro formato) dal tuo sito di binari.

    La linea js causerà il rendering / esecuzione di un file javascript /posts.js (o /posts.js.coffee). Ho scoperto che è un modo leggero per creare un sito abilitato Ajax utilizzando i pop-up dell’interfaccia utente jQuery.

    Che tipo di variabile è il formato?

    Da un POV java, il formato è un’implementazione di un’interfaccia anonima. Questa interfaccia ha un metodo chiamato per ciascun tipo MIME. Quando invochi uno di questi metodi (passandogli un blocco), se le rotaie pensano che l’utente voglia quel tipo di contenuto, invocherà il tuo blocco.

    La svolta, naturalmente, è che questo object collante anonimo non implementa effettivamente un’interfaccia – cattura il metodo chiamate dynamicmente e funziona se è il nome di un tipo MIME di cui è a conoscenza.

    Personalmente, penso che sembri strano: il blocco che hai passato viene eseguito . Avrebbe più senso per me passare in un hash di etichette e blocchi di formato. Ma – è così che è fatto in RoR, a quanto pare.

    C’è un’altra cosa che dovresti sapere – MIME.

    Se è necessario utilizzare un tipo MIME e non è supportato per impostazione predefinita, è ansible registrare i propri gestori in config / initializers / mime_types.rb:

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

    Questo è un po ‘obsoleto, Ryan Bigg fa un ottimo lavoro spiegando questo qui:

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

    In effetti, potrebbe essere un po ‘più dettagliato di quello che stavi cercando. A quanto pare, ci sono molte cose dietro le quinte, inclusa la necessità di capire come vengono caricati i tipi MIME.

    “Format” è il tuo tipo di risposta. Potrebbe essere json o html, per esempio. È il formato dell’output che il tuo visitatore riceverà.