Rails: Wie funktioniert der Block reply_to?

210

Ich gehe den Leitfaden Erste Schritte mit Rails durch und wurde mit Abschnitt 6.7 verwechselt. Nach dem Generieren eines Gerüsts finde ich den folgenden automatisch generierten Block in meinem Controller:

def index
  @posts = Post.all

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

Ich möchte verstehen, wie der Block "reply_to" tatsächlich funktioniert. Welche Art von Variable ist Format? Sind .html- und .json-Methoden des Formatobjekts? Die Dokumentation für

ActionController::MimeResponds::ClassMethods::respond_to

beantwortet die Frage nicht.

Cole
quelle
Es wäre schön, wenn ich auf die Dokumentation für ActionController :: MimeResponds :: ClassMethods :: reply_to verlinken könnte, aber api.rubyonrails.org scheint direkte Hyperlinks nicht zu mögen ...
Cole
reply_to nimmt das Ende des Aufrufs (z. B. blah.html, blah.json usw.) und stimmt mit der angegebenen Ansicht überein. Andere Antworten können je nach Anwendung XML, CSV und viele weitere sein.
ScottJShea
5
Wie stimmt es mit der angegebenen Ansicht überein?
Cole
Ich glaube nicht, dass die Erweiterung (xml, html usw.) einer Ansicht zugeordnet ist. Wenn Sie das Standard-Rendering auswählen ( format.html- kein Argument), werden Konventionen (basierend auf URL und HTTP-Verb) verwendet, um eine Ansicht auszuwählen (voraussichtlich HTML). Der Responder (Format) wird hier angewiesen, URLs mit der Endung .json durch Serialisierung in json zu rendern, anstatt Ansichten und Konventionen zu verwenden.
Craig Celeste

Antworten:

188

Ich bin neu bei Ruby und stecke bei demselben Code fest. Die Teile, an denen ich aufgehängt wurde, waren etwas grundlegender als einige der Antworten, die ich hier fand. Dies kann jemandem helfen oder auch nicht.

  • respond_toist eine Methode in der Oberklasse ActionController.
  • Es dauert einen Block, der wie ein Delegat ist. Der Block ist von dobis end, mit |format|als Argument für den Block.
  • reply_to führt Ihren Block aus und übergibt einen Responder an das formatArgument.

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

  • Das Responderenthält KEINE Methode für .htmloder .json, aber wir nennen diese Methoden trotzdem! Dieser Teil warf mich für eine Schleife.
  • Ruby hat eine Funktion namens method_missing. Wenn Sie eine Methode aufrufen, die nicht existiert (wie jsonoder html), ruft Ruby method_missingstattdessen die Methode auf.

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

  • Die ResponderKlasse verwendet es method_missingals eine Art Registrierung. Wenn wir 'json' aufrufen, weisen wir ihn an, auf Anfragen mit der Erweiterung .json zu antworten, indem er in json serialisiert. Wir müssen htmlohne Argumente aufrufen , um anzuweisen, dass HTML-Anforderungen standardmäßig behandelt werden sollen (unter Verwendung von Konventionen und Ansichten).

Es könnte so geschrieben werden (mit JS-ähnlichem Pseudocode):

// 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)

Dieser Teil verwirrte mich zum Teufel. Ich finde es immer noch nicht intuitiv. Ruby scheint diese Technik ziemlich oft anzuwenden. Die gesamte Klasse ( responder) wird zur Methodenimplementierung. Um die Hebelwirkung nutzen zu können method_missing, benötigen wir eine Instanz der Klasse. Daher müssen wir einen Rückruf übergeben, an den das methodenähnliche Objekt übergeben wird. Für jemanden, der seit 20 Jahren in C-ähnlichen Sprachen codiert, ist dies für mich sehr rückständig und nicht intuitiv. Nicht dass es schlecht wäre! Aber es ist etwas, was viele Leute mit diesem Hintergrund brauchen, um sich zurechtzufinden, und ich denke, es könnte das sein, wonach das OP gesucht hat.

ps beachte, dass in RoR 4.2 respond_toin Responder Gem extrahiert wurde .

Craig Celeste
quelle
Vielen Dank, Craig, dieser Link hatte tatsächlich auch eine Menge nützlicher Informationen. Ich wusste nicht, wie viel damit möglich ist method_missing, wenn man bedenkt, dass man ihm Argumente und einen Block übergeben kann!
Aditya MP
2
Beste Antwort zur Erklärung der Verwendung von method_missing () als Registrierungsmechanismus in der Responder-Klasse! Ich war auch sehr verwirrt mit diesem Code.
Alan Evangelista
1
Rails 6-Gerüstgeneratoren scheinen Code mit respond_toin den Controllern zu erzeugen , ohne dass der Responder-Edelstein in der Gemfile vorhanden ist. Vielleicht wurde das bisschen darüber respond_to, in das Juwel der Responder extrahiert zu werden, geändert?
Qasim
106

