了解 Rails 3 的 respond_with

发布于 2024-10-07 12:03:48 字数 1878 浏览 3 评论 0原文

利用 ActionController 的新 respond_with 方法...它如何确定当操作(保存)成功和不成功时渲染什么?

我问这个问题是因为我试图让脚手架生成的规范(包括在下面)通过,如果只是为了让我能够理解它。该应用程序工作正常,但奇怪的是,当验证失败时,它似乎正在渲染 /carriers (至少浏览器的 URL 是这样显示的)。然而,规范期待 "new"(就此而言,我也是如此),但却收到了 <"">。如果我更改规范以期望 "" 它仍然会失败。

当它呈现 /carriers 时,该页面会在验证失败的字段旁边显示 error_messages,正如人们所期望的那样。

任何熟悉 respond_with 的人都可以看到这里发生了什么吗?

#carrier.rb
  validates :name, :presence => true 

#carriers_controller.rb
class CarriersController < ApplicationController
  respond_to :html, :json

...

  def new
    respond_with(@carrier = Carrier.new)
  end

  def create
     @carrier = Carrier.new(params[:carrier])
     flash[:success] = 'Carrier was successfully created.' if @carrier.save
     respond_with(@carrier) 
  end

失败的规范:

#carriers_controller_spec.rb
require 'spec_helper'

describe CarriersController do

  def mock_carrier(stubs={})
    (@mock_carrier ||= mock_model(Carrier).as_null_object).tap do |carrier|
      carrier.stub(stubs) unless stubs.empty?
    end
  end


  describe "POST create" do
    describe "with invalid params" do
      it "re-renders the 'new' template" do
        Carrier.stub(:new) { mock_carrier(:save => false) }
        post :create, :carrier => {}
        response.should render_template("new")
      end
    end
  end
end

出现以下错误:

  1) CarriersController POST create with invalid params re-renders the 'new' template
     Failure/Error: response.should render_template("new")
     expecting <"new"> but rendering with <"">.
     Expected block to return true value.
     # (eval):2:in `assert_block'
     # ./spec/controllers/carriers_controller_spec.rb:81:in `block (4 levels) in <top (required)>'

Utilizing ActionController's new respond_with method...how does it determine what to render when action (save) is successful and when it's not?

I ask because I'm trying to get a scaffold generated spec (included below) to pass, if only so that I can understand it. The app is working fine but, oddly, it appears to be rendering /carriers (at least that's what the browser's URL says) when a validation fails. Yet, the spec is expecting "new" (and so am I, for that matter) but instead is receiving <"">. If I change the spec to expect "" it still fails.

When it renders /carriers that page shows the error_messages next to the fields that failed validation as one would expect.

Can anyone familiar with respond_with see what's happening here?

#carrier.rb
  validates :name, :presence => true 

#carriers_controller.rb
class CarriersController < ApplicationController
  respond_to :html, :json

...

  def new
    respond_with(@carrier = Carrier.new)
  end

  def create
     @carrier = Carrier.new(params[:carrier])
     flash[:success] = 'Carrier was successfully created.' if @carrier.save
     respond_with(@carrier) 
  end

Spec that's failing:

#carriers_controller_spec.rb
require 'spec_helper'

describe CarriersController do

  def mock_carrier(stubs={})
    (@mock_carrier ||= mock_model(Carrier).as_null_object).tap do |carrier|
      carrier.stub(stubs) unless stubs.empty?
    end
  end


  describe "POST create" do
    describe "with invalid params" do
      it "re-renders the 'new' template" do
        Carrier.stub(:new) { mock_carrier(:save => false) }
        post :create, :carrier => {}
        response.should render_template("new")
      end
    end
  end
end

with this error:

  1) CarriersController POST create with invalid params re-renders the 'new' template
     Failure/Error: response.should render_template("new")
     expecting <"new"> but rendering with <"">.
     Expected block to return true value.
     # (eval):2:in `assert_block'
     # ./spec/controllers/carriers_controller_spec.rb:81:in `block (4 levels) in <top (required)>'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

淡淡绿茶香 2024-10-14 12:03:48

tl:dr

将错误哈希添加到模拟中:

Carrier.stub(:new) { mock_carrier(:save => false, 
                       :errors => { :anything => "any value (even nil)" })}

这将触发 respond_with 中所需的行为。

这里发生了什么

post :create 之后添加此内容

response.code.should == "200"

它会失败,expected: "200", got: "302"。因此,它在不应该的情况下进行重定向,而不是渲染新模板。它要去哪里?给它一个我们知道会失败的路径:

response.should redirect_to("/")

现在它失败了,预期响应是重定向到;但重定向到

该规范应该通过呈现 new 模板来传递,这是之后事件的正常过程模拟 Carrier 对象上的 save 返回 false。相反,respond_with 最终重定向到show_carrier_path。这完全是错误的。但为什么?

在深入研究源代码后,控制器似乎尝试渲染“Carriers/create”。没有这样的模板,因此引发异常。救援块确定请求是 POST 并且错误哈希中没有任何内容,控制器将重定向到默认资源,即模拟 Carrier。

这是令人费解的,因为控制器不应该假设存在有效的模型实例。毕竟这是一个创建。此时我只能猜测测试环境在某种程度上走了捷径。

因此,解决方法是提供一个虚假的错误哈希值。通常,在 save 失败后,哈希中会有一些东西,所以这是有道理的。

tl:dr

Add an error hash to the mock:

Carrier.stub(:new) { mock_carrier(:save => false, 
                       :errors => { :anything => "any value (even nil)" })}

This will trigger the desired behavior in respond_with.

What is going on here

Add this after the post :create

response.code.should == "200"

It fails with expected: "200", got: "302". So it is redirecting instead of rendering the new template when it shouldn't. Where is it going? Give it a path we know will fail:

response.should redirect_to("/")

Now it fails with Expected response to be a redirect to <http://test.host/> but was a redirect to <http://test.host/carriers/1001>

The spec is supposed to pass by rendering the new template, which is the normal course of events after the save on the mock Carrier object returns false. Instead respond_with ends up redirecting to show_carrier_path. Which is just plain wrong. But why?

After some digging in the source code, it seems that the controller tries to render 'carriers/create'. There is no such template, so an exception is raised. The rescue block determines the request is a POST and there is nothing in the error hash, upon which the controller redirects to the default resource, which is the mock Carrier.

That is puzzling, since the controller should not assume there is a valid model instance. This is a create after all. At this point I can only surmise that the test environment is somehow taking shortcuts.

So the workaround is to provide a fake error hash. Normally something would be in the hash after save fails, so that kinda makes sense.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文