简体   繁体   English

Rails:respond_to 块是如何工作的?

[英]Rails: How does the respond_to block work?

I'm going through the Getting Started with Rails guide and got confused with section 6.7.我正在阅读Rails 入门指南,但对 6.7 节感到困惑。 After generating a scaffold I find the following auto-generated block in my controller:生成脚手架后,我在控制器中找到以下自动生成的块:

def index
  @posts = Post.all

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

I'd like to understand how the respond_to block actually works.我想了解 response_to 块是如何实际工作的。 What type of variable is format?什么类型的变量是格式? Are .html and .json methods of the format object?是格式对象的 .html 和 .json 方法吗? The documentation for文档

ActionController::MimeResponds::ClassMethods::respond_to

doesn't answer the question.不回答问题。

I am new to Ruby and got stuck at this same code.我是 Ruby 的新手,并被困在相同的代码中。 The parts that I got hung up on were a little more fundamental than some of the answers I found here.我挂断的部分比我在这里找到的一些答案更基本一点。 This may or may not help someone.这可能会或可能不会帮助某人。

  • respond_to is a method on the superclass ActionController . respond_to是超类ActionController上的一个方法。
  • it takes a block, which is like a delegate.它需要一个块,就像一个委托。 The block is from do until end , with |format|块是从doend ,带|format| as an argument to the block.作为块的参数。
  • respond_to executes your block, passing a Responder into the format argument. response_to 执行你的块,将一个 Responder 传递到format参数中。

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

  • The Responder does NOT contain a method for .html or .json , but we call these methods anyways! Responder不包含.html.json的方法,但我们仍然调用这些方法! This part threw me for a loop.这部分让我陷入了困境。
  • Ruby has a feature called method_missing . Ruby 有一个名为method_missing的特性。 If you call a method that doesn't exist (like json or html ), Ruby calls the method_missing method instead.如果你调用一个不存在的方法(比如jsonhtml ),Ruby 会调用method_missing方法。

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

  • The Responder class uses its method_missing as a kind of registration. Responder类使用它的method_missing作为一种注册。 When we call 'json', we are telling it to respond to requests with the .json extension by serializing to json.当我们调用 'json' 时,我们告诉它通过序列化为 json 来响应带有 .json 扩展名的请求。 We need to call html with no arguments to tell it to handle .html requests in the default way (using conventions and views).我们需要不带参数调用html来告诉它以默认方式(使用约定和视图)处理 .html 请求。

It could be written like this (using JS-like pseudocode):可以这样写(使用类似 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)

This part confused the heck out of me.这部分让我很困惑。 I still find it unintuitive.我仍然觉得它不直观。 Ruby seems to use this technique quite a bit. Ruby 似乎相当多地使用这种技术。 The entire class ( responder ) becomes the method implementation.整个类( responder )成为方法实现。 In order to leverage method_missing , we need an instance of the class, so we're obliged to pass a callback into which they pass the method-like object.为了利用method_missing ,我们需要一个类的实例,所以我们必须传递一个回调,他们将类方法对象传递到其中。 For someone who has coded in C-like languages for 20 some years, this is very backwards and unintuitive to me.对于使用类 C 语言编码 20 年的人来说,这对我来说是非常落后和不直观的。 Not that it's bad!不是说不好! But it's something a lot of people with that kind of background need to get their head around, and I think might be what the OP was after.但这是很多具有这种背景的人需要了解的事情,我认为这可能是 OP 所追求的。

ps note that in RoR 4.2 respond_to was extracted into responders gem. ps 请注意,在 RoR 4.2 中respond_to被提取到响应者gem 中。

This is a block of Ruby code that takes advantage of a Rails helper method.这是一个利用 Rails 辅助方法的 Ruby 代码块。 If you aren't familiar with blocks yet, you will see them a lot in Ruby.如果您还不熟悉块,您会在 Ruby 中看到很多。

respond_to is a Rails helper method that is attached to the Controller class (or rather, its super class). respond_to是一个附加到 Controller 类(或者更确切地说,它的超类)的 Rails 辅助方法。 It is referencing the response that will be sent to the View (which is going to the browser).它引用了将发送到视图(将发送到浏览器)的响应。

The block in your example is formatting data - by passing in a 'format' paramater in the block - to be sent from the controller to the view whenever a browser makes a request for html or json data.您示例中的块是格式化数据 - 通过在块中传递一个“格式”参数 - 每当浏览器请求 html 或 json 数据时,就会从控制器发送到视图。

