Allow access to different views and routes with cancancan? - ruby-on-rails

I'm managing user and admin roles with cancan. I want the user to have access to certain views ("Cursos", "Credenciales", ) but cancan does not allow it. How can I give them access? So, what is happening is that it tries to access the route but goes back to the root as I specified it to do it in the application controller. (Of course it is supposed to access to the controller through that route.) Thanks for your help!!
index.html.erb
<% if current_user %>
<% if can? :new, #user %>
<% if can? :new, #empleado %>
<li><%= link_to "Lista de Empleados", empleados_path %></li>
<li> <%= link_to "Agregar Empleado", new_empleado_path %></li>
<% end %>
<% end %>
<li><%= link_to "Cursos", cursovence_path %></li>
<li><%= link_to "Credenciales", credencialvence_path %></li>
<% end %>
ability.rb
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.admin?
can :manage, :all
else
can :read, :all
can :display, :all
end
end
routes.rb
devise_for :users
root 'empleados#index'
resources :empleados
resources :users
post '/empleados/:id' => 'empleados#display'
get '/cursovence' => 'empleados#indexCursoVence'
get '/credencialvence' => 'empleados#indexCredencialVence'
get '/proxvacas' => 'empleados#indexProxVacas'
empleados_controller.rb
class EmpleadosController < ApplicationController
before_action :authenticate_user!
# load_and_authorize_resource - It did not work with this validation
load_resource #So, I changed it for only this one
def index
....
end
def new
....
end
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from CanCan::AccessDenied do |exception|
flash[:error] = "Access denied."
redirect_to root_url
end
end

In cancan you can assign abilities based on symbols not classes (in case you don't have models to base your abilities on) https://github.com/ryanb/cancan/wiki/Non-RESTful-Controllers
So in your view check for can? :index, :cursos and can? :index, :credenciales given that you added can :read, :all in your ability
<% if can? :index, :cursos %>
<li><%= link_to "Cursos", cursovence_path %></li>
<% end %>
<% if can? :index, :credenciales %>
<li><%= link_to "Credenciales", credencialvence_path %></li>
<% end %>

Related

No route matches [GET] for put request to custom action

I have a users model and controller. I am trying to make a put and delete request to my controller where the put request is tied to users#allow and the delete request is suppose to be directed to users#destroy. I However keep on getting this error when I make the request:
No route matches [GET] "/users/1/allow"
I am also using devise for users and admin classes so that might have something to do with this error. Im not really sure. Here is my routes.rb:
Rails.application.routes.draw do
get 'users/index'
put 'users/:id/allow', :to => 'users#allow', :as=>:users_allow
delete 'users/:id/destroy', :to => 'users#destroy',:as => :users_destroy
devise_for :users
devise_for :admins
root 'website#show'
resources :tweets do
put '/pass', :to => 'tweets#pass',:as => :pass
put '/fail', :to => 'tweets#fail',:as => :fail
end
get '/to_json', :to=>'tweets#to_json'
end
Here is my users controller:
class UsersController < ApplicationController
before_action :find_user, only:[:allow,:destroy]
before_filter :authenticate_admin!
def index
#users = User.all
end
def allow
find_user.update(:edit_permission=>true)
redirect_to(:back)
end
def destroy
find_user.destroy
redirect_to(:back)
end
private
def find_user
#user = User.find(params[:user_id])
end
end
And here is my users view where I am calling from, users/index.html.erb:
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>
<ul>
<% #users.each do |user|%>
<% if user.edit_permission == nil%>
<li style="color:red"> <%=link_to 'approve', users_allow_path(user), method: :put %> <%=link_to 'delete', users_destroy_path(user), method: :delete %><%= "#{user.name} #{user.email}"%> </li>
<% else%>
<li style="color:green"><%=link_to 'delete', users_destroy_path(user), method: :delete %><%= "#{user.name} #{user.email}"%> </li>
<% end%>
<%end%>
</ul>
I also want to add that when I go into the view and I do an inspect element in my browser for the link for the put request I do see this:
<a rel="nofollow" data-method="put" href="/users/1/allow">approve</a>
I would run rake routes to see your actual routes, because in your routes.rb you actually don't have a route that matches a get with users/:id/allow
#config/routes.rb
resources :users, only: [:index, :destroy] do
put :allow, action: :allow #-> url.com/users/:user_id/allow
end
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def allow
user.update edit_permission: true
redirect_to :back
end
private
def user
User.find params[:user_id] #-> why return an instance var if you aren't going to use it
end
end
#app/views/users/index.html.erb
<ul>
<% #users.each do |user| %>
<% options = {} %>
<% options.allow = ["allow", "red", "put"] %>
<% options.destroy = ["destroy", "green", "delete"] %>
<% option = user.edit_permission ? options.allow : [options.allow, options.destroy] %>
<% option.each do |action,colour,method| %>
<%= link_to action, eval("users_#{action}_path(user)"), method: method, class: colour %><%= "#{user.name} #{user.email}") %>
<% end %>
<% end %>
</ul>
--
To fix your actual problem, you need to make sure you have your Rails UJS driver loaded:
#app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require_tree .

