undefined local variable or method 'current_user' - ruby-on-rails

I'm currently going through a RoR tutorial (http://railstutorial.org/chapters/sign-in-sign-out#sec:signin_success is the relevant section) which seems to be fairly good, although I've run into the following problem when trying to view the sample site.
Extracted source (around line #10):
7: <li><%= link_to "Home", root_path %></li>
8: <li><%= link_to "About", about_path %></li>
9:
10: <% if signed_in? %>
11: <li><%= link_to "Profile", current_user %></li>
12: <li><%= link_to "Sign out", signout_path, :method => delete %></li>
13: <% else %>
As you can see, the issue is stemming from my method "signed_in?" which is supposed to check if the user has logged in or not by checking whether the current_user variable is set (I've included the rest of the code from the helper to give a context, apologies):
module SessionsHelper
def sign_in(user)
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
current_user = user
end
def sign_out
cookies.delete[:remember_token]
current_user = nil
end
def current_user= (user)
#current_user ||= user_from_remember_token
end
def signed_in?
!current_user.nil?
end
private
def user_from_remember_token
User.authenticate_with_salt(*remember_token)
end
def remember_token
cookies.signed[:remember_token] || [nil, nil]
end
end
From my understanding, .nil? is a method that checks whether or not an object has been defined and therefore the object being undefined shouldn't generate an error but rather return false? I searched the tutorial for all cases of current_user (before checking to see if anyone else had this problem with little success) and my code seems correct so I'm a little confused and if anyone is able to help me understand the way Ruby variables are supposed to be accessed and why my code isn't working I'd be most grateful.
Edit:
I'm not sure if it's important with scope as I'm just beginning both Rails and Ruby, however the helper SessionsHelper is being used by my Users controller and views (it's included in my Applications controller)

I ran in to this same issue & it was for the same reason. You overlooked part of the instructions on 'Listing 9.16'.
def current_user= (user)
#current_user ||= user_from_remember_token
end
You were supposed to change this to the following.
def current_user
#current_user ||= user_from_remember_token
end
You'll also want to change all of the instances of *self.*current_user to *#*current_user.
Once you do this the error(s) are resolved.

Make sure you have the following code in the SessionHelper
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= user_from_remember_token
end

The nil? method is not going to check whether a variable or method is defined. It is solely checking whether it is defined as a nil object or not. Ruby walks up the ancestors chain for SessionsHelper and finally determines that current_user is not defined anywhere (it will eventually end at Kernel#method_missing) and then throws an error. The quickest way to solve the problem would be:
#app/helpers/sessions_helper.rb
def current_user
#current_user ||= false
end

I asked a friend, and he corrected my errors. I think a large part of my mistake came from not being completely familiar with variable scope in Ruby and forgetting that everything is an object and therefore the method current_user=(user) was overriding the assignment function.
My friend's solution was to change the scope of current_user to an instanced variable (so it can be properly used), and change the function curent_user=(user) to a simple get_current_user function in order to determine if the current user exists in the cookie.
The final changed code is as follows:
#app/helpers/sessions_helper.rb
def sign_in(user)
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
#current_user = user
end
def sign_out
cookies.delete(:remember_token)
#current_user = nil
end
def get_current_user
#current_user ||= user_from_remember_token
end
def signed_in?
!get_current_user.nil?
end
#app/views/layouts/_header.erb
<% if signed_in? %>
<li><%= link_to "Profile", get_current_user %></li>
<li><%= link_to "Sign out", signout_path, :method => :delete %></li>
<% else %>
<li><%= link_to "Sign in", signin_path %></li>
<% end %>
As you can see the variable in my header partial has also been changed to reflect the method used in the helper to obtain the user.
Going to start reading some basic Ruby guides so next time I get in over my head I have an idea of where to start fixing it :)

Related

Using if statement in rails to show a link_to tag based on who is signed in?

beginner in Rails and I am making a rails news page with devise authentication and when someone visits localhost:3000/news, it shows a list of news. I am trying to show "edit" link only if it was posted by current user or if the current user's username is "admin". Here is my code for it
<% #news.each do |element| %>
<p>Title: <%= element.title%></p>
<p><%= element.body%></p>
<li>Posted by: <%= element.user.username %></li>
<li><%= link_to "Comments", newsone_path(element) %></li>
<% if current_user.username == "admin" || current_user == element.user %>
<li><%= link_to "Edit this drink", edit_news_path(element) %></li>
<% end %>
<% end %>
It works and it shows the edit link only if username "admin" is signed in or if the user who posted it is signed in. But if I try to go to localhost:3000/news without signing in, it shows an error
"undefined method `username' for nil:NilClass" on line "<% if
current_user.username == "admin" || current_user == element.user %>"
I think this maybe because when I am signed out, there is no username maybe? How do I get around this please help?
Devise here is setting current_user to nil, which doesn't have a method .username.
To get around that, you can check for the existence of current_user before attempting to call that method.
An easy-to-read way is to do this instead:
<% if current_user && (current_user.username == "admin" || current_user == element.user) %>
In this line you're checking "does current_user exist"? Then, if so, are they an admin? Because of the way that Ruby evaluates boolean logic statements like this, the second part of that line after the && does not get evaluated, and so it doesn't raise an error.
An alternative would be to use the try method, which is a little more complex to understand, but it attempts to run the named method ('try') by referring to it as a symbol (:try):
<% if current_user.try(:username) == "admin" || current_user == element.user %>
As you develop this, you might want to look at the Rolify gem or similar, so that you can have various different named roles, and then one of the permissions gems, such as Pundit, which allows you to define what different types of users can and can't do.

creating rails method for checking object if exists

I am using Devise for authentication in my rails app and I have a _header partial in my layout folder for navbar. I wanna put there a link for Create Profile (user model is created w/ devise, user has_one profile and profile belongs_to user) but only if the user profile doesn't exist yet.
I would like to create a method for this and put the if statement into the view but I can't figure it out where to create the method and how it would look like.
The basic devise method works fine when it comes to checking if user is signed in. I want a similar method that can check if user profile exists.
layout/_header.html.erb
<% if user_signed_in? %>
<% if user.profile(current_user) %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>
So my questions:
Where to put the method (helper/controller/model/appcontroller/etc.)?
How the method would look like?
You can define it in your Helper files (app/helpers/). You can use the application_helper but for a better consistency we will name this file users_helper:
# app/helpers/users_helper.rb
module UsersHelper
def user_has_profile?(user = current_user)
return false unless user.present?
Profile.where(user_id: user.try(:id) || user).exists?
end
end
and use it like this:
# any view
<% if user_signed_in? && !user_has_profile? %>
I would put this in the helpers directory(app/helpers/application_helper.rb) as a method called has_profile?
the method would look like
def has_profile?
current_user.profile.present?
end
then in your view:
<% if user_signed_in? && has_profile? %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>

NoMethodError in SessionsController#create using Omniauth Facebook authentication railscast 360

Hi there I am currently following railscast 360 on adding Facebook authentication to my webapp. It was all going well until I started getting this error:
"NoMethodError in SessionsController#create
undefined method `name=' for #"
app/models/user.rb:6:in block in from_omniauth'
app/models/user.rb:3:intap'
app/models/user.rb:3:in from_omniauth'
app/controllers/sessions_controller.rb:3:increate'
I have looked at a few different answers on this website but a lot of them seem to be for the Twitter authentication I am just trying to get the Facebook working.
I have created an app on the Facebook Developers and followed the tutorial for the rail cast fully. I would appreciate any help with this greatly.
My code so far is
user.rb:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
rescue ActiveRecord::RecordNotFound
end
helper_method :current_user
end
session_controller.rb
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_url
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
end
This is my code inside my application.html.erb:
<div id="user_nav">
<% if current_user %>
Signed in as <strong><%= current_user.name %></strong>!
<%= link_to "Sign out", signout_path, id: "sign_out" %>
<% else %>
<%= link_to "Sign in with Facebook", "/auth/facebook", id: "sign_in" %>
<% end %>
Would really appreciate any help.
Thanks
Going off of your call stack trace (it is in reverse order of calls), it seems like your session_controller calls user model, and in particular the user block.
Looking at your code, no error stands out, but the stack trace is flagging line 6 and mentions that it does not understand the method: undefined method 'name='. If I had to take a guess, it is likely that there is no name column in the table - this may not solve your issue, but try the following:
$ rake db:migrate
and
$ rails c
Then in the rails console, try checking the fields of the User object.
> User.new
Hopefully you'll know whether name is listed in the object or not.

Unable to fully log out - RailsTutorial.org - Chapter 8

I'm working through the tutorial at Railstutorial.org and have completed chapter 8 with passing tests. My issue is that if I follow the guide's code exactly, I am able to log in, but unable to log out.
If I click "Log out" I am redirected to the root_path, but as a still logged in member.
I think I traced the behavior to my sessions helper. Specifically the following lines:
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
Through the rails console and pry I was able to determine that cookies[:remember_token] is nil, but since my user's remember_token is also nil, the find_by_remember_token is saying, "Hey nil == nil! Great! We found our user!" Except that this is obviously not desired behavior.
I've fixed it by changing the current_user method to the following:
def current_user
#current_user ||= cookies[:remember_token] && User.find_by_remember_token(cookies[:remember_token])
end
I completely accept and understand that this is likely an error in my code. I've found Michael Hartl's commit from this point in the project and compared our files and can't find a discrepancy. Any ideas on what might be going on here?
Thank you for your time.
put this in your...
Controller:
def logout
reset_session
redirect_to :controller => 'classified', :action => 'list'
end
and in your view: you need to add a logout link.
<% else %>
<p><%= "Welcome #{session[:user].login}!" -%></p>
<p><%= link_to 'Logout', :controller => 'user',:action => 'logout' -%></p>
<% end %>

RailsTutorial.org Exercise 9.6.2 - using session instead of cookies for user sign-in

RailsTutorial.org Exercise 9.6.2: I'm trying to sign in with cookies if a 'Stay signed in' checkbox is checked, otherwise sign in with a session. Starting from Michael Hartl's code, I've made the following changes:
I've added this code to the sign-in form in views > sessions > new.html.erb:
<div class="field">
<%= f.check_box :stay_signed_in %> Stay signed in?
</div>
I've added this one line in the sessions controller:
def create
session[:staysignedin] = (params[:session][:stay_signed_in] == "1") ? true : false
...
end
And I've made the following alterations in the Sessions Helper:
def sign_in(user)
if session[:staysignedin]
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
else
session[:userid] = user.id
end
self.current_user = user
end
def sign_out
if session[:staysignedin]
cookies.delete(:remember_token)
else
session[:userid] = nil
session[:staysignedin] = nil
end
self.current_user = nil
end
def user_from_remember_token
session[:staysignedin] ? User.authenticate_with_salt(*remember_token) : User.find_by_id(session[:userid])
end
The problem is that it always behaves as if the checkbox is unchecked, always signing in using session rather than cookies, even when the checkbox is checked, and I just can't figure out why.
Try this: http://www.nimweb.it/web-development/ruby-on-rails-web-development/ruby-on-rails-tutorial-exercise-9-6-2-rails-session/

Resources