Tag Archives: ruby on rails

CanCan (CanCanCan)

CanCan was written by Ryan Bates to make role based security simple and fast for Ruby on Rails applications. It was first published in late 2009 and was the go to role based security gem for rails applications. There were several reasons why so many developers wanted to use it. Some of those reasons were because it was fast, easy to setup and worked well. Support for the original CanCan gem ended in mid 2013. Ryan Bates decided to take a break from development all together so the gem was forked by Bryan Rite and is being maintained by him today. This allowed the gem to work with the latest version of rails and is able to keep working when new versions of rails are released. As one of the developers that use’s CanCanCan I can say that it’s just as stable as it’s ever been. It’s a mature gem by this point in time so you won’t see too many breaking changes if any.

Features

One thing that I really love about CanCan is that it gives you an ability class that allows you to define all of your roles and what they can do in one place. You can define as many roles as you want and have them share operations with little effort. Also you are able to assign multiple roles to a user. This will give you the flexibility to only have one role for one purpose. Instead of mixing roles together to fit some custom role you need. You are able to just give the user two roles instead. Another thing that I found really useful is you can pass in a set of criteria that can be applied to the role check. For example a user creates a new record. That record belongs to that user and should only be able to be viewed or edited by that user. With CanCan you can simply pass in a scope like can :edit, ModelName, user_id: user.id. This will apply this scope to the check so only the user who created the record will be able to view or change it. This is really powerful because you can do it all in one place. If you want to setup multiple roles per user simply following this guide (Role-Based-Authorization). Here is how I implemented it based on that how to.


class User < ActiveRecord::Base
  ROLES = %i[role_1 role_2 role_3]

  attr_accessor :roles

  def ability
    @ability ||= Ability.new(self)
  end
  delegate :can?, :cannot?, to: :ability

  def roles=(roles)
    roles = [*roles].map { |r| r.to_sym }
    self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
  end

  def roles
    ROLES.reject do |r|
      ((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero?
    end
  end

  def has_role?(role)
    roles.include?(role)
  end
end

Lessons Learned

One thing I learned while working on my project using CanCan is you need to setup the scopes for the can statements. Originally when I had developed this project I did not add these scopes on. This gave any user the ability to edit any record they had access to. So for example a user was able to view and edit a record that they did not create. This would have caused major issues if it wouldn’t have been caught before going live. Here is an example on how you can provide a scope to your can statement.


can :show, Vehicle, id: user.vehicles.map { |vehicle| vehicle.id }

This will allow a user who’s user_id is in the vehicle table and only that user to view that record. So if a user decided to change the id in the url they will get the access denied error message. You can do this with any active record call as long as it ties back to a user.

Alternatives

When originally choosing CanCan I took the time to look into a couple of alternatives. One of the major players today in rails is Pundit. When Ryan decided to take a leave from the development world a lot of people started looking for an alternative to CanCan. Since it was no longer going to be maintained. A lot of people chose Pundit. I have never used or setup Pundit fully but while doing my research I couldn’t figure out a good way to assign multiple roles to one user. Leave a comment if this is actually simple and I just missed it in the Pundit documentation. I’m sure this is possible but I didn’t want to invest the time digging through the code to find out. CanCan had good documentation in how to do so. At the end of the day I encourage everybody to do their research and determine the solution that best fits their application. I’ve seen CanCan used in multiple applications and so far it’s been able to handle everything thrown at it.

Wallproductions on Rails

Wallproductions is a portal like site that will contain many applications. Currently it contains a couple of applications Gas Tracker and Budget Tracker. Both of these projects are live and are in production use. Over the years it’s gone through a couple of transformations. When it was first created it was a side project. It was something to work on during the weekends to see what was possible outside of what I was doing at work. The reason why the project started was due to a lack of existing applications that did what I wanted. So I set out to create my own.

When it was originally written it was done using PHP. It started out without an off the shel framework. This was a pretty good solution for a while until the project started to get larger. The larger it got the more I thought about it. I was thinking that a framework would be needed. Unless I wanted to spend all my weekends doing tedious things that were already done in the various frameworks. As the only developer working on the project it was important to be able to complete tasks with speed. So instead of creating everything myself I could use some shared code from the framework to handle all of the standard things. Things like ActiveRecord and Routing. So I looked around and I found a couple of frameworks. At the time the Yii framework seemed the most appropriate for my situation. The reason why I choose Yii instead of the other frameworks was simply because I was more familiar with how their ActiveRecord implementation worked. Also it had a pretty good extensions library so I could reuse code shared by others in the community. So I went with that and build the second version of Wallproductions off of it. This worked well and it stayed in the Yii framework for about two years.

During this period of time I change directions in my career and instead of doing PHP development I started doing Ruby development. Specifically working with Ruby on Rails. I did not know much about Ruby or Rails when I started. But after working with it for a short amount of time I was able to be more productive than ever before. Over the course of the next year Wallproductions stayed in the Yii framework. Making additional features and fixing existing bugs. Then the Yii framework introduced their new framework Yii 2. This was a complete rewrite of the framework. So I had a decision to make. Do I want to stay on the first version of Yii for a long time, do I want to rewrite the majority of my PHP code or just move to Ruby on Rails. Seeing that I work on Ruby on Rails on a daily basis it was an option. After carefully considering it I decided that I wanted to go with Ruby on Rails. So I set off on the path of rewriting my entire site using Ruby on Rails.

The first step was to gather all of the old requirements for the system. Then I needed to make sure that I met them. This was not very hard as I was the only developer on this project. Also since I wrote the entire application myself I didn’t need much documentation to understand it. Also I needed to set milestones for myself so I knew I was progressing. I didn’t want to get stuck and then have to go back. But I also didn’t want to keep spinning my wheels if it wasn’t going to work out. As I am sure you realize we all have had side projects that we start and then never finished. I decided early on that if I was going to put a ton of effort into converting Wallproductions I would finish.

The conversion itself actually went faster than I expected. I knew Ruby was very powerful and Rails added even more power to it. What I did not realize is how much power it added. I was able to convert something that I worked up for over three years in under three months. This was not your typical conversion either since it was going cross languages. Also I was not working on this conversion process full time. I was doing so like always on the weekends or nights after regular working hours. Now that I am on Ruby on Rails I am able to complete new tasks at greater speeds. At the end of the day I am glad that it’s on Ruby on Rails. I am a huge fan of the framework and the language. Also the community around it is really good.