Rails:respond_to块如何工作?

时间:2012-02-29 01:28:05

标签: ruby-on-rails

我正在浏览Getting Started with 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的{​​{3}}没有回答这个问题。

9 个答案:

答案 0 :(得分:174)

我是Ruby新手并且遇到了同样的代码。我挂起来的部分比我在这里找到的一些答案更为基础。这可能会或可能不会帮助某人。

  • respond_to是超类ActionController上的一种方法。
  • 它需要一个块,就像一个委托。该块从doend|format|作为块的参数。
  • respond_to执行您的阻止,将响应者传递给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所追求的目标。

P.S。请注意,在RoR 4.2中respond_to被提取到responders gem。

答案 1 :(得分:100)

这是一个利用Rails辅助方法的Ruby代码块。如果您还不熟悉块,您将在Ruby中看到它们。

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

示例中的块是格式化数据 - 通过传入块中的“格式”参数 - 只要浏览器发出html或json数据请求,就会从控制器发送到视图。

如果您在本地计算机上并且设置了Post支架,则可以转到http://localhost:3000/posts,您将看到html格式的所有帖子。但是,如果您键入:http://localhost:3000/posts.json,那么您将看到从服务器发送的json对象中的所有帖子。

这对于制作需要从服务器来回传递json的javascript繁重的应用程序非常方便。如果你愿意,你可以轻松地在你的rails后端创建一个json api,并且只传递一个视图 - 就像你的Post控制器的索引视图一样。然后你可以使用像JqueryBackbone(或两者)这样的javascript库来操作数据并创建自己的界面。这些被称为异步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'部分并遍历所有'用户'对象:

<强> index.html.erb

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

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

您可能会发现此博文有用: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

答案 2 :(得分:10)

据我所知,respond_to是一个附加到ActionController的方法,因此您可以在每个控制器中使用它,因为它们都是从ActionController继承的。这是Rails的respond_to方法:

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

你传给block,就像我在这里展示的那样:

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

| format | 部分是块期望的参数,因此在respond_to方法中我们可以使用它。怎么样?

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

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

答案 3 :(得分:7)

  

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

为了理解format是什么,您可以先查看respond_to的来源,但很快就会发现您需要查看的内容是{{3}的代码}}

从这里开始,您将看到传递给respond_to(在您的代码中)的块实际上被调用并通过retrieve_response_from_mimes的实例传递(块中的块被引用为format)。收集器基本上根据Collector rails知道的内容生成方法(我相信Rails启动)。

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

答案 4 :(得分:2)

响应者注册背后的元编程(参见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)。我发现使用jQuery UI弹出窗口创建一个支持Ajax的站点是一种轻量级方法。

答案 5 :(得分:1)

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

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

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

答案 6 :(得分:1)

  

什么类型的变量是格式?

从Java POV,格式是匿名接口的实现。此接口有一个为每个mime类型命名的方法。当你调用其中一个方法(传递一个块)时,如果rails认为用户想要那个内容类型,那么它将调用你的块。

当然,扭曲的是这个匿名粘合对象实际上并没有实现一个接口 - 它动态捕获方法调用,如果它是它知道的mime类型的名称,则可以解决。

就我个人而言,我觉得它看起来很奇怪:你传入的块是执行。传递格式标签和块的散列对我来说更有意义。但是 - 看来它就是在RoR中完成的。

答案 7 :(得分:1)

还有一件事需要注意 - MIME。

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

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

答案 8 :(得分:0)

“格式”是您的回复类型。例如,可以是json或html。这是访问者将收到的输出格式。