lib/cancan/ability/actions.rb
# frozen_string_literal: true module CanCan module Ability module Actions # Alias one or more actions into another one. # # alias_action :update, :destroy, :to => :modify # can :modify, Comment # # Then :modify permission will apply to both :update and :destroy requests. # # can? :update, Comment # => true # can? :destroy, Comment # => true # # This only works in one direction. Passing the aliased action into the "can?" call # will not work because aliases are meant to generate more generic actions. # # alias_action :update, :destroy, :to => :modify # can :update, Comment # can? :modify, Comment # => false # # Unless that exact alias is used. # # can :modify, Comment # can? :modify, Comment # => true # # The following aliases are added by default for conveniently mapping common controller actions. # # alias_action :index, :show, :to => :read # alias_action :new, :to => :create # alias_action :edit, :to => :update # # This way one can use params[:action] in the controller to determine the permission. def alias_action(*args) target = args.pop[:to] validate_target(target) aliased_actions[target] ||= [] aliased_actions[target] += args end # Returns a hash of aliased actions. The key is the target and the value is an array of actions aliasing the key. def aliased_actions @aliased_actions ||= default_alias_actions end # Removes previously aliased actions including the defaults. def clear_aliased_actions @aliased_actions = {} end private def default_alias_actions { read: %i[index show], create: [:new], update: [:edit] } end # Given an action, it will try to find all of the actions which are aliased to it. # This does the opposite kind of lookup as expand_actions. def aliases_for_action(action) results = [action] aliased_actions.each do |aliased_action, actions| results += aliases_for_action(aliased_action) if actions.include? action end results end def expanded_actions @expanded_actions ||= {} end # Accepts an array of actions and returns an array of actions which match. # This should be called before "matches?" and other checking methods since they # rely on the actions to be expanded. def expand_actions(actions) expanded_actions[actions] ||= begin expanded = [] actions.each do |action| expanded << action if (aliases = aliased_actions[action]) expanded += expand_actions(aliases) end end expanded end end end end end