Dies ist ein Block von Ruby-Code, der eine Rails-Hilfsmethode nutzt. Wenn Sie mit Blöcken noch nicht vertraut sind, werden Sie sie in Ruby häufig sehen.

respond_toist eine Rails-Hilfsmethode, die an die Controller-Klasse (oder besser gesagt an ihre Superklasse) angehängt ist. Es bezieht sich auf die Antwort, die an die Ansicht gesendet wird (die an den Browser gesendet wird).

Der Block in Ihrem Beispiel formatiert Daten - indem Sie einen 'Format'-Parameter im Block übergeben -, die vom Controller an die Ansicht gesendet werden, wenn ein Browser eine Anfrage nach HTML- oder JSON-Daten stellt.

Wenn Sie sich auf Ihrem lokalen Computer befinden und Ihr Post-Gerüst eingerichtet haben, können Sie zu gehen http://localhost:3000/postsund alle Ihre Posts im HTML-Format sehen. Wenn Sie jedoch http://localhost:3000/posts.jsonFolgendes eingeben::, werden alle Ihre Beiträge in einem vom Server gesendeten JSON-Objekt angezeigt.

Dies ist sehr praktisch, um Javascript-schwere Anwendungen zu erstellen, die json vom Server hin und her übertragen müssen. Wenn Sie möchten, können Sie problemlos eine JSON-API auf Ihrem Rails-Backend erstellen und nur eine Ansicht übergeben - wie die Indexansicht Ihres Post-Controllers. Dann könnten Sie eine Javascript-Bibliothek wie Jquery oder Backbone (oder beides) verwenden, um Daten zu bearbeiten und Ihre eigene Schnittstelle zu erstellen. Diese werden als asynchrone Benutzeroberflächen bezeichnet und erfreuen sich großer Beliebtheit (Google Mail ist eine davon). Sie sind sehr schnell und bieten dem Endbenutzer eine Desktop-ähnliche Erfahrung im Web. Dies ist natürlich nur ein Vorteil der Formatierung Ihrer Daten.

Die Rails 3-Schreibweise wäre folgende:

    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

Indem Sie respond_to :html, :xml, :jsonan die Spitze der Klasse setzen, können Sie alle Formate deklarieren, die Ihr Controller an Ihre Ansichten senden soll.

In der Controller-Methode müssen Sie dann nur noch mit (@whatever_object_you_have) antworten.

Es vereinfacht Ihren Code nur ein wenig mehr als das, was Rails automatisch generiert.

Wenn Sie mehr über das Innenleben wissen wollen ...

Soweit ich weiß, überprüft Rails die Objekte, um das tatsächliche Format zu bestimmen. Der Wert der 'Format'-Variablen basiert auf dieser Selbstbeobachtung. Schienen können mit ein paar Informationen eine ganze Menge tun. Sie wären überrascht, wie weit ein einfacher @post oder: post gehen wird.

Wenn ich zum Beispiel eine _user.html.erb-Teildatei hätte, die so aussieht:

_user.html.erb

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

Dann würde dies allein in meiner Indexansicht Rails wissen lassen, dass es notwendig ist, die "Benutzer" teilweise zu finden und alle "Benutzer" -Objekte zu durchlaufen:

index.html.erb

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

würde Rails wissen lassen, dass es notwendig ist, den 'Benutzer'-Teil zu finden und alle' Benutzer'-Objekte zu durchlaufen:

Sie können diesen Blog-Beitrag nützlich finden: http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

Sie können auch die Quelle lesen: https://github.com/rails/rails

PhillipKregg
quelle
1
Netter Tipp auf dem Rails3-Weg. Ich versuche immer noch, dem Block "reply_to" und dem Blockargument | format | auf den Grund zu gehen wird bestanden.
Cole
4
Gute Antwort, sagt aber nichts Spezifisches über die Formatvariable aus, die an den Block übergeben wird. In dem angegebenen Beispiel gibt es format.html und format.json - werden beide an reply_to übergeben und reply_to entscheidet dann, was mit ihnen geschehen soll?
Anthony
wann wurde respond_tound respond_witheingeführt? Ich benutze Schienen 2.3.5 und ich bekommeNoMethodError (undefined method respond_to)
Abbood
10

Soweit ich weiß, ist reply_to eine an den ActionController angehängte Methode, sodass Sie sie in jedem einzelnen Controller verwenden können, da alle vom ActionController erben. Hier ist die Rails-Methode "reply_to":

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