If you are on your local machine and you have your Post scaffold set up, you can go to http://localhost:3000/posts and you will see all of your posts in html format.如果您在本地机器上并设置了 Post 脚手架,您可以访问http://localhost:3000/posts ,您将看到所有 html 格式的帖子。 But, if you type in this: http://localhost:3000/posts.json , then you will see all of your posts in a json object sent from the server.但是,如果您输入: http://localhost:3000/posts.json ,那么您将在从服务器发送的 json 对象中看到您的所有帖子。

This is very handy for making javascript heavy applications that need to pass json back and forth from the server.这对于制作需要从服务器来回传递 json 的 javascript 重度应用程序非常方便。 If you wanted, you could easily create a json api on your rails back-end, and only pass one view - like the index view of your Post controller.如果您愿意,您可以轻松地在 Rails 后端创建一个 json api,并且只传递一个视图 - 就像您的 Post 控制器的索引视图。 Then you could use a javascript library like Jquery or Backbone (or both) to manipulate data and create your own interface.然后你可以使用像JqueryBackbone (或两者)这样的 javascript 库来操作数据并创建你自己的界面。 These are called asynchronous UIs and they are becomming really popular (Gmail is one).这些被称为异步 UI ,它们变得非常流行(Gmail 就是其中之一)。 They are very fast and give the end-user a more desktop-like experience on the web.它们速度非常快,可为最终用户提供更类似于桌面的 Web 体验。 Of course, this is just one advantage of formatting your data.当然,这只是格式化数据的优势之一。

The Rails 3 way of writing this would be this: Rails 3 的写法是这样的:

    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

By putting respond_to :html, :xml, :json at the top of the class, you can declare all the formats that you want your controller to send to your views.通过将respond_to :html, :xml, :json放在类的顶部,您可以声明您希望控制器发送到您的视图的所有格式。

Then, in the controller method, all you have to do is respond_with(@whatever_object_you_have)然后,在控制器方法中,您所要做的就是 respond_with(@whatever_object_you_have)

It just simplifies your code a little more than what Rails auto-generates.与 Rails 自动生成的相比,它只是简化了您的代码。

If you want to know about the inner-workings of this ...如果你想知道这个的内部运作......

From what I understand, Rails introspects the objects to determine what the actual format is going to be.据我所知,Rails 会内省对象以确定实际的格式。 The 'format' variables value is based on this introspection. “格式”变量值基于此内省。 Rails can do a whole lot with a little bit of info. Rails 可以用一点点信息做很多事情。 You'd be surprised at how far a simple @post or :post will go.你会惊讶于一个简单的 @post 或 :post 能走多远。

For example, if I had a _user.html.erb partial file that looked like this:例如,如果我有一个看起来像这样的 _user.html.erb 部分文件:

_user.html.erb _user.html.erb

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

Then, this alone in my index view would let Rails know that it needed to find the 'users' partial and iterate through all of the 'users' objects:然后,仅在我的索引视图中就可以让 Rails 知道它需要找到“用户”部分并遍历所有“用户”对象:

index.html.erb index.html.erb

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

would let Rails know that it needed to find the 'user' partial and iterate through all of the 'users' objects:会让 Rails 知道它需要找到“用户”部分并遍历所有“用户”对象:

You may find this blog post useful: http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with您可能会发现这篇博文很有用: http : //archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

You can also peruse the source: https://github.com/rails/rails您还可以仔细阅读源代码: https : //github.com/rails/rails

From what I know, respond_to is a method attached to the ActionController, so you can use it in every single controller, because all of them inherits from the ActionController.据我所知,respond_to 是一个附加到 ActionController 的方法,所以你可以在每个控制器中使用它,因为它们都继承自 ActionController。 Here is the Rails respond_to method:这是 Rails 的 response_to 方法:

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

You are passing it a block , like I show here:你正在传递一个block ,就像我在这里展示的那样:

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

The |format| |格式| part is the argument that the block is expecting, so inside the respond_to method we can use that. part 是块所期望的参数,所以在 respond_to 方法中我们可以使用它。 How?如何?

Well, if you notice we pass the block with a prefixed & in the respond_to method, and we do that to treat that block as a Proc.好吧,如果您注意到我们在 respond_to 方法中传递了带有前缀 & 的块,我们这样做是为了将该块视为 Proc。 Since the argument has the ".xml", ".html" we can use that as methods to be called.由于参数具有“.xml”、“.html”,我们可以将其用作要调用的方法。

