Szyny: Jak działa blok respond_to?

211

Przeglądam przewodnik Pierwsze kroki z Railsami i pomyliłem się z sekcją 6.7. Po wygenerowaniu rusztowania znajduję w moim kontrolerze następujący automatycznie wygenerowany blok:

def index
  @posts = Post.all

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

Chciałbym zrozumieć, jak faktycznie działa blok respond_to. Jakim typem zmiennej jest format? Czy metody .html i .json obiektu formatu są? Dokumentacji dla

ActionController::MimeResponds::ClassMethods::respond_to

nie odpowiada na pytanie.

Kapusta
źródło
Byłoby miło, gdyby mógł połączyć się z dokumentacją ActionController :: MimeResponds :: ClassMethods :: respond_to ale api.rubyonrails.org nie wydaje się niczym bezpośrednich linków ...
Cole
respond_to przejmuje koniec wywołania (np. blah.html, blah.json itp.) i odpowiada podanemu widokowi. Inne odpowiedzi to XML, CSV i wiele innych, w zależności od aplikacji.
ScottJShea
5
W jaki sposób „pasuje do określonego widoku?”
Cole
Nie sądzę, aby rozszerzenie (xml, html itp.) Odwzorowało widok. Jeśli wybierzesz domyślne renderowanie ( format.html- bez argumentu), użyje konwencji (na podstawie adresu URL i czasownika HTTP), aby wybrać widok (prawdopodobnie HTML). Obiekt odpowiadający (format) jest tutaj instruowany, aby renderował adresy URL kończące się na .json przez serializację do json, zamiast używania widoków i konwencji.
Craig Celeste

Odpowiedzi:

189

Jestem nowy w Ruby i utknąłem na tym samym kodzie. Części, na których się rozłączyłem, były trochę bardziej fundamentalne niż niektóre odpowiedzi, które tu znalazłem. To może, ale nie musi komuś pomóc.

  • respond_tojest metodą nadklasy ActionController.
  • zajmuje blok, który jest jak delegat. Blok jest od dodo end, z |format|argumentem do bloku.
  • respond_to wykonuje blok, przekazując obiekt odpowiadający do formatargumentu.

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

  • ResponderNie zawiera metodę .htmlalbo .json, ale nazywamy tych metod anyways! Ta część rzuciła mnie na pętlę.
  • Ruby ma funkcję o nazwie method_missing. Jeśli wywołasz metodę, która nie istnieje (jak jsonlub html), Ruby method_missingzamiast niej wywołuje metodę.

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

  • ResponderKlasa wykorzystuje method_missingjako rodzaj rejestracji. Kiedy nazywamy „json”, mówimy mu, aby odpowiadał na żądania z rozszerzeniem .json, serializując do json. Musimy zadzwonić htmlbez argumentów, aby nakazać mu obsługę żądań .html w domyślny sposób (przy użyciu konwencji i widoków).

Można to napisać w ten sposób (używając pseudokodu podobnego do 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)

Ta część mnie zdezorientowała. Nadal uważam to za nieintuicyjne. Ruby wydaje się używać tej techniki dość często. Cała klasa ( responder) staje się implementacją metody. Aby wykorzystać dźwignię method_missing, potrzebujemy instancji klasy, więc jesteśmy zobowiązani do przekazania wywołania zwrotnego, do którego przekazują obiekt podobny do metody. Dla kogoś, kto koduje w językach podobnych do C od 20 lat, jest to dla mnie bardzo zacofane i nieintuicyjne. Nie to, że jest źle! Ale jest to coś, co wielu ludzi z takim doświadczeniem musi się rozejrzeć, i myślę, że może być tym, o co chodziło OP.

ps zauważyć, że w RoR 4.2 respond_tozostał wyodrębniony w klejnot odpowiedzi .

