DEV Community 👩‍💻👨‍💻

Cover image for A Cut Above: CanCanCan
Evan Black
Evan Black

Posted on • Updated on • Originally published at

A Cut Above: CanCanCan

Developing a web application with multiple user types can lead to tons of headaches about authorization. Trying to come up with my own solution led to lots of logic in before_actions in controllers, and even more awkward logic in views shared by multiple user types that required slight differences. This mess also extended throughout multiple files, making it even worse.

Fed up, I found a gem called CanCanCan that made authorization so much simpler. Rather than lots of checks spread throughout your codebase, CanCanCan keeps it all together with an intuitive DSL.

I've set up a typical example below. I'd normally explain a code segment before, but I think CanCanCan's DSL is so easy to read that it does not require anything but the few comments.

# app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    alias_action :create, :read, :update, :destroy, to: :crud

    if user.is_admin? or user.is_moderator?
      # Admins and mods can CRUD any post
      can :crud, Post
      # Normal users can CRUD their own post
      can :crud, Post, user_id:
      # But they can only read others
      can :read, Post
Enter fullscreen mode Exit fullscreen mode
# app/controllers/posts_controller

class PostsController < ApplicationController
  # Your normal controller methods go here
Enter fullscreen mode Exit fullscreen mode

And that's it as far as controllers go. What about views, though?

Let's say you want admins and moderators to be the only ones to see the delete button for the post (within the post partial).

<%# app/views/posts/_post.html.erb %>

<% if can? :destroy, post %>
  <%= link_to 'Delete', post, method: :delete, class: 'btn btn-danger' %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

This is so much better than checking a user's class without understanding what the context for the check is.

Something to remember, though: CanCanCan's automagic-ness relies on standard Rails naming practices. If, however, you find yourself breaking the standard a bit, the gem offers plenty of options to work with almost any situation.

I can not recommend this gem enough to anybody designing a web app with multiple user types. CanCanCan is a real gem of a gem, especially if you use Devise.

This is a cross-post from my blog.

Top comments (1)

thorstenhirsch profile image
Thorsten Hirsch

full ack. I also use CanCanCan with Devise in my rails app. Integration was straightforward and it works like a charm. Sometimes I still wonder if I implemented all requirements, because it took so little time.

Image description

Join the One Year Club

You can earn this badge by being a registered member of the DEV Community for at least one year. Create an account and get started today.