Skip to content

Commit 7c99c59

Browse files
authored
Allow to disable rules compressor (#798)
1 parent 8efb522 commit 7c99c59

File tree

6 files changed

+59
-10
lines changed

6 files changed

+59
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* [#653](https://github.com/CanCanCommunity/cancancan/pull/653): Add support for using an nil relation as a condition. ([@ghiculescu][])
44
* [#702](https://github.com/CanCanCommunity/cancancan/pull/702): Support scopes of STI classes as ability conditions. ([@honigc][])
5+
* [#798](https://github.com/CanCanCommunity/cancancan/pull/798): Allow disabling of rules compressor via `CanCan.rules_compressor_enabled = false`. ([@coorasse][])
56

67
## 3.4.0
78

docs/rules_compression.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Rules compressions
22

3-
Your rules are optimized automatically at runtime. There are a set of "rules" to optimize your rules definition and they are implemented in the `RulesCompressor` class. Here you can see how this works:
3+
Database are great on optimizing queries, but sometimes cancancan builds `joins` that might lead to slow performance.
4+
This is why your rules are optimized automatically at runtime.
5+
There are a set of "rules" to optimize your rules definition and they are implemented in the `RulesCompressor` class.
6+
You can always disable the rules compressor by setting `CanCan.rules_compressor_enabled = false` in your initializer.
7+
You can also enable/disable it on a specific check by using: `with_rules_compressor_enabled(false) { ... }`
8+
9+
Here you can see how this works:
410

511
A rule without conditions is defined as `catch_all`.
612

lib/cancan/config.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,29 @@ def self.valid_accessible_by_strategies
1111
strategies
1212
end
1313

14+
# You can disable the rules compressor if it's causing unexpected issues.
15+
def self.rules_compressor_enabled
16+
return @rules_compressor_enabled if defined?(@rules_compressor_enabled)
17+
18+
@rules_compressor_enabled = true
19+
end
20+
21+
def self.rules_compressor_enabled=(value)
22+
@rules_compressor_enabled = value
23+
end
24+
25+
def self.with_rules_compressor_enabled(value)
26+
return yield if value == rules_compressor_enabled
27+
28+
begin
29+
rules_compressor_enabled_was = rules_compressor_enabled
30+
@rules_compressor_enabled = value
31+
yield
32+
ensure
33+
@rules_compressor_enabled = rules_compressor_enabled_was
34+
end
35+
end
36+
1437
# Determines how CanCan should build queries when calling accessible_by,
1538
# if the query will contain a join. The default strategy is `:subquery`.
1639
#

lib/cancan/model_adapters/active_record_adapter.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ def self.version_lower?(version)
1515

1616
def initialize(model_class, rules)
1717
super
18-
@compressed_rules = RulesCompressor.new(@rules.reverse).rules_collapsed.reverse
18+
@compressed_rules = if CanCan.rules_compressor_enabled
19+
RulesCompressor.new(@rules.reverse).rules_collapsed.reverse
20+
else
21+
@rules
22+
end
1923
StiNormalizer.normalize(@compressed_rules)
2024
ConditionsNormalizer.normalize(model_class, @compressed_rules)
2125
end
@@ -39,8 +43,8 @@ def nested_subject_matches_conditions?(parent, child, all_conditions)
3943
def parent_child_conditions(parent, child, all_conditions)
4044
child_class = child.is_a?(Class) ? child : child.class
4145
foreign_key = child_class.reflect_on_all_associations(:belongs_to).find do |association|
42-
association.klass == parent.class
43-
end&.foreign_key&.to_sym
46+
association.klass == parent.class
47+
end&.foreign_key&.to_sym
4448
foreign_key.nil? ? nil : all_conditions[foreign_key]
4549
end
4650
end

spec/cancan/model_adapters/active_record_adapter_spec.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,12 +393,26 @@ class User < ActiveRecord::Base
393393
expect(@ability.model_adapter(Article, :read).joins).to be_nil
394394
end
395395

396-
it 'has nil joins if rules got compressed' do
397-
@ability.can :read, Comment, article: { category: { visible: true } }
398-
@ability.can :read, Comment
399-
expect(@ability.model_adapter(Comment, :read))
400-
.to generate_sql("SELECT \"#{@comment_table}\".* FROM \"#{@comment_table}\"")
401-
expect(@ability.model_adapter(Comment, :read).joins).to be_nil
396+
context 'if rules got compressed' do
397+
it 'has nil joins' do
398+
@ability.can :read, Comment, article: { category: { visible: true } }
399+
@ability.can :read, Comment
400+
expect(@ability.model_adapter(Comment, :read))
401+
.to generate_sql("SELECT \"#{@comment_table}\".* FROM \"#{@comment_table}\"")
402+
expect(@ability.model_adapter(Comment, :read).joins).to be_nil
403+
end
404+
end
405+
406+
context 'if rules did not get compressed' do
407+
before :each do
408+
CanCan.rules_compressor_enabled = false
409+
end
410+
411+
it 'has joins' do
412+
@ability.can :read, Comment, article: { category: { visible: true } }
413+
@ability.can :read, Comment
414+
expect(@ability.model_adapter(Comment, :read).joins).to be_present
415+
end
402416
end
403417

404418
it 'has nil joins if no nested hashes specified in conditions' do

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
config.after :each do
3232
CanCan.accessible_by_strategy = CanCan.default_accessible_by_strategy
33+
CanCan.rules_compressor_enabled = true
3334
end
3435
end
3536

0 commit comments

Comments
 (0)