respond_to块如何工作?

我正在浏览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::ClassMethods::respond_to

没有回答问题。

129829 次浏览

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

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

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

如果你在你的本地机器上,你已经设置了Post脚手架,你可以去http://localhost:3000/posts,你会看到你所有的html格式的Post。但是,如果你输入这个:http://localhost:3000/posts.json,那么你会看到你所有的帖子在一个json对象中从服务器发送。

这对于那些需要从服务器来回传递json的javascript应用程序来说非常方便。如果你愿意,你可以很容易地在你的rails后端创建一个json api,并且只传递一个视图——比如Post控制器的索引视图。然后你可以使用javascript库,如Jquery骨干(或两者都有)来操作数据和创建自己的界面。它们被称为异步ui,它们变得非常流行(Gmail是其中之一)。它们非常快,给最终用户在网络上一个更像桌面的体验。当然,这只是格式化数据的一个好处。

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

据我所知,respond_to是一个附加到ActionController的方法,所以你可以在每个单独的控制器中使用它,因为它们都继承自ActionController。下面是Rails的respond_to方法:

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

你正在给它传递一个,就像我在这里显示的:

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

格式| |部分是块所期望的参数,所以在respond_to方法中我们可以使用它。如何?

如果你注意到我们通过了带前缀&在respond_to方法中,我们这样做是为了将该块视为Proc。由于参数有“.xml”,“.html”,我们可以使用它们作为要调用的方法。

我们在respond_to类中所做的基本上是调用Responder类实例的方法“.html, .xml, .json”。

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

为了理解format是什么,你可以先看看respond_to的源代码,但很快你就会发现你真正需要看的是retrieve_response_from_mimes的代码。

从这里,你将看到传递给respond_to的块(在你的代码中)实际上是调用和传递收集器的实例(在块中被引用为format)。收集器基本上生成方法(我相信在Rails启动时)基于mime类型 Rails所知道的。

所以,是的,.html.json是在Collector(又名format)类上定义的方法(在运行时)。

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

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

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

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

  • respond_to是父类ActionController上的一个方法。
  • 它需要一个block,就像一个委托。该块从doend,其中|format|作为块的参数。
  • respond_to执行你的块,将Responder传递给format参数。

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

  • Responder不包含用于.html.json的方法,但我们无论如何都会调用这些方法!这部分让我大吃一惊。
  • Ruby有一个叫做method_missing的特性。如果你调用一个不存在的方法(比如jsonhtml), 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)

这部分把我搞糊涂了。我仍然觉得这很不直观。Ruby似乎经常使用这种技术。整个类(responder)成为方法实现。为了利用method_missing,我们需要类的一个实例,所以我们必须传递一个回调,他们将类方法对象传递到这个回调中。对于一个用c类语言写了20多年代码的人来说,这对我来说是非常落后和不直观的。倒不是说它不好!但这是很多有这种背景的人需要了解的,我认为这可能是OP的目的。

附注:在RoR 4.2中,respond_to被提取到急救员 gem中。

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

格式是什么类型的变量?

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

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

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

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

还有一件事你应该知道- MIME。

如果你需要使用MIME类型,并且默认不支持,你可以在config/initializers/mime_types.rb中注册你自己的处理程序:

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