undefined method `has_role?' for nil:NilClass - ruby-on-rails

Trying to apply a rolify method to my user, I get the following error:
undefined method `has_role?' for nil:NilClass
I don't understand why, because current_user should be a global method accessible in every view right? How can it be nil?
Thanks!
Controller
def show
#photo = Photo.friendly.find(params[:id])
impressionist(#photo)
#photos = Photo.latest_photos
#user = #photo.user
#pin = Pin.new
#pin.photo_id = #photo.id
#category = Category.all
#commentable = #photo
#comments = #commentable.comments
#comment = Comment.new
#sponsors = #photo.sponsors
#zone = Zone.all
respond_to do |format|
format.html #show.html.erb
format.json {render json: #photo}
end
end
My view
<% if current_user.has_role? :admin %>
<%= link_to "Eliminar", user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id) , method: :delete, data: { confirm: 'Quieres borrar esto?'}, class: "delete right" %>
<%= link_to 'Editar', edit_user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id), :class=> "link", class: "edit quarter_margin_right right" %>
<% end %>
<% end %>

You need to check if there is a current_user in your views:
<% if current_user && current_user.has_role?(:admin) %>
<%= link_to "Eliminar", user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id) , method: :delete, data: { confirm: 'Quieres borrar esto?'}, class: "delete right" %>
<%= link_to 'Editar', edit_user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id), :class=> "link", class: "edit quarter_margin_right right" %>
<% end %>
<% end %>
This uses boolean short-circuiting - if current_user is nil then current_user.has_role? :admin is never evaluated.
If you are using Devise you can also use the user_signed_in? helper method.
Added.
If you find a yourself doing this often you can create a helper method:
# app/helpers/roles_helper.rb
module RolesHelper
def has_role?(role)
current_user && current_user.has_role?(role)
end
end
You can then simplefy your view:
<% if has_role?(:admin) %>
<%= link_to "Eliminar", user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id) , method: :delete, data: { confirm: 'Quieres borrar esto?'}, class: "delete right" %>
<%= link_to 'Editar', edit_user_photo_pin_path(user_id: #user.id, photo_id: #photo.id, id: pin.id), :class=> "link", class: "edit quarter_margin_right right" %>
<% end %>
Note that we are calling has_role? on the view context.

For Devise to authenticate and set the current_user, you can include the following before_action in your controller:
before_action :authenticate_user!
If you only want to authenticate user for a specific action:
before_action :authenticate_user!, only: [:new, :update]
If you want to authenticate the user for all your controllers you can just put it in your ApplicationController

Related

Rails 4 acts_as_follower

I have two models
Users and Groups
a am using gem acts_as_follower
https://github.com/tcocca/acts_as_follower
User.rb
class User < ActiveRecord::Base
has_many :groups
acts_as_follower
end
and Group.rb
class Group < ActiveRecord::Base
belongs_to :user
acts_as_followable
end
links to follow and unfollow
<% if current_user %>
<% if current_user.following?(#group) %>
<h2> <%= link_to 'Unfollow', unfollow_group_path(#group), class: 'fa fa-pause' %></h2>
<% else %>
<h2> <%= link_to 'Follow', follow_group_path(#group), class: 'fa fa-play' %></h2>
<% end %>
<% else %>
<%= link_to 'Login or Register to Follow', user_session_path, class: 'fa fa-unlock' %>
<% end %>
link to administration group followers
<h5>followers
<%= p #group.followers.count %></h5>
<% #group.followers.each do |follower| %>
<%= link_to user_path(follower) do %>
<%= image_tag(follower.image.url(:show), size: "40x40") %>
<% if current_user.id == #group.user.id %>
<%= link_to 'Reject', unfollow_group_path(#group , user_id:follower.id), class: ' fa fa-close' , style: 'color:red;' %>
<% end %>
<% end %>
<% end %>
<% if current_user.id == #group.user.id %>
<h5>expect</h5>
<% #followers_block.each do |block| %>
<%= link_to user_path(block.follower) do %>
<%= image_tag(block.follower.image.url(:show), size: "40x40") %>
<%= link_to 'Accept', follow_group_path(#group , user_id:block.follower.id), class: ' fa fa-close' , style: 'color:red;' %>
<% end %>
<% end %>
<% end %>
groups_controller.rb
def follow
#group = Group.find(params[:id])
if params[:user_id] && #group.user_id == current_user.id
#user = User.find(params[:user_id])
#user_block = Follow.where(followable_id: #group, followable_type: "Group" , follower_id: #user ).first
#user_block.update_attribute(:blocked, false)
#user_block.save
redirect_to :back
else
if current_user
current_user.follow(#group)
flash[:notice] = 'are now following'
else
flash[:error] = "You must login to follow "
end
redirect_to :back
end
end
def unfollow
#group = Group.find(params[:id])
if params[:user_id] && #group.user_id == current_user.id
#user = User.find(params[:user_id])
#user.stop_following(#group)
redirect_to :back
else
if current_user
current_user.stop_following(#group)
flash[:notice] = 'You are no longer following .'
else
flash[:error] = 'You to unfollow #'
end
redirect_to :back
end
end
how to add parameter (:blocked, true) this
if current_user
current_user.follow(#group)
flash[:notice] = 'are now following'
or this
<h2> <%= link_to 'Follow', follow_group_path(#group), class: 'fa fa-play' %></h2>
solved
if current_user
follow = current_user.follow(#group)
follow.update blocked: true
flash[:notice] = 'are now following'

Passing a block variable in an js.erb ajax call

I'm trying to make an ajax call but get a 500 (Internal Server Error) due to a template error. Here's what I get in my server logs:
ActionView::Template::Error (undefined local variable or method `dish' for #<#<Class:0x007fcd62f1b008>:0x007fcd6516bab8>):
1: <% if current_user.liked? dish %>
2: <%= link_to "Unlike", unlike_restaurant_dish_path(#restaurant, dish), method: :put, remote: true %>
3: <% else %>
4: <%= link_to "Like", like_restaurant_dish_path(#restaurant, dish), method: :put, remote: true %>
app/views/restaurants/dish_partials/_like_toggle.html.erb:1:in `_app_views_restaurants_dish_partials__like_toggle_html_erb___2598971629205628451_70260057733340'
app/views/dishes/like.js.erb:1:in `_app_views_dishes_like_js_erb__1312876563634492607_70260057701620'
app/controllers/dishes_controller.rb:6:in `like'
From what I understand its not finding the block variable dish that I'm passing in my each block in my partial. What needs to be done so that they're recognized?
Dishes Controller
class DishesController < ApplicationController
before_action :load_restaurant_and_dish, only: [:like, :unlike, :dislike, :undislike]
def like
#dish.liked_by current_user
respond_to do |format|
format.html { redirect_to #restaurant }
format.js
end
end
...
private
def load_restaurant_and_dish
#restaurant = Restaurant.find(params[:id])
#dish = Dish.find(params[:id])
end
end
views/dishes/like.js.erb
$(".restaurant__dish").html('<%= escape_javascript(render partial: "restaurants/dish_partials/like_toggle", dish: dish) %>');
views/restaurants/dish_partials/_dishes.html.erb
<% #dishes.each do |dish| %>
<div class="restaurant__dish">
<b><%= dish.name %></b>
<%= render "restaurants/dish_partials/like_toggle", dish: dish %>
</div>
<% end %>
views/restaurants/dish_partials/_like_toggle.html.erb
<% if current_user.liked? dish %>
<%= link_to "Unlike", unlike_restaurant_dish_path(#restaurant, dish), method: :put, remote: true %>
<% else %>
<%= link_to "Like", like_restaurant_dish_path(#restaurant, dish), method: :put, remote: true %>
<% end %>
<% if current_user.disliked? dish %>
<%= link_to "Undislike", undislike_restaurant_dish_path(#restaurant, dish), method: :put, remote: true %>
<% else %>
<%= link_to "Dislike", dislike_restaurant_dish_path(#restaurant, dish), method: :put, remote: true %>
<% end %>
views/dishes/like.js.erb
$(".restaurant__dish").html('<%= escape_javascript(render "restaurants/dish_partials/like_toggle", dish: #dish) %>');
or
$(".restaurant__dish").html('<%= escape_javascript(render partial: "restaurants/dish_partials/like_toggle", locals: { dish: #dish }) %>');
you're missing # before dish variable.

Setting a controller action inside link_to

I need help with allowing users to click "Follow/Unfollow" link and have the action take place on the user show page. Currently I have the action sitting inside a popup and I don't know how I can set it inside a link action so there would be no need for a popup.
I tried rendering the forms on the show page but that does not show as I expected.
show.html.slim:
- if current_user.following?(#user)
= link_to follow_popup_user_path(#user), class: 'profile_btn save_btn', :'data-mfp-src'=>'#follow_div', remote: true do
= image_tag 'start_icon.png'
span Unfollow
- else
= link_to follow_popup_user_path(#user), class: 'profile_btn save_btn', :'data-mfp-src'=>'#follow_div', remote: true do
= image_tag 'start_icon.png'
_follow.html.slim:
= form_for(current_user.relationships.build(followed_id: #user.id)) do |f|
div
= f.hidden_field :followed_id
= f.submit "Follow", class: "btn btn-large btn-primary"
_unfollow.html.slim:
= form_for(current_user.relationships.find_by(followed_id: #user),
html: { method: :delete }) do |f|
= f.submit "Unfollow", class: "btn btn-large"
Relationships controller:
def create
#user = User.find(params[:relationship] [:followed_id] )
current_user.follow!(#user)
redirect_to #user
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
redirect_to #user
end
end
You shouldn't need a form for this, just pass the correct params to a nested route for relationships, then handle the specifics in the user model follow and unfollow methods. Try something like this:
users/show.slim
- if current_user.following?(#user)
= link_to new_user_relationship_path(id: current_user.id, followed_id: #user.id), class: 'profile_btn save_btn' do
= image_tag 'start_icon.png'
span Unfollow
- else
= link_to user_relationship_path(id: current_user.id, followed_id: #user.id), method: delete, class: 'profile_btn save_btn' do
= image_tag 'start_icon.png'
routes
resources :users do
resources :relationships
relationships_controller
def create
#user = User.find(user_params(:id))
#followed_user = User.find(user_params(:followed_id))
#user.follow #followed_user
redirect_to #user
end
def destroy
#user = User.find(user_params(:id))
#followed_user = User.find(user_params(:followed_id))
#user.unfollow #followed_user
redirect_to #user
end
private
def user_params
params.require(:user).permit(:followed_id, )
end

Nested Resources - No route matches

I'm having trouble with nested resources in my Rails4 application. What am I missing here?
I can view the participations/new.html.erb form well but when I submit it's returning "No route matches [POST] "/examinations/1-ales/participations/new" error.
When I'm trying to edit a participation for example this url: localhost:3000/examinations/1-ales/participations/15/edit is returning "ActionController::ParameterMissing in ParticipationsController#edit" and "param not found: participation".
Edit and destroy links in the index.html.erb file causing error:
ActionController::UrlGenerationError in Participations#index
No route matches {:action=>"edit", :controller=>"participations",
:examination_id=>#<Participation id: 12, user_id: 1, examination_id: 1,
payment_status: false, language_preference: "English">,
id=>nil, :format=>nil} missing required keys: [:id]"
routes.rb
resources :examinations do
resources :participations
end
examination.rb
class Examination < ActiveRecord::Base
has_many :participations
has_many :users, :through => :participations
end
participation.rb
class Participation < ActiveRecord::Base
belongs_to :user
belongs_to :examination
end
participations_controller.rb
class ParticipationsController < ApplicationController
before_filter :authenticate_user!
before_action :set_participation, only: [:show, :edit, :update, :destroy]
def index
#participations = Participation.all
end
def show
end
def new
#participation = Participation.new
#examination = params[:examination_id]
end
def edit
#examination = params[:examination_id]
#participation = Participation.new(participation_params)
end
def create
#participation = Participation.new(participation_params)
#participation.user = current_user
respond_to do |format|
if #participation.save
redirect_to(#examination)
format.html { redirect_to #participation, notice: 'Sınav Katılımınız Oluşturuldu!' }
else
render 'new'
format.html { render action: 'new' }
end
end
end
def update
respond_to do |format|
if #participation.update(participation_params)
format.html { redirect_to #participation, notice: 'Participation was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
def destroy
#participation.destroy
respond_to do |format|
format.html { redirect_to participations_url }
end
end
private
def set_participation
#participation = Participation.find(params[:id])
end
def participation_params
params.require(:participation).permit(:user_id, :examination_id, :payment_status, :language_preference, :exam_center_preference, :disability)
end
end
app/views/participations/new.html.erb
<%= simple_form_for #participation, as: :participation, url: new_examination_participation_path(#examination) do |f| %>
<%= f.input :user_id, :as => :hidden, :input_html => { :value => current_user.id } %>
<%= f.input :examination_id, as: :hidden %>
<%= f.input :language_preference, collection: ["English", "German"] %>
<%= f.button :submit, "Register to Exam" %>
<% end %>
app/views/participations/edit.html.erb
<%= simple_form_for #participation, as: :participation, url: edit_examination_participation_path(#examination) do |f| %>
<%= f.input :user_id, :as => :hidden, :input_html => { :value => current_user.id } %>
<%= f.input :examination_id, as: :hidden %>
<%= f.input :language_preference, collection: ["English", "German"] %>
<%= f.button :submit, "Register to Exam" %>
<% end %>
app/views/participations/index.html.erb
** Links, which are causing error in the application.
<%= link_to 'Edit', edit_examination_participation_path(participation), :class => 'btn btn-small' %>
<%= link_to 'Delete', participation, method: :delete, data: { confirm: 'bla bla' }, :class => 'btn btn-small btn-danger' %>
Create a Participation:
In app/views/participations/new.html.erb,
Replace
<%= simple_form_for #participation, as: :participation, url: new_examination_participation_path(#examination) do |f| %>
With
<%= simple_form_for #participation, as: :participation, url: examination_participations_path(#examination, #participation) do |f| %>
examination_participations_path would direct the the request to ParticipationsController #create upon form submission. That is what I think you are trying to achieve => Create a Participation
Update a Participation:
In app/views/participations/edit.html.erb,
Replace
<%= simple_form_for #participation, as: :participation, url: edit_examination_participation_path(#examination) do |f| %>
With
<%= simple_form_for #participation, as: :participation, url: examination_participation_path(#examination, #participation) do |f| %>
examination_participation_path would direct the the request to ParticipationsController #update upon form submission.
List all Participations:
In app/views/participations/index.html.erb,
Replace
<%= link_to 'Edit', edit_examination_participation_path(participation), :class => 'btn btn-small' %>
<%= link_to 'Delete', participation, method: :delete, data: { confirm: 'bla bla' }, :class => 'btn btn-small btn-danger' %>
With
<%= link_to 'Edit', edit_examination_participation_path(#examination, participation), :class => 'btn btn-small' %>
<%= link_to 'Delete', examination_participation_path(#examination, participation), method: :delete, data: { confirm: 'bla bla' }, :class => 'btn btn-small btn-danger' %>
NOTE: I would highly recommend you to read about Nested Resources in Rails guides.
You can always do rake routes and see the list of routes that are available to you.

Method becomes nil after an ajax response?

The following code does the following: If the user, voted up show him a delete vote form and a vote down form. If the user voted down, show him a vote up form and the delete vote form. Otherwise just show the vote up and vote down form (I omitted some of the code so the question doesn't get too long).
(scenario: user voted down):
posts_controller.rb:
def show
#post = Post.find(params[:id])
#replies = #post.replies.paginate(page: params[:page])
#reply = #post.replies.build
#vote = Vote.new
store_location
end
votes_controller.rb:
class VotesController < ApplicationController
before_filter :signed_in_user
def create
#votable = find_votable
# Destroy the vote first in case the user already voted
if already_voted?
#vote = #votable.votes.find_by_user_id(current_user.id)
#vote.destroy
end
#vote = #votable.votes.build(params[:vote])
#vote.user_id = current_user.id
#votable.save
respond_to do |format|
format.html { redirect_back }
format.js
end
end
def destroy
#votable = find_votable
#vote = #votable.votes.find_by_user_id(current_user.id)
#vote.destroy
#votable.reload
respond_to do |format|
format.html { redirect_back }
format.js
end
end
private
def find_votable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def already_voted?
#votable.votes.exists?(:user_id => current_user.id)
end
end
posts/vote_form
<div class="vote-form">
<% if #post.votes.exists?(:user_id => current_user.id) %>
<% if #post.votes.find_by_user_id(current_user.id).polarity == -1 %>
<%= form_for ([#post, #vote]), remote: true do |f| %>
<%= f.hidden_field :polarity, value: 1 %>
<div class="form-actions">
<%= button_tag type: :submit, class: "btn btn-small vote" do %>
<i class="icon-thumbs-down"></i> Vote up
<% end %>
</div>
<% end %>
<%= form_for ([#post, #post.votes.find_by_user_id(current_user.id)]),
method: :delete,
remote: true do |f| %>
<div class="form-actions">
<%= button_tag type: :submit, class: "btn btn-small btn-primary unvote" do %>
<i class="icon-thumbs-up"></i> Vote down
<% end %>
</div>
<% end %>
<% end %>
votes/create.js:
$('#<%= #votable.class.name.downcase %>-<%= #votable.id %> .vote-form').html(
"<%= escape_javascript(render('shared/delete_vote')) %>"
);
shared/_delete_vote.html.erb:
<% if #votable.votes.find_by_user_id(current_user.id).polarity == -1 %>
<%= form_for ([#votable, #votable.votes.new]), remote: true do |f| %>
<div class="form-actions">
<%= button_tag type: :submit, class: "vote-down btn btn-small" do %>
<i class="icon-thumbs-up"></i> Vote up
<% end %>
</div>
<% end %>
<%= form_for ([#votable, #vote]), method: :delete, remote: true do |f| %>
<div class="form-actions">
<%= button_tag type: :submit, class: "vote-up btn btn-small btn-primary" do %>
<i class="icon-thumbs-up"></i> Vote down
<% end %>
</div>
<% end %>
<% end %>
So, now everything works fine, except that I get this error when I click vote down, and then vote up right after (without refreshing the page):
ActionView::Template::Error (undefined method `polarity' for nil:NilClass):
1: <% if #votable.votes.find_by_user_id(current_user.id).polarity == 1 %>
2: <%= form_for ([#votable, #vote]), method: :delete, remote: true do |f| %>
What could be the problem?
EDIT:
I realized that the line in the error message is the problem (not #votable):
<% if #votable.votes.find_by_user_id(current_user.id).polarity == 1 %>
Strange, I thought it was the same as
<% if #post.votes.find_by_user_id(current_user.id).polarity == 1 %>
I think the problem is here:
def destroy
#votable = find_votable
#vote = #votable.votes.find_by_user_id(current_user.id)
#vote.destroy
#votable.reload
respond_to do |format|
format.html { redirect_back }
format.js
end
end
You destroy the vote for current user, so it can not be found inside view:
#votable.votes.find_by_user_id(current_user.id).polarity
That's why polarity is called on nil. You should reword the logic of your vote manipulations.
EDIT:
As the easiest solution, you may replace
if #votable.votes.find_by_user_id(current_user.id).polarity == -1
with
if #votable.votes.find_by_user_id(current_user.id).blank?

Resources