What we basically do in the respond_to class is call methods ".html, .xml, .json" to an instance of a Responder class.我们在 respond_to 类中所做的基本上是调用方法“.html、.xml、.json”到 Responder 类的实例。

I'd like to understand how the respond_to block actually works.我想了解 response_to 块是如何实际工作的。 What type of variable is format?什么类型的变量是格式? Are .html and .json methods of the format object?是格式对象的 .html 和 .json 方法吗?

In order to understand what format is, you could first look at the source for respond_to , but quickly you'll find that what really you need to look at is the code for retrieve_response_from_mimes .为了了解什么是format ,您可以先查看respond_to的源respond_to ,但很快您就会发现真正需要查看的是retrieve_response_from_mimes的代码。

From here, you'll see that the block that was passed to respond_to (in your code), is actually called and passed with an instance of Collector (which within the block is referenced as format ).从这里,您将看到传递给respond_to (在您的代码中)的块实际上是通过一个Collector实例调用和传递的(在块中被引用为format )。 Collector basically generates methods (I believe at Rails start-up) based on what mime types rails knows about.收集器基本上根据 rails 知道的mime 类型生成方法(我相信在 Rails 启动时)。

So, yes, the .html and .json are methods defined (at runtime) on the Collector (aka format ) class.所以,是的, .html.json是在 Collector(又名format )类上定义(在运行时)的方法。

The meta-programming behind responder registration (see Parched Squid's answer) also allows you to do nifty stuff like this:响应者注册背后的元编程(参见 Parched Squid 的回答)也允许你做这样的漂亮事情:

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

The csv line will cause to_csv to be called on each post when you visit /posts.csv.当您访问 /posts.csv 时,csv 行将导致在每个帖子上调用 to_csv。 This makes it easy to export data as CSV (or any other format) from your rails site.这使得从 Rails 站点将数据导出为 CSV(或任何其他格式)变得容易。

The js line will cause a javascript file /posts.js (or /posts.js.coffee) to be rendered/executed. js 行将导致呈现/执行 javascript 文件 /posts.js(或 /posts.js.coffee)。 I've found that to be a light-weight way to create an Ajax enabled site using jQuery UI pop-ups.我发现这是一种使用 jQuery UI 弹出窗口创建支持 Ajax 的站点的轻量级方法。

This is a little outdated, by Ryan Bigg does a great job explaining this here:这有点过时了,Ryan Bigg 在这里做了很好的解释:

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

In fact, it might be a bit more detail than you were looking for.事实上,它可能比您所寻找的要详细一些。 As it turns out, there's a lot going on behind the scenes, including a need to understand how the MIME types get loaded.事实证明,幕后有很多事情要做,包括需要了解 MIME 类型是如何加载的。

What type of variable is format?什么类型的变量是格式?

From a java POV, format is an implemtation of an anonymous interface.从 Java POV 来看,格式是匿名接口的实现。 This interface has one method named for each mime type.这个接口有一个为每种 mime 类型命名的方法。 When you invoke one of those methods (passing it a block), then if rails feels that the user wants that content type, then it will invoke your block.当您调用这些方法之一(向其传递一个块)时,如果 rails 认为用户需要该内容类型,那么它就会调用您的块。

The twist, of course, is that this anonymous glue object doesn't actually implement an interface - it catches the method calls dynamically and works out if its the name of a mime type that it knows about.当然,扭曲的是这个匿名胶水对象实际上并没有实现一个接口——它动态地捕捉方法调用并计算它是否是它知道的 mime 类型的名称。

Personally, I think it looks weird: the block that you pass in is executed .就个人而言,我认为它看起来很奇怪:您传入的块被执行 It would make more sense to me to pass in a hash of format labels and blocks.传递格式标签和块的散列对我来说更有意义。 But - that's how its done in RoR, it seems.但是 - 它似乎是在 RoR 中完成的。

"Format" is your response type. “格式”是您的响应类型。 Could be json or html, for example.例如,可以是 json 或 html。 It's the format of the output your visitor will receive.这是您的访问者将收到的输出格式。

There is one more thing you should be aware of - MIME.您还应该注意一件事 - MIME。

If you need to use a MIME type and it isn't supported by default, you can register your own handlers in config/initializers/mime_types.rb:如果您需要使用 MIME 类型并且默认情况下不支持它,您可以在 config/initializers/mime_types.rb 中注册您自己的处理程序:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM