CanCan vs Pundit



CanCan vs Pundit

0 0


lyonrb_lt_pundit

LyonRB lightning talk about CanCan vs Pundit

On Github EtienneDepaulis / lyonrb_lt_pundit

CanCan vs Pundit

LyonRB lightning talks / @EtienneDepaulis

Context

  • Roles and Role management services
AuthorizeObjectService.new(user).can_write?(contract_from_user) # => true/false
  • Business logic services
BudgetLogicService.new(budget).can_update? # => true/false

Solution #1 : CanCan

alias_action :create, :update, :destroy, :to => :write
service = AuthorizeObjectService.new(user)

can :write, Budget do |budget|
  service.can_write?(budget)
end
alias_action :create, :update, :destroy, :to => :write
service = AuthorizeObjectService.new(user)

can :write, Budget do |budget|
  service.can_write?(budget)
end

cannot [:update, :destroy], Budget do |budget|
  !BudgetAbilitiesService.new(budget).can_update?
end

...
BudgetLogicService.new(budget).permitted_attributes # => [:id, :status, ...]

Strong Parameters :s

Index ???

Solution #2 : Pundit

Plain Old Ruby Object !

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index? ; true; end
  def show? ; false; end
  def create? ; false; end
  alias_method :new?, :create?
  ...
end

manage :all

def user_index? ; true; end
def user_show? ; false; end
  
def method_missing(method_sym, *arguments, &block)
  # to only catch create? and not user_create? (avoiding ∞ loops)
  if method_sym.to_s !~ /^user_/ && method_sym.to_s =~ /^(.*)*\?$/ 
    return true if user.admin?     
    public_send("user_#{method_sym}", *arguments)
  else
    false # by default, nothing is authorized
  end
end

can?(:create, @budget)

def create
  @budget = Budget.new(budget_params)
  @budget.creator = current_user
  authorize @budget
end
class BudgetPolicy < ApplicationPolicy

  def user_show?
    can_read?
  end

  def user_create?
    can_write?
  end
  alias_method :user_new?, :user_create?
  
end

scopes

class BudgetPolicy < ApplicationPolicy

  Scope = Struct.new(:user, :scope) do
    def resolve
      return scope if user.power?

      AuthorizeScopeService.new(user, scope).call.collection
    end
  end
end
@budgets = policy_scope(Budget)

RSpec :)

describe UserPolicy do
  subject { UserPolicy.new(current_user, object) }

  context "for a lambda user" do
    let(:current_user)  { build_stubbed :user }
    let(:object)        { build_stubbed :user }
    
    permits :none
  end
  
  context "for himself" do
    let(:current_user)  { build_stubbed :user }
    let(:object)        { current_user }
    
    permits [:edit, :update]
  end
  
  context "for a power user" do
    let(:current_user)  { build_stubbed :power_user }
    
    permits :all
  end
end

http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/

Yeaaahh !

On recrute :)

team@per-angusta.com

THE END

BY Etienne Depaulis

One More Thing ...

Services for Controllers

CreateUserService.new(@user).call
def create
  # initiate the service
  if success
    # send an email
    redirect_to users_path, success: "You rock !"
  else
    redirect_to users_path, error: "You suck ..."
  end
end