我正在做一个小实验并试图从控制器的动作中提取一些逻辑。
我有这样的代码:
控制器
def create
@user = User.new(user_params)
if @user.save
redirect_to user_url(@user), notice: 'Welcome to MyApp'
else
render :new
end
end
尝试创建这样的东西:
控制器
def create
user_service.display
end
private
def user_service
RenderService.new(self)
end
单独的服务对象
require 'forwardable'
class RenderService
extend Forwardable
delegate [:params, :user_url, :redirect_to, :render] => :@obj
attr_reader :obj, :user
def initialize(obj)
@obj = obj
@user = User.new(user_params)
end
def display
redirect_to(user_url(user), notice: 'Welcome to MyApp') if user.save
render(:new) unless user.save
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :confirmation)
end
end
创建新用户的表单
= simple_form_for(@user, html: { }) do |form|
= form.input :name, required: true
= form.input :email, required: true
= form.input :password, required: true
= form.input :confirmation, required: true
= form.submit 'Create my account', class: 'btn btn-primary'
正如您所看到的,我尝试做的就是将逻辑封装到一个单独的类中,但由于某种原因我从ActionView::Template::Error
收到错误,错误显示为undefined method 'model_name' for nil:NilClass
。
我真的不明白为什么它不起作用。对我来说,看起来我发送相同的消息到同一个对象,它应该工作得很好,但事实并非如此。
第二个问题:为什么ActionView
涉及到这里?
感谢您的任何解释。
哦,顺便说一下,如果您知道负责此类行为的代码以及它在Rails存储库中的位置,请指出它。
提前致谢。 :)
答案 0 :(得分:2)
这是因为您在@user
的实例上而不是在控制器实例(RenderService
)上设置@obj
实例变量。
render
在模板中创建ActionView::Base
(self
)的实例,然后将控制器的实例变量复制到视图实例。由于未在控制器实例上设置@user
,因此永远不会复制它,也不会在视图中设置此类变量。视图在不存在的model_name
实例变量上调用@user
,您会看到错误。
您应该可以通过在控制器实例上设置@user
来修复它。而不是
@user = User.new(user_params)
DO
@obj.instance_variable_set :@user, User.new(user_params)
很难完全描述当Rails控制器呈现视图时会发生什么,因为有很多子类化和重写方法,所以如果你真的想要了解它,我建议你在render
调用一个调试器。最后,您将转到ActionView::Rendering#_render_template
,然后#view_context
。在那里你会看到对#view_assigns
的调用,控制器告诉它查看它有哪些实例变量。