undefined method `each' for nil:NilClass within Pages#friends

I am trying to get my followers to display on the pages/friends however, I keep getting multiple errors like an undefined method `each' for nil:NilClass in my Pages#friends error. I am following Michael Harlts tutorials on follow/unfollow however, I needed to tweak a couple of things because I did not follow the entire thing.
When a user goes to their Friends link (pages/friends) I want it to display everyone who is following them. Like how my users/index displays everyone. Please see my code below. Any help of suggestions would be great.
Pages/friends
<h1>Friends</h1>
<% #user ||= current_user %>
<% #users.each do |user| %>
<center><%= link_to image_tag(user.avatar.url(:thumb)), user %></center>
<strong><center><br><%= link_to user.name, user %></br></center></strong>
<% if current_user.admin %>
<% end %>
<% end %>
Pages/controller
def home
end
def about
end
def friends
end
end
**Users/index*
<% provide(:title, 'All users') %>
<h1>All users</h1>
<div class="col-md-offset-4 col-med-8">
<div class="panel panel-default">
<div class="panel-heading center">
<% #users.each do |user| %>
<center><%= link_to image_tag(user.avatar.url(:thumb)), user %></center>
<strong><center><br><%= link_to user.name, user %></br></center></strong>
<% if current_user.admin %>
<% #user ||= current_user %>
<div class="stats">
<a href="<%= friends_path(#user) %>">
<strong id="following" class="stat">
<%= #user.followed_users.count %>
</strong>
following
</a>
<a href="<%= friends_path(#user) %>">
<strong id="followers" class="stat">
<%= #user.followers.count %>
</strong>
followers
</a>
</div>
<center><%= link_to "Delete", user, method: :delete, data: { confirm: "Are you sure?" } %></center>
<% end %>
<% end %>
<div class="center">
<%= will_paginate #users, renderer: BootstrapPagination::Rails %>
</div>
</div>
Routes
devise_for :admins
devise_for :users
resources :posts
resources :users do
member do
get :following, :followers
end
end
resources :relationships, only: [:create, :destroy]
resources :user_friendships do
member do
put :accept
end
end
get "users/show"
root "pages#home"
get 'feed', to: 'posts#index', as: :feed
get "about" => "pages#about"
get "friends" => 'pages#friends'
match 'users/:id' => 'users#show', via: :get
match 'users/:id' => 'users#index', via: :destroy
match 'users/:id' => 'users#destroy', via: :get
match 'users/:id' => 'users#destroy', via: :delete
get '/:id', to: 'users#show'
Users/controller
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update, :destroy, :following, :followers]
before_action :authenticate_user!, except: [:index, :show]
before_action :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page], :per_page => 5)
end
def show
#user = User.find(params[:id])
if #user
#posts = #user.posts.all
render actions: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Your account has been deleted."
redirect_to root_path
end
def correct_user
#user = User.find(params[:id])
redirect_to root_path
end
def admin_user
redirect_to root_path unless current_user.admin?
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
end
Relationships Controller
class RelationshipsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
The error undefined method 'each' for nil:NilClass means that you're calling .each on an object that is nil.
For example, here, #users could be nil:
def index
#users = User.paginate(page: params[:page], :per_page => 5)
end
If you then call <% #users.each ... %>, you'll get the undefined method error. You've posted quite a lot of code above, so it's difficult to know exactly where that error is being thrown. Try and work out exactly where the error is being thrown (posting a stack trace would help), and work out which object is nil. Then work out why it's nil - is there no data? Or is your ActiveRecord query returning no results?
Hopefully that gives you some pointers as to where to start looking next.
Edit: As mmichael points out, the error is being caused due to the fact that #users was undeclared in pages#friends. Thanks!
As you're new, I'll give you another opinion. What I'm going to write is similar to Sam's answer; I intend to help you appreciate how Rails works, to further your experience
Objects
You must remember that Rails is built on top of Ruby - an object-orientated language. This means every time you declare / use a variable inside Ruby, you're actually accessing an object, which Ruby treats as having a series of methods:
Ruby is pure object-oriented language and everything appears to Ruby
as an object. Every value in Ruby is an object, even the most
primitive things: strings, numbers and even true and false. Even a
class itself is an object that is an instance of the Class class. This
chapter will take you through all the major functionalities related to
Object Oriented Ruby.
I write this because Ruby's object-orientated structure is much different than handling variables in the "traditional" sense -- in the simplest description, it means that Ruby presumes that objects are ALWAYS present (it just builds them from the NilClass), allowing you call "empty" data sets without running into "undeclared" issues
The problems occur when you try and run methods like .each on empty / nil objects. If you do this, errors like the one you alluded to occur; preventing you from being able to perform the functionality which you intended
--
Fix
Your error basically means you haven't declared your variable.
After seeing your code, the most obvious problem will be here:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def friends
# normally, you'd define your #instance_variables here
end
end
I see that your friends action does not declare your #users variable, which is required to perform the #users.each method. As recommended by Sam, the first step is to ensure you're able to declare this value, allowing you to loop through it as required:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def friends
#users = User.all
end
end

