-
-
Notifications
You must be signed in to change notification settings - Fork 638
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Accessible by strategy: Exists subquery (#691)
* Added the new strategy: double_exist_subquery * Accessible by strategy: Each rule as exists subquery * Use aliased joined table instead of double sub query * Solved offences and specifically solved complexity offences by extracting code out into separate strategy classes * Extracted existing strategies out into separate classes as well * Fixed more offences and cleaned up * Removed unused file * Fixed spec * Fixed specs * Select column through table name * Fixed more specs * Fixed offences * Fixed SQL in spec * Unscoped might remove existing settings on the query * Limit results to 1 * Added limit in spec as well * Fixed SQL * Just use JOIN both places * Fixed several issues after merging with latest version of develop
- Loading branch information
Showing
6 changed files
with
184 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
lib/cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
module CanCan | ||
module ModelAdapters | ||
class Strategies | ||
class JoinedAliasEachRuleAsExistsSubquery < Base | ||
def execute! | ||
model_class | ||
.joins( | ||
"JOIN #{quoted_table_name} AS #{quoted_aliased_table_name} ON " \ | ||
"#{quoted_aliased_table_name}.#{quoted_primary_key} = #{quoted_table_name}.#{quoted_primary_key}" | ||
) | ||
.where(double_exists_sql) | ||
end | ||
|
||
def double_exists_sql | ||
double_exists_sql = '' | ||
|
||
compressed_rules.each_with_index do |rule, index| | ||
double_exists_sql << ' OR ' if index > 0 | ||
double_exists_sql << "EXISTS (#{sub_query_for_rule(rule).to_sql})" | ||
end | ||
|
||
double_exists_sql | ||
end | ||
|
||
def sub_query_for_rule(rule) | ||
conditions_extractor = ConditionsExtractor.new(model_class) | ||
rule_where_conditions = extract_multiple_conditions(conditions_extractor, [rule]) | ||
joins_hash, left_joins_hash = extract_joins_from_rule(rule) | ||
sub_query_for_rules_and_join_hashes(rule_where_conditions, joins_hash, left_joins_hash) | ||
end | ||
|
||
def sub_query_for_rules_and_join_hashes(rule_where_conditions, joins_hash, left_joins_hash) | ||
model_class | ||
.select('1') | ||
.joins(joins_hash) | ||
.left_joins(left_joins_hash) | ||
.where( | ||
"#{quoted_table_name}.#{quoted_primary_key} = " \ | ||
"#{quoted_aliased_table_name}.#{quoted_primary_key}" | ||
) | ||
.where(rule_where_conditions) | ||
.limit(1) | ||
end | ||
|
||
def extract_joins_from_rule(rule) | ||
joins = {} | ||
left_joins = {} | ||
|
||
extra_joins_recursive([], rule.conditions, joins, left_joins) | ||
[joins, left_joins] | ||
end | ||
|
||
def extra_joins_recursive(current_path, conditions, joins, left_joins) | ||
conditions.each do |key, value| | ||
if value.is_a?(Hash) | ||
current_path << key | ||
extra_joins_recursive(current_path, value, joins, left_joins) | ||
current_path.pop | ||
else | ||
extra_joins_recursive_merge_joins(current_path, value, joins, left_joins) | ||
end | ||
end | ||
end | ||
|
||
def extra_joins_recursive_merge_joins(current_path, value, joins, left_joins) | ||
hash_joins = current_path_to_hash(current_path) | ||
|
||
if value.nil? | ||
left_joins.deep_merge!(hash_joins) | ||
else | ||
joins.deep_merge!(hash_joins) | ||
end | ||
end | ||
|
||
# Converts an array like [:child, :grand_child] into a hash like {child: {grand_child: {}} | ||
def current_path_to_hash(current_path) | ||
hash_joins = {} | ||
current_hash_joins = hash_joins | ||
|
||
current_path.each do |path_part| | ||
new_hash = {} | ||
current_hash_joins[path_part] = new_hash | ||
current_hash_joins = new_hash | ||
end | ||
|
||
hash_joins | ||
end | ||
end | ||
end | ||
end | ||
end |
29 changes: 29 additions & 0 deletions
29
lib/cancan/model_adapters/strategies/joined_alias_exists_subquery.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
module CanCan | ||
module ModelAdapters | ||
class Strategies | ||
class JoinedAliasExistsSubquery < Base | ||
def execute! | ||
model_class | ||
.joins( | ||
"JOIN #{quoted_table_name} AS #{quoted_aliased_table_name} ON " \ | ||
"#{quoted_aliased_table_name}.#{quoted_primary_key} = #{quoted_table_name}.#{quoted_primary_key}" | ||
) | ||
.where("EXISTS (#{joined_alias_exists_subquery_inner_query.to_sql})") | ||
end | ||
|
||
def joined_alias_exists_subquery_inner_query | ||
model_class | ||
.unscoped | ||
.select('1') | ||
.left_joins(joins) | ||
.where(*where_conditions) | ||
.where( | ||
"#{quoted_table_name}.#{quoted_primary_key} = " \ | ||
"#{quoted_aliased_table_name}.#{quoted_primary_key}" | ||
) | ||
.limit(1) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters