Liz
Liz

Reputation: 1457

Ruby No Method Error on a Model for Current User when Logged Out

I'm building a rails app that has a lot of things that change based on whether or not the user has completed certain quizzes. Everything works fine when a user is logged in, but I just tried logging out and I got a NoMethodErroron this line of code:

<% if current_user.per_quiz.nil? %>

Basically, if the user has completed per_quiz they go to one page (the edit view), if they haven't they go to a different page (the new view). The only thing that changed was the fact that I logged out, so I can only assume that is what caused the error. Do I need to add some kind of if statement to account for a state in which no user is logged in? How should I fix this error in accordance with Ruby best practices?

Upvotes: 2

Views: 176

Answers (3)

Michael Gaskill
Michael Gaskill

Reputation: 8042

You just need to check to see if current_user is set before checking per_quiz. You can handle checking for the login state easily in a view by querying current_user.nil?:

<% if !current_user %>
    <p> You must be logged in to do anything useful.  Please go login</p>
<% elsif !current_user.per_quiz %>
    <p>Cool quiz stuff goes here...</p>
<% else %>

What you probably really want is to have a logged out user go elsewhere, such as the home page or signin page. To do that, you need to do a couple of simple things to your controller. I'm going to assume that the controller is called 'QuizzesController' since you hadn't included your controller code in the question.

Here's how to do it:

class QuizzesController < ApplicationController

  # Other devise authentication logic goes here...

  before_action :authorize_user!

  # Your actions go here...

private

  def authorize_user!
    if !current_user
      redirect_to '/', notice: "You must be logged in to access to this page"
    end
  end
end

What this does is install a "before_action" handler that will check that the user is logged in before letting them do anything in this controller. If they are not logged in, you can redirect them wherever you wish.

Note that sometimes, only certain actions need this kind of treatment. In that case, you can use an :only or :except option to specify which action(s) are or are not handled. It looks like this:

  before_action :authorize_user!, only: [ :new, :edit, :create ]

or like this:

  before_action :authorize_user!, except: :list

This will give you greater flexibility in managing the authorization part of the equation, where devise handles the authentication part.

Upvotes: 2

retgoat
retgoat

Reputation: 2464

That's because current_user is nil when no user logged in. And if you will cal some method on nil it will throw NoMethodError.

I assume that have code you provided in the view. So, you can check if user is logged in.

<% if user_signed_in? %>
  # do smth with user quizzes
<% else %>
  # do something else
<% end %>

But the best way is to use before filter in the controller and not allow to anonymous to see that page.

Here is the example application with Rails and devise. https://github.com/RailsApps/rails-devise

Hope that helps.

Upvotes: 2

Liz
Liz

Reputation: 1457

I'm not sure if this is the "correct" Ruby way to do this, but the way I eventually found was to change it to an if/elsif/else statement:

<% if current_user.nil? %>
  ...
<% elsif current_user.bal_quiz.nil? %>
  ...
<% else %>
  ...
<% end %>

Probably should have figured this out before I posted the question, but I was (and still am) interested to see if there's a better or "more Ruby" way of doing this.

Upvotes: 1

Related Questions