了解 Rails 3 的 respond_with
利用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
tl:dr
将错误哈希添加到模拟中:
这将触发
respond_with
中所需的行为。这里发生了什么
在
post :create
之后添加此内容它会失败,
expected: "200", got: "302"
。因此,它在不应该的情况下进行重定向,而不是渲染新模板。它要去哪里?给它一个我们知道会失败的路径:现在它失败了,
预期响应是重定向到;但重定向到
该规范应该通过呈现
new
模板来传递,这是之后事件的正常过程模拟 Carrier 对象上的save
返回 false。相反,respond_with
最终重定向到show_carrier_path
。这完全是错误的。但为什么?在深入研究源代码后,控制器似乎尝试渲染“Carriers/create”。没有这样的模板,因此引发异常。救援块确定请求是 POST 并且错误哈希中没有任何内容,控制器将重定向到默认资源,即模拟 Carrier。
这是令人费解的,因为控制器不应该假设存在有效的模型实例。毕竟这是一个
创建
。此时我只能猜测测试环境在某种程度上走了捷径。因此,解决方法是提供一个虚假的错误哈希值。通常,在
save
失败后,哈希中会有一些东西,所以这是有道理的。tl:dr
Add an error hash to the mock:
This will trigger the desired behavior in
respond_with
.What is going on here
Add this after the
post :create
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: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 thesave
on the mock Carrier object returns false. Insteadrespond_with
ends up redirecting toshow_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.