Nikita  Luparev Nikita Luparev - 1 year ago 48
Ruby Question

Why do I get "undefined method 'model_name' "?

I'm doing a small experiment and trying to extract some logic from controller's action.

I have code like this:


def create
@user =
redirect_to user_url(@user), notice: 'Welcome to MyApp'
render :new

And try to create something like this:


def create


def user_service

Separate Service object

require 'forwardable'

class RenderService
extend Forwardable
delegate [:params, :user_url, :redirect_to, :render] => :@obj

attr_reader :obj, :user
def initialize(obj)
@obj = obj
@user =

def display
redirect_to(user_url(user), notice: 'Welcome to MyApp') if
render(:new) unless


def user_params
params.require(:user).permit(:name, :email, :password, :confirmation)

Form for creating new user

= 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'

As you can see all I tried to do is to encapsulate logic into a separate class, but I got an error from
for some reason and the error says
undefined method 'model_name' for nil:NilClass

I really don't understand why it does not work. To me it looks like I sent the same message to the same object and it should work just fine, but it doesn't.

Second question: why is
involved here?

Thank you for any explanation.

Oh, by the way, if you know the code responsible for such behavior and where it lives in the Rails repository please point me to it.

Thanks in advance. :)

Answer Source

This happens because you're setting the @user instance variable on the instance of RenderService rather than on the controller instance (@obj).

render creates an instance of ActionView::Base (self in a template) and then copies the controller's instance variables to the view instance. Since @user isn't set on the controller instance, it's never copied and no such variable is set in the view. The view calls model_name on the nonexistent @user instance variable and you get the error you saw.

You should be able to fix it by setting @user on the controller instance. Instead of

@user =


@obj.instance_variable_set :@user,

It's difficult to fully describe what happens when a Rails controller renders a view, because there is a lot of subclassing and overridden methods, so if you really want to understand it I recommend stepping through a render call in a debugger. Eventually you'll get to ActionView::Rendering#_render_template and then #view_context. There you'll see the call to #view_assigns, where the controller tells the view what instance variables it has.