Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails undefined method `model_name' for NilClass:Class

I have a form that i want to appear at the top of every page so i've included it in the /app/views/layouts/application.html.erb file and i get the error undefined methodmodel_name' for NilClass:Class` when trying to load the page.

Here's the form snippet in application.html.erb

 <%= form_for @user do |f| %>
    <h3>Add new contact</h3>
    <%= f.text_field :first_name %><br />
    <%= f.text_field :last_name %>
    <%= f.text_field :email %>
    <hr />
    <%= f.submit "Add Contact" %>
<% end %>

Here's my /app/controllers/user_controller.rb

class UserController < ApplicationController

    def index

    end

    def new
        @user = User.new
    end

end

I'm thinking that i'm hitting this error because since the form is in the application.html.erb file, i need to somehow specify the path, but then again i'm very new to rails.

Let me know if you need anything else posted.

EDIT Following Ola's suggestion, In my app/views/layouts/application.html.erb file I have something like this:

<div>
<%= yield :add_user %>
</div>

and in my app/views/users/new.html.erb file I have this

<% content_for :add_user do %>
    <%= form_for @user do |f| %>
        <h3>Add new contact</h3>
        First Name<br />
        <%= f.text_field :first_name %><br />
        Last Name<br />
        <%= f.text_field :last_name %><br />
        Email<br />
        <%= f.text_field :email %>
        <hr />
        <%= f.submit "Add Contact" %>
    <% end %>
<% end %>

The form is not rendered because my url is http://localhost:3000/admin/index and so it's looking for the add_user content_for in app/views/admin/index.html.erb

like image 233
Catfish Avatar asked Jun 19 '12 03:06

Catfish


2 Answers

If the form is included for multiple actions (pages) you need to set @user to something or the form_for directive will fail (it won't have anything to create a form for).

A much better approach (and the Rails default) is to have separate views for each action, each including only the elements required by that action, and loaded into your application.html.erb layout with <%= yield %>. So you would have a app/views/users/new.html.erb view which has the form in it. You never very rarely need to define any load paths in Rails, they are all derived from the model, controller and action names - or from your routes.rb. This is a core part of the convention over configuration paradigm which runs so deep in Rails.

Edit: If you do need to have a form for creating a new object on every page (often used for UserSessions for example), you can rewrite your form_for so that it doesn't depend on an object being present:

form_for(User.new)

or if you need to force it to post to the create method

form_for(User.new, :url => { :action => "create" }) 

You can read more about resource driven form_for in the Ruby On Rails API docs.

like image 145
Ola Tuvesson Avatar answered Sep 20 '22 18:09

Ola Tuvesson


Did you try debugging first? I ask because you say you are new to Rails. If not, here is my approach. I want to share it, because I'm surprised how many people don't (for whatever reason) debug carefully.

(I created a test app so that I could walk through your problem. First, I visited http://localhost:3000/users.)

First, look at the stack trace.

NoMethodError in Users#index

Showing /Users/david/dev/testapp/app/views/layouts/application.html.erb where line #11 raised:

undefined method `model_name' for NilClass:Class Extracted source (around line #11):

8: </head>
9: <body>
10: 
11: <%= form_for @user do |f| %>
12:     <h3>Add new contact</h3>
13:     <%= f.text_field :first_name %><br />
14:     <%= f.text_field :last_name %>

This tells you line 11 in your code is the problem. Think -- what could be the issue? Routing is unlikely because you are just rendering the page. Not a syntax error, that would have triggered another error. Ok, so keep reading:

Rails.root: /Users/david/dev/testapp

Application Trace | Framework Trace | Full Trace

Now click "framework trace" and you will see:

activemodel (3.2.6) lib/active_model/naming.rb:163:in model_name_from_record_or_class' activemodel (3.2.6) lib/active_model/naming.rb:158:inparam_key' actionpack (3.2.6) lib/action_view/helpers/form_helper.rb:369:in `form_for'

You may not understand all this -- few people do -- but you will see the error comes from model_name_from_record_or_class which comes from param_key which comes from form_for. That is a big clue!

I recommend the use of the open_gem gem to make it easy to dig into source code:

gem install open_gem

Now, take a look at activemodel, since it is the topmost gem in the stack trace:

gem open activemodel

Now, search for this with regular expression searching enabled:

def .*model_name_from_record_or_class

(Why? you want to find class methods -- e.g. definitions that start with self as well.)

This will take you to line 162 in naming.rb.

def self.model_name_from_record_or_class(record_or_class)
  (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
end

Even without understanding all of this, it does give a big hint... the method tries to return the model name from the record or class. Why can't it? Because the parameter you are passing, @user, hasn't been set. The value of unset instance variables is nil, so it won't complain until you try to do something with it.

I hope this helps for this question and for future ones.

like image 40
David J. Avatar answered Sep 23 '22 18:09

David J.