为什么我会得到“未定义的方法'model_name'”?

时间:2016-05-17 12:38:48

标签: ruby-on-rails ruby ruby-on-rails-4

我正在做一个小实验并试图从控制器的动作中提取一些逻辑。

我有这样的代码:

控制器

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存储库中的位置,请指出它。

提前致谢。 :)

1 个答案:

答案 0 :(得分:2)

这是因为您在@user的实例上而不是在控制器实例(RenderService)上设置@obj实例变量。

render在模板中创建ActionView::Baseself)的实例,然后将控制器的实例变量复制到视图实例。由于未在控制器实例上设置@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的调用,控制器告诉它查看它有哪些实例变量。