Can Can with Role Model restriction

I'm Trying to achieve that the user can see only HIS applications, and not the "Application Index" containing all the applications from all Users.
My Header:
<%= link_to "My Application ", current_user.application %>
My Abilities:
if user.has_role? :speaker
can :read, Application do |application| # heres the problem
application.try(:user) == user
end
can :create, Application
can :update, Application do |application|
application.try(:user) == user
end
end
Currently the User can access the Application/Index page. But the restriction should give him only access to:
http://localhost:3000/applications/8 # 8 being his application
What am i missing ?
Might be not the cleanest way, but this works:
in index action:
if current_user.has_role? :admin
#applications = Application.all
else
#applications = current_user.application
end
in index.html.erb
<% if current_user.has_role? :admin %>
<%= render #applications %>
<% else %>
<% #applications.title %> etc..
<% end %>
Note: current_user.application is because user model has_one :application

Ruby on Rails: uninitialized constant AdminController

I'm currently trying to set up an admin page. I'm creating a form in the page where I can update user's profile using checkboxes, but when I tried to submit, I get sent to a routing error page with uninitialized constant AdminController
my routes.rb
namespace :admin do
get '', to: 'dashboard#index', as: '/'
end
resources :admin do
collection do
post :edit_multiple
put :update_multiple
end
end
controllers/admin/dashboard_controller.rb
class Admin::DashboardController < ApplicationController
def index
#users = User.all
#admin = User.new
end
def edit_multiple
end
def update_multiple
end
end
views/admin/dashboard/index.html.erb
<%= form_tag edit_multiple_admin_index_path do |f| %>
<table>
<% #users.each do |user| %>
<% if !user.public %>
<tr>
<td><%= check_box_tag "user_ids[]", user.id %></td>
</tr>
<% end %>
<% end %>
</table>
<%= submit_tag "Edit Checked" %>
<% end %>
Anyone know when I'm getting this error?
Thanks!
Change your routes.rb file to:
namespace :admin do
get '', to: 'dashboard#index', as: '/'
resource :dashboard do
post :edit_multiple
put :update_multiple
end
end

Issues with Cancan and rails_admin with devise

So I have setup rails_admin with devise and cancan, I have it so that only admins can access the /admin pages.
But when trying to only show certain code to admins using <% if user_admin? %> I get undefined methoduser_admin?' for`
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
can :access, :rails_admin #grant access to rails_admin
can :dashboard #gives access to the dashboard
else
can :read, :all
end
end
end
_header.html.erb
<% if user_admin? %>
<li><%= link_to 'Settings', edit_user_registration_path %></li>
<li><%= link_to 'Logout', destroy_user_session_path, method: :delete %></li>
<% else %>
<li><%= link_to "Create Account", new_user_registration_path %></li>
<li><%= link_to "Login", new_user_session_path %></li>
<% end %>
I needed to set the abilities up in the ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
can :access, :rails_admin #grant access to rails_admin
can :dashboard #gives access to the dashboard
else
can :read, :all
end
end
end
Then was able to just call
<% if can? :access, :rails_admin %>
<li><%= link_to 'Admin', rails_admin_path %></li>
<% end %>
I can now do if can? on anything I want to authorize.

Resources