Skip to content

Commit

Permalink
Squeel, MetaWhere successor, support added from https://gist.github.c…
Browse files Browse the repository at this point in the history
  • Loading branch information
Sijawusz Pur Rahnama authored and Alex Dilley committed Feb 21, 2014
1 parent f15723e commit 6708da9
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 14 deletions.
16 changes: 16 additions & 0 deletions lib/cancan.rb
Expand Up @@ -11,3 +11,19 @@
require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
require 'cancan/model_adapters/data_mapper_adapter' if defined? DataMapper
require 'cancan/model_adapters/mongoid_adapter' if defined?(Mongoid) && defined?(Mongoid::Document)

if defined? Squeel
class String
include Squeel::Nodes::PredicateOperators
end

module Squeel
module Visitors
class PredicateVisitor < Visitor
def visit_String(o, parent)
Arel::Nodes::SqlLiteral.new(o)
end
end
end
end
end
56 changes: 43 additions & 13 deletions lib/cancan/model_adapters/active_record_adapter.rb
Expand Up @@ -6,21 +6,22 @@ def self.for_class?(model_class)
end

def self.override_condition_matching?(subject, name, value)
name.kind_of?(MetaWhere::Column) if defined? MetaWhere
name.kind_of?(Squeel::Nodes::Predicate) if defined? Squeel
end

def self.matches_condition?(subject, name, value)
subject_value = subject.send(name.column)
if name.method.to_s.ends_with? "_any"
value.any? { |v| meta_where_match? subject_value, name.method.to_s.sub("_any", ""), v }
elsif name.method.to_s.ends_with? "_all"
value.all? { |v| meta_where_match? subject_value, name.method.to_s.sub("_all", ""), v }
subject_value = subject.send(name.expr)
method_name = name.method_name.to_s
if method_name.ends_with? "_any"
value.any? { |v| squeel_match? subject_value, method_name.sub("_any", ""), v }
elsif method_name.ends_with? "_all"
value.all? { |v| squeel_match? subject_value, method_name.sub("_all", ""), v }
else
meta_where_match? subject_value, name.method, value
squeel_match? subject_value, name.method_name, value
end
end

def self.meta_where_match?(subject_value, method, value)
def self.squeel_match?(subject_value, method, value)
case method.to_sym
when :eq then subject_value == value
when :not_eq then subject_value != value
Expand All @@ -31,8 +32,8 @@ def self.meta_where_match?(subject_value, method, value)
when :gt then subject_value > value
when :gteq then subject_value >= value
when :matches then subject_value =~ Regexp.new("^" + Regexp.escape(value).gsub("%", ".*") + "$", true)
when :does_not_match then !meta_where_match?(subject_value, :matches, value)
else raise NotImplemented, "The #{method} MetaWhere condition is not supported."
when :does_not_match then !squeel_match?(subject_value, :matches, value)
else raise NotImplemented, "The #{method} Squeel condition is not supported."
end
end

Expand All @@ -54,11 +55,28 @@ def self.meta_where_match?(subject_value, method, value)
def conditions
if @rules.size == 1 && @rules.first.base_behavior
# Return the conditions directly if there's just one definition
tableized_conditions(@rules.first.conditions).dup
tableized_conditions(unsqueeled_conditions(@rules.first.conditions.dup))
else
@rules.reverse.inject(false_sql) do |sql, rule|
merge_conditions(sql, tableized_conditions(rule.conditions).dup, rule.base_behavior)
@rules.reverse.inject(false_sql) do |accumulator, rule|
conditions = tableized_conditions(unsqueeled_conditions(rule.conditions.dup))
if conditions.blank?
rule.base_behavior ? (accumulator | true_sql) : (accumulator & false_sql)
else
rule.base_behavior ? (accumulator | conditions) : (accumulator & -conditions)
end
end
end
end

def unsqueeled_conditions(conditions)
return conditions unless conditions.kind_of? Hash
conditions.inject({}) do |result_hash, (name, value)|
name = name._name if name.is_a? Squeel::Nodes::Join
if value.kind_of? Hash
value = unsqueeled_conditions(value)
end
result_hash[name] = value
result_hash
end
end

Expand Down Expand Up @@ -164,6 +182,18 @@ def merge_joins(base, add)
end
end

# override to fix overwrites
# do not write existing hashes using empty hashes
def merge_joins(base, add)
add.each do |name, nested|
if base[name].is_a?(Hash) && nested.present?
merge_joins(base[name], nested)
elsif !base[name].is_a?(Hash) || nested.present?
base[name] = nested
end
end
end

# Removes empty hashes and moves everything into arrays.
def clean_joins(joins_hash)
joins = []
Expand Down
8 changes: 7 additions & 1 deletion lib/cancan/rule.rb
Expand Up @@ -109,7 +109,13 @@ def matches_conditions_hash?(subject, conditions = @conditions)
if model_adapter(subject).override_condition_matching? subject, name, value
model_adapter(subject).matches_condition? subject, name, value
else
attribute = subject.send(name)
method_name = case name
when Symbol then name
when Squeel::Nodes::Join then name._name
when Squeel::Nodes::Predicate then name.expr
else raise name
end
attribute = subject.send(method_name)
if value.kind_of?(Hash)
if attribute.kind_of?(Array) || attribute.kind_of?(ActiveRecord::Relation)
attribute.any? { |element| matches_conditions_hash? element, value }
Expand Down

0 comments on commit 6708da9

Please sign in to comment.