Rails:respond_to块如何工作?
我正在阅读Rails入门指南,并与6.7节混淆。 生成一个脚手架后,我在我的控制器中find以下自动生成的块:
def index @posts = Post.all respond_to do |format| format.html # index.html.erb format.json { render :json => @posts } end end
我想了解respond_to块如何实际工作。 什么types的variables是格式? 格式对象的.html和.json方法? ActionController::MimeResponds::ClassMethods::respond_to
的文档不回答问题。
我是Ruby的新手,被困在这个相同的代码中。 我挂断的部分比我在这里find的一些答案更重要。 这可能会或可能不会帮助某人。
-
respond_to
是超类ActionController
一个方法。 - 它需要一个块,就像一个代表。 该块从
do
end
,用|format|
作为块的一个参数。 - respond_to执行你的block,把一个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)
这个部分混淆了我。 我仍然觉得它不直观。 Ruby似乎使用这种技术相当多。 整个类( responder
)成为方法实现。 为了利用method_missing
,我们需要一个类的实例,所以我们必须传递一个callback函数来传递方法类对象。 对于用C语言编写了20年的人来说,这是非常倒退和不直观的。 不是这样不好! 但是,这种背景需要很多人的头脑,我认为可能是OP之后的事情。
ps注意在RoR 4.2中respond_to
被提取到响应者的gem中。
这是一个利用Rails帮助器方法的Ruby代码块。 如果你还不熟悉块,你会看到他们在Ruby中很多。
respond_to
是连接到Controller类(或者更确切地说,它的超类)的Rails辅助方法。 它是引用将被发送到视图(这是浏览器)的响应。
在你的例子中的块是格式化数据 – 通过在块中传递'格式'参数 – 从浏览器发出请求的HTML或JSON数据从控制器发送到视图。
如果您在本地计算机上,并且已经设置了Post脚手架,则可以转至http://localhost:3000/posts
然后您将以html格式查看您的所有post。 但是,如果你input这个: http://localhost:3000/posts.json
,那么你将看到从服务器发送的json对象中的所有post。
这对于制作需要从服务器来回传递json的JavaScript重要应用程序非常方便。 如果你愿意,你可以很容易地在你的rails后端创build一个json api,并且只传递一个视图 – 就像你的Post控制器的索引视图。 然后,您可以使用像Jquery或Backbone (或两者)的JavaScript库来操作数据并创build自己的界面。 这些被称为asynchronous用户界面 ,他们正在stream行(Gmail是一个)。 它们速度非常快,可以让最终用户在networking上获得更类似于桌面的体验。 当然,这只是格式化数据的一个优点。
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反思对象以确定实际的格式将会是什么。 “格式”variables值是基于这种反思。 Rails可以用一些信息做很多事情。 你会惊讶于一个简单的@post或:post会走多远。
例如,如果我有一个看起来像这样的_user.html.erb部分文件:
_user.html.erb
<li> <%= link_to user.name, user %> </li>
然后,单独在我的索引视图中就可以让Rails知道它需要find'用户'部分并遍历所有'用户'对象:
index.html.erb
<ul class="users"> <%= render @users %> </ul>
让Rails知道它需要find'用户'部分并遍历所有'用户'对象:
您可能会发现这篇博文很有用: 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的方法,所以你可以在每一个控制器中使用它,因为它们都是从ActionControllerinheritance的。 这里是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类中做的是将方法“.html,.xml,.json”调用到Responder类的实例。
我想了解respond_to块如何实际工作。 什么types的variables是格式? 格式对象的.html和.json方法?
为了理解什么format
,你可以先看看respond_to
的源码,但是很快你会发现你真正需要看的是retrieve_response_from_mimes的代码。
从这里,你会看到传递给respond_to
(在你的代码中)的块实际上是被调用的,并且与一个Collector实例(在块内被引用为format
)一起传递。 收集器基本上生成的方法(我相信在Rails启动)基于什么MIMEtypes轨道知道。
所以,是的, .html
和.json
是Collector(aka format
)类中定义的(运行时)方法。
响应者注册背后的元编程(参见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行会在每个post中调用to_csv。 这使得您可以轻松地从您的rails站点导出数据为CSV(或任何其他格式)。
js行会导致javascript文件/posts.js(或/posts.js.coffee)被渲染/执行。 我发现使用jQuery UIpopup窗口来创build一个支持Ajax的站点是一个轻量级的方法。
什么types的variables是格式?
从java POV中,格式是一个匿名接口的实现。 这个接口有一个为每个MIMEtypes命名的方法。 当你调用其中的一个方法(传递一个块)时,如果rails感觉用户需要这个内容types,那么它会调用你的块。
当然,扭曲的是,这个匿名的glue对象实际上并没有实现一个接口 – 它dynamic地捕获方法调用,如果它知道它的MIMEtypes的名字,就可以解决这个问题。
我个人认为这看起来很奇怪:你传入的块被执行 。 传递格式标签和块的哈希值会更有意义。 但是 – 这似乎是在RoR中完成的。
这是有点过时了,Ryan Bigg在这里做了一个很好的解释:
http://ryanbigg.com/2009/04/how-rails-works-2-mime-types-respond_to/
事实上,它可能比你想要的更详细。 事实certificate,幕后有很多事情要做,包括需要了解如何加载MIMEtypes。
“格式”是您的回应types。 可能是json或html,例如。 这是您的访问者将收到的输出格式。