Sie passieren es einen Block , wie ich hier zeige:

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

Das | Format | Teil ist das Argument, das der Block erwartet, also können wir das innerhalb der Methode reply_to verwenden. Wie?

Wenn Sie bemerken, übergeben wir den Block mit einem vorangestellten & in der reply_to-Methode, und wir tun dies, um diesen Block als Proc zu behandeln. Da das Argument ".xml", ".html" hat, können wir dies als aufzurufende Methoden verwenden.

Grundsätzlich rufen wir in der Klasse "reply_to" die Methoden ".html, .xml, .json" für eine Instanz einer Responder-Klasse auf.

Nobita
quelle
1
Die Quelle für reply_to in den API-Dokumenten unterscheidet sich von der Quelle, die Sie angegeben haben, und hat mich abgeworfen. Ihr Snippet macht mir klarer, dass dem Formatblockargument ein Responder-Objekt übergeben wird. Die Responder-Dokumentation scheint die Frage zu beantworten und liest sie jetzt.
Cole
7

Ich möchte verstehen, wie der Block "reply_to" tatsächlich funktioniert. Welche Art von Variable ist Format? Sind .html- und .json-Methoden des Formatobjekts?

Um zu verstehen, was formatist, könnten Sie zuerst nach der Quelle suchen respond_to, aber schnell werden Sie feststellen, dass Sie wirklich den Code für retrieve_response_from_mimes suchen müssen .

Von hier aus sehen Sie, dass der Block, an den respond_to(in Ihrem Code) übergeben wurde, tatsächlich aufgerufen und mit einer Instanz von Collector übergeben wird (auf die innerhalb des Blocks verwiesen wird format). Collector generiert grundsätzlich Methoden (glaube ich beim Start von Rails) basierend auf den MIME-Typen, über die Rails Bescheid weiß.

Also, ja, die Methoden sind .htmlund .jsonsind (zur Laufzeit) in der Collector- formatKlasse (aka ) definiert.

rnicholson
quelle
2

Die Meta-Programmierung hinter der Responder-Registrierung (siehe Antwort von Parched Squid) ermöglicht es Ihnen auch, solche raffinierten Dinge zu tun:

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

Die CSV-Zeile bewirkt, dass to_csv für jeden Beitrag aufgerufen wird, wenn Sie /posts.csv besuchen. Dies macht es einfach, Daten als CSV (oder ein anderes Format) von Ihrer Rails-Site zu exportieren.

Die js-Zeile bewirkt, dass eine Javascript-Datei /posts.js (oder /posts.js.coffee) gerendert / ausgeführt wird. Ich habe festgestellt, dass dies eine einfache Möglichkeit ist, eine Ajax-fähige Site mithilfe von Popup-Fenstern für die jQuery-Benutzeroberfläche zu erstellen.

Catharz
quelle
1

Welche Art von Variable ist Format?

Aus einem Java-POV ist das Format eine Implementierung einer anonymen Schnittstelle. Diese Schnittstelle verfügt über eine Methode, die für jeden MIME-Typ benannt ist. Wenn Sie eine dieser Methoden aufrufen (einen Block übergeben) und Rails der Meinung ist, dass der Benutzer diesen Inhaltstyp möchte, wird Ihr Block aufgerufen.

Die Wendung ist natürlich, dass dieses anonyme Klebeobjekt keine Schnittstelle implementiert - es fängt die Methodenaufrufe dynamisch ab und ermittelt, ob es sich um den Namen eines MIME-Typs handelt, den es kennt.

Persönlich finde ich es komisch: Der Block, den Sie übergeben, wird ausgeführt . Es wäre für mich sinnvoller, einen Hash von Formatbezeichnungen und -blöcken einzugeben. Aber - so wird es anscheinend in RoR gemacht.

PaulMurrayCbr
quelle
1

Dies ist ein wenig veraltet, von Ryan Bigg macht einen tollen Job und erklärt dies hier:

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

In der Tat könnte es ein bisschen detaillierter sein, als Sie gesucht haben. Wie sich herausstellt, ist hinter den Kulissen viel los, einschließlich der Notwendigkeit zu verstehen, wie die MIME-Typen geladen werden.

idStar
quelle
0

"Format" ist Ihr Antworttyp. Könnte zum Beispiel json oder html sein. Dies ist das Format der Ausgabe, die Ihr Besucher erhält.

Rafael Nascimento
quelle
0

Es gibt noch eine Sache, die Sie beachten sollten - MIME.

Wenn Sie einen MIME-Typ verwenden müssen und dieser standardmäßig nicht unterstützt wird, können Sie Ihre eigenen Handler in config / initializers / mime_types.rb registrieren:

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


quelle