Craig Celeste
źródło
Dziękuję Craig, ten link zawierał również mnóstwo przydatnych informacji, nie zdawałem sobie sprawy z tego, ile to możliwe method_missing, biorąc pod uwagę, że można przekazać argumenty i blok!
Aditya MP
2
Najlepsza odpowiedź na wyjaśnienie użycia metody method_missing () jako mechanizmu rejestracji w klasie Responder! Byłem również bardzo zdezorientowany tym kodem.
Alan Evangelista
1
Generatory rusztowań w Rails 6 wydają się produkować kod respond_tow kontrolerach, bez klejnotu odpowiadającego w Gemfile. Być może zmieniono nieco respond_tokwestię bycia wyodrębnionym do klejnotu respondentów?
Qasim
106

Jest to blok kodu Ruby, który korzysta z metody pomocniczej Rails. Jeśli nie znasz jeszcze bloków, zobaczysz je bardzo często w Ruby.

respond_tojest metodą pomocniczą Rails, która jest dołączona do klasy Controller (a raczej jej superklasy). Odnosi się do odpowiedzi, która zostanie wysłana do Widoku (który przechodzi do przeglądarki).

Blok w twoim przykładzie to formatowanie danych - poprzez przekazanie parametru „format” w bloku - wysyłanego z kontrolera do widoku, ilekroć przeglądarka wysyła żądanie danych HTML lub JSON.

Jeśli korzystasz z komputera lokalnego i masz skonfigurowane rusztowanie Post, możesz przejść do, http://localhost:3000/postsa zobaczysz wszystkie swoje posty w formacie HTML. Ale jeśli wpiszesz http://localhost:3000/posts.jsonto:, zobaczysz wszystkie swoje posty w obiekcie json wysłanym z serwera.

Jest to bardzo przydatne do tworzenia ciężkich aplikacji javascript, które muszą przesyłać json tam iz powrotem z serwera. Jeśli chcesz, możesz łatwo utworzyć interfejs json api na zapleczu szyn i przekazać tylko jeden widok - na przykład widok indeksu kontrolera Post. Następnie możesz użyć biblioteki javascript, takiej jak Jquery lub Backbone (lub obie), aby manipulować danymi i utworzyć własny interfejs. Są to tak zwane asynchroniczne interfejsy użytkownika i stają się bardzo popularne (Gmail jest jednym z nich). Są bardzo szybkie i zapewniają użytkownikowi końcowemu bardziej wrażenia z korzystania z komputera w Internecie. Oczywiście jest to tylko jedna zaleta formatowania danych.

Sposób pisania w Rails 3 byłby następujący:

    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

Umieszczając respond_to :html, :xml, :jsonna szczycie klasy, możesz zadeklarować wszystkie formaty, które kontroler ma wysyłać do twoich widoków.

Następnie w metodzie kontrolera wszystko, co musisz zrobić, to odpowiedz_z (@wszystko_obiektywem_masz)

To tylko upraszcza twój kod nieco bardziej niż to, co Rails generuje automatycznie.

Jeśli chcesz wiedzieć o wewnętrznym funkcjonowaniu tego ...

Z tego, co rozumiem, Railsy introspekują obiekty, aby ustalić, jaki będzie rzeczywisty format. Wartość zmiennych „format” opiera się na tej introspekcji. Szyny mogą wiele zrobić z odrobiną informacji. Byłbyś zaskoczony, jak daleko posunie się prosty @post lub: post.

Na przykład, jeśli mam plik częściowy _user.html.erb, który wyglądałby tak:

_user.html.erb

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

Wtedy to samo w moim widoku indeksu dałoby Railsom informację, że trzeba znaleźć częściowe „użytkowników” i iterować przez wszystkie obiekty „użytkowników”:

index.html.erb

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

poinformuje Railsów, że musi znaleźć częściowe „użytkownika” i iterować przez wszystkie obiekty „użytkowników”:

Ten post może być przydatny: http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

Możesz także przejrzeć źródło: https://github.com/rails/rails

PhillipKregg
źródło
1
Dobra wskazówka na szynach 3 sposób. Nadal próbuję dostać się na dół bloku respond_to i jaki jest format argumentu bloku | przechodzi
Cole
4
Dobra odpowiedź, ale nie mówi nic konkretnego o zmiennej formatu przekazywanej do bloku. W podanym przykładzie jest format.html i format.json - czy oba są przekazywane do respond_to, a następnie respond_to decyduje, co z nimi zrobić?
Anthony
kiedy respond_toi respond_withwprowadził? Używam szyn 2.3.5 i dostajęNoMethodError (undefined method respond_to)
abbood
10

