Majoris
Majoris

Reputation: 3189

Ruby - rails - NoMethodError on user model

I am getting this NoMethodError for current_user.user_id. Below is the table of my users model. I don't know what that error means, since current_user.user_id exists. What could cause this issue? I am lost here. As far as I remember I wasn't getting this error, I kept of adding new things to the project and somewhere I got messed up. though I was making changes that were not related to user mode. after why NoMethodError if current_user.user_id is there? Clueless and confused.

Error -

Started GET "/item/list" for 10.10.10.10 at 2012-05-01 23:19:19 -0400
Processing by Item#list as */*
Completed 500 Internal Server Error in 0ms

NoMethodError (undefined method `user_id' for nil:NilClass):
  app/controllers/Item_controller.rb:52:in `list'

User Model table -

mysql> explain users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| user_id    | varchar(255) | YES  |     | NULL    |                |
| name       | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

Item controller that uses current_user.user_id -

    def list
    @mylist = Item.find(:all, :select => 'item_num', :conditions => { :id => current_user.user_id, :item_status => ["New"]} )
    respond_to do |format|
    format.html { render :partial => 'item_list'}
    format.js
    end
    return @mylist
    end


    def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
    end

    def debug
    render :text => current_user.user_id #prints correct user_id
    end

Upvotes: 1

Views: 999

Answers (3)

Andrew
Andrew

Reputation: 43153

You're getting this because current_user is turning up nil. Your current user function doesn't do what you think it does, let's take a look:

def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
end

So, this executes in this order:

  1. IF there is a session[:user_id], continue | or else return nil

  2. Then, if @current_user is set, return @current_user | or else continue

  3. Then, set @current_user to User.find(session[:user_id])

NOTE: User.find() will raise an exception if it cannot find a user. User.find_by_user_id() would just return nil without raising an exception. Be sure and choose the behavior you want.

Your method is ok, it means that you're going to get nil whenever session[:user_id] is not set. You just need to catch that in your code. Because nil is falsy this is pretty easy. In any ruby code:

any_method if current_user

In your controller you would need to do:

def list
  if current_user
    @mylist = Item.find(:all, :select => 'item_num', :conditions => { :id => current_user.user_id, :item_status => ["New"]} )
    respond_to do |format|
      format.html { render :partial => 'item_list'}
      format.js
    end
  else
    redirect_to login_path, :notice => "You must be logged in to continue."
  end
end

Note that (1) in Ruby you don't need to say return, the last line of every method is returned automatically, and (2) Rails controller methods end when you redirect or render, there is no return.

To make this cleaner I would probably say you need a before_filter. Something like this in your controller should do the trick:

before_filter :require_login

def require_login
  redirect_to login_path, :message => 'You must be logged in to view this page.' unless current_user
end

Then all your methods will redirect the person to login if they aren't logged in already. You can also specify only certain methods to use this filter using the :only or :except options.

Because you're likely to use that throughout your app you might move the current_user and require_login methods to your application controller.

One last thing though, your code and your question reflect that you're not familiar with some of the really basic ideas of rails and REST. You'll really speed up your development if you learn the "Rails Way" of doing things. A great, free resource to get you on track would be Michael Hartl's Rails 3 Tutorial. You should work through it, you'll be really glad you did, and when you're done you'll be WAY ahead of where you are now.

Upvotes: 2

DanneManne
DanneManne

Reputation: 21180

The problem does not seem to be that your User model does not respond to the method user_id, but according to the error message, the method current_user returns nil and nil does not respond to user_id. So the reason is probably that you call this action even without any user being logged in.

One way to get around it would be to first check if current_user returns anything before calling user_id. Maybe like this:

@mylist = Item.find(:all, :select => 'item_num', :conditions => { :id => (current_user && current_user.user_id), :item_status => ["New"]} )

If curret_user is nil it will evaluate to false and then user_id will not be called, instead it will query for Items where id is NULL. If that is the appropriate behavior for your app, that I leave up to you.

And as a side note, your syntax is not really Rails 3. I would change it to this:

@mylist = Item.select(:item_num).where(:id => (current_user && current_user.user_id), :item_status => ["New"])

Upvotes: 2

Cade
Cade

Reputation: 3181

The error message is telling you that current_user is set to nil when you try to call user_id on it. I can't tell you why current_user is nil based on the code you've included, but if you can track down how/where current_user assigns the user, that's where you'll find your problem. It doesn't have anything to do with your database.

Upvotes: 3

Related Questions