Skip to content

Commit 10eba81

Browse files
committed
Leverage accessible_by from can? when possible
1 parent 7c99c59 commit 10eba81

File tree

2 files changed

+21
-4
lines changed

2 files changed

+21
-4
lines changed

lib/cancan/ability.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,26 @@ module Ability
7373
# Also see the RSpec Matchers to aid in testing.
7474
def can?(action, subject, attribute = nil, *extra_args)
7575
match = extract_subjects(subject).lazy.map do |a_subject|
76+
rules_for_query = rules_except_cannot_with_attributes(action, a_subject)
77+
subject_class = subject_class?(a_subject) ? a_subject : a_subject.class
78+
if can_use_accessible_by?(subject_class, a_subject, rules_for_query)
79+
next subject_class.accessible_by(self, action).exists?(a_subject.send(subject_class.primary_key)) ? rules_for_query.first : nil
80+
end
81+
7682
relevant_rules_for_match(action, a_subject).detect do |rule|
7783
rule.matches_conditions?(action, a_subject, attribute, *extra_args) && rule.matches_attributes?(attribute)
7884
end
7985
end.reject(&:nil?).first
8086
match ? match.base_behavior : false
8187
end
8288

89+
def can_use_accessible_by?(subject_class, subject, rules)
90+
subject_class.respond_to?(:accessible_by) &&
91+
defined?(ActiveRecord::Base) && subject.is_a?(ActiveRecord::Base) &&
92+
!subject.new_record? &&
93+
rules.none?(&:only_block?)
94+
end
95+
8396
# Convenience method which works the same as "can?" but returns the opposite value.
8497
#
8598
# cannot? :destroy, @project

lib/cancan/ability/rules.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,21 @@ def relevant_rules_for_match(action, subject)
6767
end
6868

6969
def relevant_rules_for_query(action, subject)
70-
rules = relevant_rules(action, subject).reject do |rule|
71-
# reject 'cannot' rules with attributes when doing queries
72-
rule.base_behavior == false && rule.attributes.present?
73-
end
70+
rules = rules_except_cannot_with_attributes(action, subject)
7471
if rules.any?(&:only_block?)
7572
raise Error, "The accessible_by call cannot be used with a block 'can' definition." \
7673
"The SQL cannot be determined for #{action.inspect} #{subject.inspect}"
7774
end
7875
rules
7976
end
8077

78+
def rules_except_cannot_with_attributes(action, subject)
79+
relevant_rules(action, subject).reject do |rule|
80+
# reject 'cannot' rules with attributes when doing queries
81+
rule.base_behavior == false && rule.attributes.present?
82+
end
83+
end
84+
8185
# Optimizes the order of the rules, so that rules with the :all subject are evaluated first.
8286
def optimize_order!(rules)
8387
first_can_in_group = -1

0 commit comments

Comments
 (0)