Z tego co wiem, respond_to jest metodą dołączoną do ActionController, więc możesz używać jej w każdym kontrolerze, ponieważ wszystkie dziedziczą po ActionController. Oto metoda Rails respond_to:

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

Mijasz blok , jak pokazuję tutaj:

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

| Format | część jest argumentem, którego oczekuje blok, więc w metodzie respond_to możemy tego użyć. W jaki sposób?

Cóż, jeśli zauważysz, że przekazujemy blok z prefiksem & w metodzie respond_to i robimy to, aby traktować ten blok jako Proc. Ponieważ argument zawiera „.xml”, „.html”, możemy go użyć jako metody do wywołania.

W zasadzie robimy w klasie respond_to metody wywoływania „.html, .xml, .json” do instancji klasy obiektu odpowiadającego.

Nobita
źródło
1
Źródło respond_to w dokumentach API różni się od źródła, które podałeś, i mnie wyrzucało. Twój fragment wyjaśnia mi, że argument bloku formatu jest przekazywany do obiektu odpowiadającego. Dokumentacja obiektu odpowiadającego wydaje się odpowiadać na pytanie, czytając to teraz.
Cole
7

Chciałbym zrozumieć, jak faktycznie działa blok respond_to. Jakim typem zmiennej jest format? Czy metody .html i .json obiektu formatu są?

Aby zrozumieć, co to formatjest, możesz najpierw spojrzeć na źródło respond_to, ale szybko przekonasz się, że tak naprawdę musisz przyjrzeć się kodowi retrieve_response_from_mimes .

Stąd zobaczysz, że blok, który został przekazany respond_to (w twoim kodzie), jest faktycznie wywoływany i przekazywany z instancją Collector (która w bloku jest oznaczona jako format). Collector generuje metody (jak sądzę przy starcie Railsów) w oparciu o to, o czym wie typy mime .

Więc tak, .htmli .jsonsą metodami zdefiniowanymi (w czasie wykonywania) w Collector (aliasformat ).

rnicholson
źródło
2

Metaprogramowanie odpowiadające za rejestrację respondentów (patrz odpowiedź Spieczona kałamarnica) pozwala także robić fajne rzeczy:

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

Linia csv spowoduje, że to_csv będzie wywoływany w każdym poście podczas odwiedzania /posts.csv. Ułatwia to eksportowanie danych jako CSV (lub dowolnego innego formatu) ze strony internetowej.

Wiersz js spowoduje wyświetlenie / wykonanie pliku javascript /posts.js (lub /posts.js.coffee). Odkryłem, że jest to lekki sposób na stworzenie strony obsługującej Ajax przy użyciu wyskakujących okienek interfejsu użytkownika jQuery.

Catharz
źródło
1

Jakim typem zmiennej jest format?

Z Java POV format jest implementacją anonimowego interfejsu. Ten interfejs ma jedną metodę nazwaną dla każdego typu MIME. Kiedy wywołujesz jedną z tych metod (przekazując mu blok), to jeśli szyny czują, że użytkownik chce tego typu treści, wówczas wywoła twój blok.

Rzecz w tym, że ten anonimowy obiekt kleju w rzeczywistości nie implementuje interfejsu - dynamicznie łapie wywołania metody i sprawdza, czy jest to nazwa typu MIME, o którym wie.

Osobiście uważam, że wygląda to dziwnie: blok, który przekazujesz, jest wykonywany . Bardziej sensowne byłoby dla mnie przekazanie skrótu etykiet formatu i bloków. Ale - tak to wygląda w RoR.

PaulMurrayCbr
źródło
0

„Format” to typ odpowiedzi. Może to być na przykład json lub html. Jest to format wyników, które otrzyma gość.

Rafael Nascimento
źródło
0

Jest jeszcze jedna rzecz, o której powinieneś wiedzieć - MIME.

Jeśli potrzebujesz użyć typu MIME i nie jest on domyślnie obsługiwany, możesz zarejestrować własne programy obsługi w config / initializers / mime_types.rb:

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


źródło