You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
However, when defining more than one of these and using accessible_by, CanCanCan cannot combine them (as stated in the documentation). For my project this is a severely limiting factor in how we can use CanCanCan.
I have made an extension to CanCanCan which does allow these scopes to be combined by redefining override_scope in ActiveRecordAdapter.
defoverride_scopeconditions=@compressed_rules.map(&:conditions).compactreturnunlessconditions.any?{ |c| c.is_a?(ActiveRecord::Relation)}returnconditions.firstifconditions.size == 1relation_conditions,hash_conditions=conditions.partition{ |c| c.is_a?(ActiveRecord::Relation)}# Build the relation for the hash conditionsifhash_conditions.any?if@model_class.respond_to?(:where) && @model_class.respond_to?(:joins)relation_conditions << build_relation(*hash_conditions)elserelation_conditions << @model_class.all(conditions: hash_conditions,joins: joins)endend# Compact queries to the largest cliques using or. Stay in-place to avoid array element moving due to deletion (performance)(0..relation_conditions.length).eachdo |i|
(i+1..relation_conditions.length).eachdo |j|
nextifrelation_conditions[j].nil?beginrelation_conditions[i]=relation_conditions[i].or(relation_conditions[j])relation_conditions[j]=nilrescueArgumentErrornextendendend# Reduce to single query with unionrelation_conditions.reduce(nil)do |a,b|
ifa.nil?belsifb.nil?aelsea.union(b)endendend
This way CanCanCan can be used with multiple scopes without issues.
I would propose this as a PR but there is quite some polishing required to get it to the right place. Thus I decided to first open this issue to ask:
Are you interested in such an extension?
It currently relies on the activerecord union gem which adds union support. Without union I would need to rewrite it to use subqueries (@model_class.where(id: a).or(@model_class.where(id: b))). Is that still acceptable?
The code to combine scopes using or is a bit ugly. Perhaps that should be turned into a separate class for compression? It is an approximation algorithm for creating the largest possible combinations of queries, relying on ActiveRecord to do the heavy lifting with its or operation. It yields pretty good results, though there are a few issues:
I had assumed it would not care about order, but A.joins(:b).or(A.where(something: 1)) => fine, while A.where(something: 1).or(A.joins(:b)) => error. I consider this a bug in ActiveRecord (either things are structurally compatible or they are not), should we work around it in any way?
Rails 7 adds a way to check before combining whether things are structurally compatible which would remove the need for the begin-rescue, but I would not want to add a dependency on such a new version of ActiveRecord.
Would love to hear your thoughts.
The text was updated successfully, but these errors were encountered:
It is stated in the documentation that you can use scopes (+blocks) as follows:
However, when defining more than one of these and using
accessible_by
, CanCanCan cannot combine them (as stated in the documentation). For my project this is a severely limiting factor in how we can use CanCanCan.I have made an extension to CanCanCan which does allow these scopes to be combined by redefining
override_scope
inActiveRecordAdapter
.This way CanCanCan can be used with multiple scopes without issues.
I would propose this as a PR but there is quite some polishing required to get it to the right place. Thus I decided to first open this issue to ask:
@model_class.where(id: a).or(@model_class.where(id: b))
). Is that still acceptable?or
is a bit ugly. Perhaps that should be turned into a separate class for compression? It is an approximation algorithm for creating the largest possible combinations of queries, relying on ActiveRecord to do the heavy lifting with its or operation. It yields pretty good results, though there are a few issues:A.joins(:b).or(A.where(something: 1))
=> fine, whileA.where(something: 1).or(A.joins(:b))
=> error. I consider this a bug in ActiveRecord (either things are structurally compatible or they are not), should we work around it in any way?Would love to hear your thoughts.
The text was updated successfully, but these errors were encountered: