TL;DR overwrite as_json
on all your CanCan Ability classes.
Since I’ve started using squash for bug tracking I started noticing exceptions which prevented squash client from transmitting exceptions to the squash backend.
The exceptions squash was rescueing made squash raise a new exception!
Digging through the stack traces it became obvious that Ryan Bates cancan is the root of all evil.
To reproduce fire up irb
and paste the following lines:
# dependencies to reproduce
require 'cancan' # v1.6.10
require 'json' # v1.7.7
require 'active_support' # v3.2.12
require 'active_record'
require 'sqlite3' # v1.3.7
# we need active record
ActiveRecord::Base.establish_connection({
:adapter => "sqlite3",
:database => ":memory:"
})
# and two sample classes
class User < ActiveRecord::Base
end
ActiveRecord::Migration.create_table :users do |t|
end
class Project < ActiveRecord::Base
belongs_to :user
end
ActiveRecord::Migration.create_table :projects do |t|
t.belongs_to :user
end
# as well as an Ability
class Ability
include CanCan::Ability
def initialize user
user
can [:new, :create, :edit, :update, :destroy], Project do |project|
project.user == user
end
end
end
ability = Ability.new(User.new)
ActiveSupport::JSON.encode(ability) # => boom
You’ll see an ActiveSupport::JSON::Encoding::CircularReferenceError: object references itself
exception.
As far as I can tell this is due to the data structure CanCan is using to store the permissions.
Defining as_json
as follows solves this problem:
def as_json *args
return {
class_name: 'Ability',
user_id: null # your metadata here
}
end
I’d vote to make this a default in CanCan, but for the time being this solves my problem while still leaving enough debugging informations for me to reproduce error states.
That being said, I really like cancan - it’s a great gem. But those circular reference errors are something to watch out for.