Rails - undefined method `admin' for nil:NilClass in Sessions#create - ruby-on-rails

When I'm trying to login I'm getting:
undefined method `admin' for nil:NilClass
which is called from the layouts/_head.html.erb partial
_head.html.erb:
<% if session[:user_id] %>
<% if #current_user.admin %>
<%= button_to 'Swap to user', to_user_path(User.find_by(id: session[:user_id]).id), method: :put,
class: 'btn btn-warning' %>
<% else %>
<%= button_to 'Swap to admin', to_admin_path(User.find_by(id: session[:user_id]).id), method: :put,
class: 'btn btn-warning' %>
<% end %>
<% end %>
As you can see, session[:user_id] exists. Here is application_controller.rb:
class ApplicationController < ActionController::Base
before_action :set_user
protect_from_forgery with: :exception
def set_user
if session[:user_id]
#current_user = User.find_by(session[:user_id])
end
end
end
Also the login button works through Ajax. So why am I getting #current_user as nil, if it's defines before_action in the application controller? (Remember: session[:user_id] != nil)

Your use of find_by is incorrect. It should be:
#current_user = User.find_by(:id => session[:user_id])

So the solve is to define #current_user in create method,like
def create
user = User.find_by(name: params[:name])
respond_to do |format|
if user and user.authenticate(params[:password])
session[:user_id] = user.id
#current_user = user
format.js { }
else
format.js {flash.now[:notice] = 'Wrong pass/name'}
end
end
end
guesss its not absolute right,but one of possible method

Related

using acts as followable gem on tweets

