2012-02-29 9 views
183

Sto passando attraverso la guida Getting Started with Rails e mi sono confuso con la sezione 6.7. Dopo aver generato un ponteggio trovo il seguente blocco auto-generata nel mio controller:Rails: come funziona il blocco respond_to?

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 funziona realmente. Che tipo di variabile è il formato? I metodi .html e .json dell'oggetto format sono? Il documentation per ActionController::MimeResponds::ClassMethods::respond_to non risponde alla domanda.

+0

Sarebbe bello se potessi collegare alla documentazione di ActionController :: MimeResponds :: ClassMethods :: respond_to ma api.rubyonrails.org non sembra gradire collegamenti ipertestuali diretti ... – Cole

+0

respond_to prende la fine della chiamata (es. blah.html, blah.json, ecc.) e corrisponde alla vista specificata. Altre risposte possono essere XML, CSV e molte altre in base all'applicazione. – ScottJShea

+4

In che modo "corrisponde alla vista specificata?" – Cole

risposta

95

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

respond_to è un metodo di supporto Rails che è collegato alla classe Controller (o meglio, la sua super classe). 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 "formato" 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 il tuo scaffold Post impostato, 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 oggetto 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, è possibile creare facilmente un json api sul binario back-end e passare solo una vista, ad esempio la vista indice del post controller. Quindi è possibile utilizzare una libreria javascript come Jquery o Backbone (o entrambi) per manipolare i dati e creare la propria interfaccia. Queste sono chiamate UI asincrone 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 Rails 3 di scrivere questo 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 al vertice della classe, è possibile dichiarare tutti i formati che si desidera che il controller per inviare le vostre opinioni.

Poi, nel metodo di controllo, tutto quello che dovete fare è respond_with (@whatever_object_you_have)

'solo semplifica il codice un po' più di quello che Rails genera automaticamente.

Se desidera conoscere il funzionamento interno di questo ...

Da quello che ho capito, Rails introspetta gli oggetti per determinare quali il formato attuale sta per essere. 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à.

Per esempio, se ho avuto un file parziale _user.html.erb che si presentava così:

_user.html.erb

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

Poi, solo questo a mio avviso indice avrebbe lasciato Rails sapere che aveva bisogno di trovare parziale gli 'utenti' e scorrere tutti gli oggetti dei degli utenti ':

index.html .erb

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

avrebbe lasciato Rails sapere che aveva bisogno di trovare la 'user' parziale e scorrere tutti gli oggetti dei degli utenti ':

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

È possibile anche sfogliare la fonte: https://github.com/rails/rails

+1

Bel suggerimento sul binario3. Sto ancora cercando di arrivare alla fine del blocco respond_to, e quale argomento del blocco | format | viene passato. – Cole

+3

Buona risposta ma non dice nulla di specifico sulla variabile di formato che viene passata nel blocco. Nell'esempio fornito c'è format.html e format.json - entrambi vengono passati a respond_to e quindi respond_to decide cosa fare con loro? – Anthony

+0

quando è stato introdotto 'reply_to' e' respond_with'? Sto usando ** rail 2.3.5 ** e sto ricevendo 'NoMethodError (metodo indefinito respond_to)' – abbood

9

Da quello che so, respond_to è un metodo attaccato al ActionController, in modo da poter utilizzare in ogni singolo controller , perché tutti ereditano da ActionController. Ecco il metodo di Rails respond_to:

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

Si passa una block, come 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 si nota che si passa il blocco con un prefisso & 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.

Ciò che fondamentalmente facciamo nella classe respond_to sono i metodi di chiamata ".html, .xml, .json" a un'istanza di una classe Responder.

+1

La fonte per respond_to nei documenti API diversi dalla sorgente che hai incluso, e mi stava buttando fuori. Il tuo frammento mi rende più chiaro che l'argomento del blocco di formato viene passato a un oggetto Responder. La documentazione del risponditore sembra rispondere alla domanda, leggendolo ora. – Cole

7

Mi piacerebbe capire come funziona il blocco respond_to. Che tipo di variabile è il formato? I metodi .html e .json del formato oggetto?

Per capire che cosa format è, si potrebbe prima guardare il sorgente per respond_to, ma presto vi accorgerete che cosa realmente avete bisogno di guardare è il codice per retrieve_response_from_mimes.

Da qui, vedrete che il blocco che è stato passato a respond_to (nel codice), è effettivamente chiamato e passato con un'istanza di Collector (che all'interno del blocco viene fatto riferimento come format). Il raccoglitore genera fondamentalmente metodi (credo all'avvio di Rails) in base a ciò che sa di rotaie mime types.

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

158

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 sul superclasse ActionController.
  • prende un blocco, che è come un delegato. Il blocco è da do fino al end, con |format| come argomento per il blocco.
  • respond_to esegue il blocco, passando un risponditore nell'argomento format.

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

  • Il Responder non contiene un metodo per .html o .json, ma chiamare questi metodi in ogni modo! Questa parte mi ha gettato per un giro.
  • Ruby ha una funzionalità 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 classe Responder utilizza il suo 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).

Si potrebbe scrivere come questo (utilizzando JS-come pseudocodice):

// 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 confonde il heck fuori di me. Lo trovo ancora non intuitivo. Ruby sembra usare questa tecnica un po '. L'intera classe (responder) diventa l'implementazione del metodo. Al fine di sfruttare method_missing, abbiamo bisogno di un'istanza della classe, quindi siamo obbligati a passare una richiamata in cui passano l'oggetto simile al metodo. 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.

p.s. si noti che nella RdR 4.2 respond_to è stato estratto nella gemma responders.

+0

Sono assolutamente d'accordo, la fonte di rubino è difficile da leggere. –

+0

Grazie Craig, quel collegamento aveva anche un sacco di informazioni utili, non mi rendevo conto di quanto è possibile con 'method_missing', considerando che puoi passare argomenti _e_ un blocco! –

2

La meta-programmazione dietro registrazione del risponditore (vedi risposta seccata Squid) permette anche di fare cose carine 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 causerà to_csv di essere chiamato su ogni post quando si visita/posts.csv. Ciò semplifica l'esportazione dei dati come CSV (o qualsiasi altro formato) dal tuo sito di binari.

La riga 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.

1

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, ovviamente, è che questo oggetto anonimo di colla non implementa effettivamente un'interfaccia: cattura il metodo richiama dinamicamente e funziona se è il nome di un tipo di mimo di cui è a conoscenza.

Personalmente, penso che sembri strano: il blocco che si passa è 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.

0

"Formato" è il tipo di risposta. Potrebbe essere json o html, per esempio. È il formato dell'output che il tuo visitatore riceverà.

Problemi correlati