Skip to content

Commit

Permalink
Leverage accessible_by from can? when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
oboxodo committed Jan 23, 2023
1 parent 7c99c59 commit 10eba81
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 4 deletions.
13 changes: 13 additions & 0 deletions lib/cancan/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,26 @@ module Ability
# Also see the RSpec Matchers to aid in testing.
def can?(action, subject, attribute = nil, *extra_args)
match = extract_subjects(subject).lazy.map do |a_subject|
rules_for_query = rules_except_cannot_with_attributes(action, a_subject)
subject_class = subject_class?(a_subject) ? a_subject : a_subject.class
if can_use_accessible_by?(subject_class, a_subject, rules_for_query)
next subject_class.accessible_by(self, action).exists?(a_subject.send(subject_class.primary_key)) ? rules_for_query.first : nil
end

relevant_rules_for_match(action, a_subject).detect do |rule|
rule.matches_conditions?(action, a_subject, attribute, *extra_args) && rule.matches_attributes?(attribute)
end
end.reject(&:nil?).first
match ? match.base_behavior : false
end

def can_use_accessible_by?(subject_class, subject, rules)
subject_class.respond_to?(:accessible_by) &&
defined?(ActiveRecord::Base) && subject.is_a?(ActiveRecord::Base) &&
!subject.new_record? &&
rules.none?(&:only_block?)
end

# Convenience method which works the same as "can?" but returns the opposite value.
#
# cannot? :destroy, @project
Expand Down
12 changes: 8 additions & 4 deletions lib/cancan/ability/rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,21 @@ def relevant_rules_for_match(action, subject)
end

def relevant_rules_for_query(action, subject)
rules = relevant_rules(action, subject).reject do |rule|
# reject 'cannot' rules with attributes when doing queries
rule.base_behavior == false && rule.attributes.present?
end
rules = rules_except_cannot_with_attributes(action, subject)
if rules.any?(&:only_block?)
raise Error, "The accessible_by call cannot be used with a block 'can' definition." \
"The SQL cannot be determined for #{action.inspect} #{subject.inspect}"
end
rules
end

def rules_except_cannot_with_attributes(action, subject)
relevant_rules(action, subject).reject do |rule|
# reject 'cannot' rules with attributes when doing queries
rule.base_behavior == false && rule.attributes.present?
end
end

# Optimizes the order of the rules, so that rules with the :all subject are evaluated first.
def optimize_order!(rules)
first_can_in_group = -1
Expand Down

0 comments on commit 10eba81

Please sign in to comment.