我正在阅读Rails入门指南,并对第6.7节感到困惑。在生成一个脚手架之后,我在我的控制器中发现了以下自动生成的块:

def index
  @posts = Post.all

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

我想了解respond_to块实际上是如何工作的。格式是什么类型的变量?是.html和.json方法的格式对象?的文档

ActionController:: MimeResponds::类方法::respond_to

没有回答问题。


当前回答

格式是什么类型的变量?

从java POV来看,format是一种匿名接口的实现。这个接口有一个为每种mime类型命名的方法。当您调用其中一个方法(传递一个块)时,如果rails认为用户需要该内容类型,那么它将调用您的块。

当然,这个匿名胶水对象实际上并没有实现接口——它动态地捕获方法调用,并计算出它所知道的mime类型的名称。

就我个人而言,我认为这看起来很奇怪:你传入的块被执行了。对我来说,传递格式标签和块的散列更有意义。但是——这似乎就是RoR的做法。

其他回答

这是一段利用Rails helper方法的Ruby代码。如果你还不熟悉block,你会在Ruby中看到很多。

respond_to是一个附加到Controller类(或者更确切地说,它的超类)的Rails助手方法。它引用将发送到视图(将发送到浏览器)的响应。

示例中的块正在格式化数据——通过在块中传入一个“format”参数——当浏览器请求html或json数据时,这些数据将从控制器发送到视图。

如果你在你的本地机器上,你已经设置了Post脚手架,你可以访问http://localhost:3000/posts,你会看到你所有的文章都是html格式的。但是,如果你输入这个: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. 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. Then you could use a javascript library like Jquery or Backbone (or both) to manipulate data and create your own interface. These are called asynchronous UIs and they are becomming really popular (Gmail is one). They are very fast and give the end-user a more desktop-like experience on the web. Of course, this is just one advantage of formatting your data.

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

通过将respond_to:html,:xml,:json放在类的顶部,你可以声明所有你想要控制器发送到视图的格式。

然后,在控制器方法中,你所要做的就是respond_with(@whatever_object_you_have)

它只是比Rails自动生成的代码简化了一点。

如果你想知道它的内部工作原理…

据我所知,Rails内省对象以确定实际的格式。“format”变量的值就是基于这种自省。Rails可以用一点点信息做很多事情。你会惊讶于一个简单的@post或:post能走多远。

例如,如果我有一个_user.html。Erb的部分文件是这样的:

_user.html.erb

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

然后,在我的索引视图中,这将让Rails知道它需要找到'users'部分,并遍历所有'users'对象:

index.html.erb

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

会让Rails知道它需要找到'user'部分并遍历所有'users'对象:

你可能会发现这篇博客文章很有用:http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with

你也可以阅读原文:https://github.com/rails/rails

响应器注册背后的元编程(参见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

当您访问/posts.csv时,csv行将导致在每个帖子上调用to_csv。这使得从rails站点导出数据为CSV(或任何其他格式)变得很容易。

js行将导致javascript文件/posts.js(或/posts.js.coffee)被呈现/执行。我发现这是一个轻量级的方式来创建一个Ajax支持的网站使用jQuery UI弹出窗口。

这有点过时了,Ryan Bigg在这里做了很好的解释:

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

事实上,它可能比你想要的更详细一些。事实证明,在幕后有很多事情要做,包括需要理解MIME类型是如何加载的。

“格式”是响应类型。例如,可以是json或html。它是访问者将收到的输出的格式。

我是Ruby的新手,在同样的代码上被卡住了。我纠结的部分比我在这里找到的一些答案更基本。这可能会也可能不会帮助到某些人。

respond_to是超类ActionController上的一个方法。 它需要一个block,就像一个委托。块从do到end,以|格式|作为块的参数。 respond_to执行你的块,将Responder传递到format参数中。

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

Responder不包含.html或.json的方法,但我们无论如何都会调用这些方法!这部分让我大吃一惊。 Ruby有一个叫做method_missing的特性。如果你调用一个不存在的方法(如json或html), Ruby会转而调用method_missing方法。

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

Responder类使用method_missing作为一种注册。当我们调用'json'时,我们告诉它通过序列化到json来响应带有.json扩展名的请求。我们需要不带参数地调用html,告诉它以默认方式(使用约定和视图)处理.html请求。

它可以这样写(使用类似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. The entire class (responder) becomes the method implementation. 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. For someone who has coded in C-like languages for 20 some years, this is very backwards and unintuitive to me. 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.

附注:在RoR 4.2中respond_to被提取到responders gem中。