im trying to add acts a follower to my app so a user can follow other users and on the tweets index page only display the current users tweets and the tweets of the people they are following. i currently have a "follow" button on the user show page, but i would expect it to change into "following" on a click, but nothing changes. ive looked at other questions and documentation, but no luck. thank you.
Users show view:
<% if current_user.following?(#user) %>
<%= button_to "Following", {action: "unfollow", id: #user.id}, method: "post", class: "btn btn-secondary btn_unfollow", remote: true %>
<% else current_user != #user %>
<%= button_to "Follow", {action: "follow", id: #user.id}, method: "post", class: "btn btn-primary btn_follow", remote: true %>
<% end %>
Users Controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#tweets = #user.tweets.order('created_at DESC')
authorize #user
end
def follow
#current_user.follow(#user)
#follow = Follow.find_by(follower: #current_user, followable: #user)
# #user = User.find(params[:id])
# current_user.follow(#user)
# current_user.follow(#user)
# redirect to user_path(#user)
# respond_to :js
end
def unfollow
#user = User.find(params[:id])
#current_user.stop_following(#user)
# current_user.stop_following(#user)
# redirect_to user_path(#user)
# respond_to :js
end
end
Routes:
resources :users do
member do
get :follow
get :unfollow
end
end
You're adding an ajax response by doing remote:true. You need to add a follow.js.erb and an unfollow.js.erb view that will re-render your partial.
Try and remove the remote: true part from your view:
<% if current_user.following?(#user) %>
<%= button_to "Following", {action: "unfollow", id: #user.id}, method: "post", class: "btn btn-secondary btn_unfollow" %>
<% elsif current_user != #user %>
<%= button_to "Follow", {action: "follow", id: #user.id}, method: "post", class: "btn btn-primary btn_follow" %>
<% end %>
Add post method for both follow and unfollow actions on routes.rb:
resources :users do
member do
post :follow
post :unfollow
end
end
and try current_user instead of #current_user in your UsersController if you're using Devise as a authentication solution:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#tweets = #user.tweets.order('created_at DESC')
authorize #user
end
def follow
current_user.follow(#user)
#follow = Follow.find_by(follower: current_user, followable: #user)
# #user = User.find(params[:id])
# current_user.follow(#user)
# current_user.follow(#user)
# redirect to user_path(#user)
# respond_to :js
end
def unfollow
#user = User.find(params[:id])
current_user.stop_following(#user)
# current_user.stop_following(#user)
# redirect_to user_path(#user)
# respond_to :js
end
end

Rails: ActionController::InvalidAuthenticityToken

I'm currently working on a project involving users, likes, and posts. I have a like/unlike button that I finally got to work some of the time, but on certain user's profiles when I go to unlike a post, I get thrown this error, which says that it is coming from my destroy action in my likes controller:
ActionController::InvalidAuthenticityToken
I'm using devise, but don't know if that has to do with the cause of the issue.
Right now this is what I'm working with:
<h4>All of <%= #user.email %>'s posts:</h4>
<% #user.posts.order('created_at DESC').each do |post| %>
<li><%= post.content %></li>
<% unless current_user.likes.pluck(:post_id).include?(post.id) %>
<%= form_tag likes_path do %>
<%= hidden_field_tag 'post_id', post.id %>
<%= submit_tag "Like", :class => "like_button" %>
<% end %>
<% else %>
<% like = post.likes.where(user_id: current_user.id).first %>
<div class="unlike_button">
<%= form_tag like_path(like) do %>
<%= hidden_field_tag 'post_id', post.id %>
<%= button_to "Unlike", like_path(post), method: :delete %>
</div>
<% end %>
class LikesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#like = Like.new(user_id: current_user.id, post_id: #post.id)
if #like.save
flash[:success] = "Post Liked!"
redirect_back(fallback_location: root_path)
else
flash[:notice] = "Couldn't like post"
redirect_back(fallback_location: root_path)
end
end
def destroy
#like = Like.find(params[:id])
#like.destroy
flash[:success] = "Post unliked"
redirect_back(fallback_location: root_path)
end
end
class PostsController < ApplicationController
def index
#posts = Post.all
#user = User.find(params[:user_id])
end
def new
#post = Post.new
#user = User.find(params[:user_id])
end
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Posted!"
redirect_to user_path(current_user)
else
flash[:notice] = "Post could not be submitted"
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit(:content)
end
end
There is a comment in application_controller.rb..
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
so ,you may try changing..
protect_from_forgery with: :exception
to this
protect_from_forgery with: :null_session
Hope it helps :)
I think I have figured it out.. At least have gotten it to work. I wasusing a form_for helper as well as button_to helper. I deleted the form_for helper and just stuck with
<%= button_to "Unlike", like_path(like), method: :delete %>
and it is now working
What helps me solve this problem is adding the Forward Slash in the URL
From:
= bootstrap_form_tag url: 'signup_with_phone' do |form|
To:
= bootstrap_form_tag url: '/signup_with_phone' do |form|

session[:user_id] is changing from nil to previous value on browser go back button

In my rails app , when I logout , in the destroy method I am setting session[:user_id]=nil. But when I press back button on the browser the session[:user_id] gets back its previous value and it is automatically showing the logged in page. Why is this happening? How do I make the session[:user_id]=nil persistent till I change it?
session_controller.rb
class SessionsController < ApplicationController
def index
end
def show
end
def new
end
def create
#user = User.find_by_email(params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to user_posts_path
else
render 'new'
end
end
def destroy
session[:user_id] = nil
end
end
application.html.erb
<% if !(session[:user_id].nil?)%>
Logged in as <%= current_user.email %>
<%= link_to 'Log Out', session_path(current_user), :method => :delete %>
<% else %>
<% if current_page?(new_user_path) %>
<%= link_to "Log in", login_path %>
<% elsif current_page?(login_path) %>
<%= link_to "sign up",new_user_path%>
<% else %>
<%= link_to "Log in", login_path %>
<%= link_to "sign up",new_user_path%>
<% end %>
<% end %>
<%= yield %>
there is no error in the rails s console.
last message on the console.
Started DELETE "/sessions/2" for 127.0.0.1 at 2015-10-08 00:23:11 +0530
Processing by SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"B0QLdVrsV9ZgwjS/Y8qVb3ID0q9gsC2peFQAZ/0J638kUTpXcAYcg1I+ulX1UaLujr4C7NPgIann74UETMOz6w==", "id"=>"2"}
Rendered sessions/destroy.html.erb within layouts/application (0.1ms)
Completed 200 OK in 144ms (Views: 143.4ms | ActiveRecord: 0.0ms)
Use reset_session in your logout action instead. This will issue a new session identifier and declare the old one invalid and prevents other session fixation based attacks.
http://guides.rubyonrails.org/security.html#session-fixation-countermeasures
This is a run through of how to setup your SessionsController properly:
Sessions are not really like a standard crud resource where you have the full range of CRUD verbs and fetch records from the database.
From a user standpoint there are only three actions:
new - displays the login form
create - verifies the credentials and signs the user in.
destroy - logs user out by resetting the session.
Change your routes definition to treat Sessions as a singular resource:
resource :sessions, only: [:new, :create, :destroy]
Then we are going to create a helper:
module SessionsHelper
def current_user
#user ||= User.find!(session[:user_id]) if session[:user_id]
end
def user_signed_in?
!current_user.nil?
end
def can_sign_in?
user_signed_in? || current_page?(new_user_path) || current_page?(new_session_path)
end
end
This way the actual implementation of how the user is stored in the session is only in one place in your application and not spread all over your controllers and views.
Lets make sure we can call it from our controllers:
class ApplicationController < ActionController::Base
include SessionsHelper
end
Then lets remedy the controller:
class SessionsController < ApplicationController
# GET /session
def new
end
# POST /session
def create
reset_session # prevents sessions fixation!
#user = User.find_by(email: params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to user_posts_path
else
render 'new', flash: "Invalid username or password."
end
end
# DELETE /session
def destroy
reset_session
if user_signed_in?
flash[:notice] = 'You have been signed out successfully.'
else
flash[:error] = 'You are not signed in!'
end
redirect_to root_path
end
end
application.html.erb
<%= render partial: 'sessions/actions' %>
<%= yield %>
We use a partial since the application layout tends to turn into a monster.
sessions/_actions.html.erb.
<% if user_signed_in? %>
Logged in as <%= current_user.email %>
<%= link_to 'Log Out', session_path, method: :delete %>
<% else %>
<%= link_to 'Log in', new_session_path if can_sign_in? %>
<% end %>

Undefined method for NilClass

Currently, I'm receiving the following error:
NoMethodError in EventsController#create
undefined method `events' for nil:NilClass
Here are the following files...
EventsController. I've omitted some of the methods. Including Events#new
class EventsController < ApplicationController
include SessionsHelper
def create
#event = current_user.events.build(event_params)
if #event.save
flash[:success] = "You created an event"
redirect_to events_path
else
redirect_to new_event_path
end
end
private
def event_params
params.require(:event).permit(:description)
end
end
The SessionsHelper holds current_user...I've posted the whole file here just in case.
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attributes(remember_token: User.hash(remember_token))
self.current_user = user
end
def current_user
remember_token = User.hash(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
def current_user=(user)
#current_user = user
end
def sign_out
current_user.update_attributes(remember_token:
User.hash(User.new_remember_token))
cookies.delete(:remember_token)
self.current_user = nil
end
def signed_in?
!current_user.nil?
end
end
And, finally, here is the Events#new. This is the view that calls the Events#create action:
<h1>Events#new</h1>
<!-- url: will send all form data to the action specified -->
<%= form_for(:event, url: {action: "create"}) do |f| %>
<%= f.label "Description" %>
<%= f.text_area :description, value: "Write here" %><br>
<%= f.submit "Create event" %>
<% end %>
Nevermind, all that's needed is to update the server. So close the server and type rails s.

Rails Michael Hartl Tutorial + Devise current_user?

I am trying to implement the follow actions from M. Hartl's tutorial in my rails composer, devise app.
But for some reason i get this error
undefined method `current_user?' for
when using this code
<% unless current_user?(#user) %>
<div id="follow_form">
<% if current_user.following?(#user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
</div>
<% end %>
Any idea why?
Thanks
Devise gives you the current_user helper by default. However, it doesn't give you current_user? boolean method. You need to define this method yourself in application_helper.rb:
def current_user?(user)
user == current_user
end
Now you have current_helper? defined you can pass in your #user instance variable
<% unless current_user?(#user) %>
<div id="follow_form">
<% if current_user.following?(#user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
</div>
<% end %>
Thats all you have to do. You don't have to touch your ApplicationController or SessionsHelper in any way. You are simply using current_user which Devise gives you in order to define current_user?(user). This way is much easier to implement. Hope this helps
Update
Hopefully you should have the following setup:
Your ApplicationController
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
# Force signout to prevent CSRF attacks
def handle_unverified_request
sign_out
super
end
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def authorize
redirect_to login_url, alert: "Not Autherized" if current_user.nil?
end
def track_activity(trackable, action = params[:action])
current_user.activities.create! action: action, trackable: trackable
end
end
You should also have a sessions_helper
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def signed_in?
!current_user.nil?
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
def current_user?(user)
user == current_user
end
#CHECK THE ABOVE LINE!
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
end
Then you can do in your view the following:
<% unless current_user?(#user) %>
<div id="follow_form">
<% if current_user.following?(#user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
</div>
<% end %>

Resources