I want to implement my own SQL strategy to fight some performance issues regarding subqueries and distinct keywords in the left_join strategy. My approach is, to skip the distinct as along as the all joins are belongs_to or has_one. This way, the query will run in 1/30 of the time on my system.
However, adding a custom strategy requires some monkey patching, which is quiet ugly:
# config/initializers/cancancan.rb
CanCan.module_eval do
class << self
alias _valid_accessible_by_strategies valid_accessible_by_strategies
def valid_accessible_by_strategies
_valid_accessible_by_strategies + [CanCanLeftJoinOptimizedWithFallback::STRATEGY_NAME]
end
end
end
Rails.configuration.to_prepare { CanCanLeftJoinOptimizedWithFallback.install }
# app/lib/can_can_left_join_optimized_with_fallback.rb
class CanCanLeftJoinOptimizedWithFallback < CanCan::ModelAdapters::Strategies::Base
STRATEGY_NAME = name.delete_prefix("CanCan").underscore.to_sym
class << self
def install
const = STRATEGY_NAME.to_s.camelize.to_sym
if CanCan::ModelAdapters::Strategies.const_defined?(const)
CanCan::ModelAdapters::Strategies.send(:remove_const, const)
end
CanCan::ModelAdapters::Strategies.const_set(const, self)
CanCan.accessible_by_strategy = STRATEGY_NAME
end
end
def execute!
# ...
end
end
Expected behavior
It would be nice to add a custom strategy like this:
Rails.configuration.to_prepare do
CanCan.accessible_by_strategy = CanCanLeftJoinOptimizedWithFallback
end
The setter could recognize if the argument is a class or a symbol and add it to the allowed strategies. It would also have to be compatible with Zeitwerks, so when the to_prepare hook from Rails is triggered during development, the constant is removed and added again like I do in my .install method.
I'll be happy to try for a PR, but I would need some guidance on how this could be implemented.
Actual behavior
> CanCan.accessible_by_strategy = CanCanLeftJoinOptimizedWithFallback
ArgumentError: accessible_by_strategy must be one of left_join, joined_alias_exists_subquery, joined_alias_each_rule_as_exists_subquery, subquery
Steps to reproduce
I want to implement my own SQL strategy to fight some performance issues regarding subqueries and distinct keywords in the left_join strategy. My approach is, to skip the distinct as along as the all joins are belongs_to or has_one. This way, the query will run in 1/30 of the time on my system.
However, adding a custom strategy requires some monkey patching, which is quiet ugly:
Expected behavior
It would be nice to add a custom strategy like this:
The setter could recognize if the argument is a class or a symbol and add it to the allowed strategies. It would also have to be compatible with Zeitwerks, so when the to_prepare hook from Rails is triggered during development, the constant is removed and added again like I do in my
.install
method.I'll be happy to try for a PR, but I would need some guidance on how this could be implemented.
Actual behavior
System configuration
Rails version: 7.0.8.1
Ruby version: 3.2.1
CanCanCan version 3.5.0