Skip to content

Instantly share code, notes, and snippets.

@momer
Last active December 15, 2015 11:18
Show Gist options
  • Save momer/5251526 to your computer and use it in GitHub Desktop.
Save momer/5251526 to your computer and use it in GitHub Desktop.
As promised, here's the collaboration set-up I created with CanCan. I'd found the original idea through a suggestion in stack overflow (which even had some ideas for tests listed), which I then heavily adapted to my use case. Tests pass below; next steps for anyone else looking to use something like this is just implementing the fancy footwork i…
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # IE: Guest user isn't signed in, create a user
if user.try(:is?, :admin)
can :manage, :all
elsif !user.roles.empty? && user.approved?
# Any user can edit Reports if they own said report.
can :manage, Report, user_id: user.id
# Any user can READ a Report, as long as they are a collaborator on the report,
# and their access level is 'read_only_participation'
can :read, Report, collaborators: { user_id: user.id, read_only_participation: true }
# Any user can update, read, and view version history for Reports which they're collaborators on, AND
# aren't restricted to read_only_participation status
can [:version_history, :update, :read], Report, collaborators: { user_id: user.id, read_only_participation: false }
# Users can view who the other collaborators are on a report, provided that they either own the report,
# or they are a collaborator on the report
can :read, Collaborator do |collaborator|
((user.reports.include? collaborator.report) || (user.collaborations.collect(&:report).include? collaborator.report))
end
# ONLY users who are the owners of the report can add/edit/delete collaborators from the report.
# This functionality is required for my purposes - you might want to remove it.
can :manage, Collaborator, report: { user_id: user.id }
# Users can update their own information
can :update, :users, id: user.id
can :manage, Project, report: { user_id: user.id }
can :manage, Deliverable, project: {report: { user_id: user.id } }
end
end
end
require 'spec_helper'
require "cancan/matchers"
describe Ability do
let(:fiscal_year) { FiscalYear.new(name: "99") }
let(:user) { FactoryGirl.create(:user, :regular_user) }
let(:new_user) { FactoryGirl.create(:user, :regular_user) }
let(:admin) { FactoryGirl.create(:user, :admin) }
# I should probably look into the reason behind DatabaseCleaner's failing to
# act as expected, but for now I'll work around it with the below
before do
if fiscal_year.valid?
fiscal_year.save
end
end
context "admin" do
subject { admin.ability }
it { should be_able_to(:manage, :all) }
end
context "user" do
let(:report) { Report.create(name: "#{SecureRandom.hex(6).to_s}",
fiscal_year_id: FiscalYear.find_by_name("99").id) }
let(:new_report) { new_user.reports.create(name: "#{SecureRandom.hex(6).to_s}",
fiscal_year_id: FiscalYear.find_by_name("99").id) }
let(:user_report) { user.reports.create(name: "#{SecureRandom.hex(6).to_s}",
fiscal_year_id: FiscalYear.find_by_name("99").id) }
subject { user.ability }
it { should be_able_to(:read, user_report) }
it "cannot manage reports without collaborations" do
should_not be_able_to(:manage, report)
end
describe "cannot manage reports only others collaborated on" do
before { Collaborator.create { |c| c.user_id = new_user.id } }
it { should_not be_able_to(:manage, report) }
end
context "cannot manage reports with read-only access" do
before do
Collaborator.create do |c|
c.user_id = user.id
c.read_only_participation = true
c.report_id = report.id
end
end
it { should_not be_able_to(:manage, report) }
end
describe "can read reports with read-only access" do
before do
Collaborator.create(user_id: user.id, report_id: report.id, read_only_participation: true)
end
it { should be_able_to(:read, report) }
end
describe "can manage report with collobaration" do
before do
Collaborator.create(user_id: user.id, report_id: new_report.id)
end
it { should be_able_to(:update, new_report) }
end
describe "cannot manage a reports collaborators unless it is the creator" do
before do
Collaborator.create do |c|
c.user_id = user.id
c.report_id = new_report.id
end
end
it { should_not be_able_to(:manage, new_report.collaborators) }
end
describe "Reports that user owns" do
before do
Collaborator.create do |c|
c.user_id = new_user.id
c.report_id = user_report.id
end
end
it "should be able to manage collaborators" do
user_report.collaborators.each do |collaborator|
should be_able_to(:manage, collaborator)
end
end
end
end
end
class Collaborator < ActiveRecord::Base
belongs_to :report
belongs_to :user
attr_accessible :read_only_participation, :user_id, :report_id
validates :user_id, presence: true, uniqueness: { scope: :report_id }
validates :report_id, presence: true
end
class Report < ActiveRecord::Base
attr_accessible :collaborators_attributes
has_many :collaborators
belongs_to :user
accepts_nested_attributes_for :collaborators, allow_destroy: true
end
class User < ActiveRecord::Base
delegate :can?, :cannot?, :to => :ability
devise ...
has_many :reports, dependent: :nullify
has_many :collaborations, :class_name => "Collaborator"
attr_accessible ...
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment