aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorErnie Miller <ernie@metautonomo.us>2010-04-09 19:52:43 -0400
committerErnie Miller <ernie@metautonomo.us>2010-05-07 13:07:43 -0400
commitbd473344000bd538d353e4bc6d20ca8fff2e4704 (patch)
treec2586b9dc543aa828dcbefd2769e7a1d7485280a /lib
parent0433b054eebd5a53ff6c5f35383a6c0aed0015b2 (diff)
downloadrails-bd473344000bd538d353e4bc6d20ca8fff2e4704.tar.gz
rails-bd473344000bd538d353e4bc6d20ca8fff2e4704.tar.bz2
rails-bd473344000bd538d353e4bc6d20ca8fff2e4704.zip
Support predicate complements and alternate not syntax (overload BasicObject#!)
Diffstat (limited to 'lib')
-rw-r--r--lib/arel/algebra/attributes/attribute.rb4
-rw-r--r--lib/arel/algebra/predicates.rb144
-rw-r--r--lib/arel/engines/memory/predicates.rb39
-rw-r--r--lib/arel/engines/sql/core_extensions/nil_class.rb4
-rw-r--r--lib/arel/engines/sql/core_extensions/object.rb4
-rw-r--r--lib/arel/engines/sql/predicates.rb14
-rw-r--r--lib/arel/engines/sql/primitives.rb8
7 files changed, 166 insertions, 51 deletions
diff --git a/lib/arel/algebra/attributes/attribute.rb b/lib/arel/algebra/attributes/attribute.rb
index 0266b38db3..640d6facde 100644
--- a/lib/arel/algebra/attributes/attribute.rb
+++ b/lib/arel/algebra/attributes/attribute.rb
@@ -104,12 +104,12 @@ module Arel
",
:any => "
def #{name}_any(*others)
- Predicates::Any.new(Predicates::#{klass}, self, *others)
+ Predicates::Any.build(Predicates::#{klass}, self, *others)
end
",
:all => "
def #{name}_all(*others)
- Predicates::All.new(Predicates::#{klass}, self, *others)
+ Predicates::All.build(Predicates::#{klass}, self, *others)
end
"
}
diff --git a/lib/arel/algebra/predicates.rb b/lib/arel/algebra/predicates.rb
index be80f2a987..2da37af2e7 100644
--- a/lib/arel/algebra/predicates.rb
+++ b/lib/arel/algebra/predicates.rb
@@ -9,32 +9,52 @@ module Arel
And.new(self, other_predicate)
end
- def not
+ def complement
Not.new(self)
end
+
+ def not
+ complement
+ end
+
+ if respond_to?('!') # Nice! We're running Ruby 1.9 and can override the inherited BasicObject#!
+ def empty? # Need to define empty? to keep Object#blank? from going haywire
+ false
+ end
+
+ define_method('!') do
+ self.complement
+ end
+ end
end
class Polyadic < Predicate
- attributes :operator, :operand1, :additional_operands
+ attributes :predicates
- def initialize(operator, operand1, *additional_operands)
- @operator = operator
- @operand1 = operand1
- @additional_operands = additional_operands.uniq
+ def initialize(*predicates)
+ @predicates = predicates
+ end
+
+ # Build a Polyadic predicate based on:
+ # * <tt>operator</tt> - The Predicate subclass that defines the type of operation
+ # (LessThan, Equality, etc)
+ # * <tt>operand1</tt> - The left-hand operand (normally an Arel::Attribute)
+ # * <tt>additional_operands</tt> - All possible right-hand operands
+ def self.build(operator, operand1, *additional_operands)
+ new(
+ *additional_operands.uniq.inject([]) do |predicates, operand|
+ predicates << operator.new(operand1, operand)
+ end
+ )
end
def ==(other)
- self.class === other and
- @operator == operator and
- @operand1 == other.operand1 and
- same_elements?(@additional_operands, other.additional_operands)
+ same_elements?(@predicates, other.predicates)
end
def bind(relation)
self.class.new(
- operator,
- operand1.find_correlate_in(relation),
- *additional_operands.map {|o| o.find_correlate_in(relation)}
+ *predicates.map {|p| p.find_correlate_in(relation)}
)
end
@@ -49,6 +69,18 @@ module Arel
end
end
+ class Any < Polyadic
+ def complement
+ All.new(*predicates.map {|p| p.complement})
+ end
+ end
+
+ class All < Polyadic
+ def complement
+ Any.new(*predicates.map {|p| p.complement})
+ end
+ end
+
class Unary < Predicate
attributes :operand
deriving :initialize, :==
@@ -57,6 +89,12 @@ module Arel
self.class.new(operand.find_correlate_in(relation))
end
end
+
+ class Not < Unary
+ def complement
+ operand
+ end
+ end
class Binary < Predicate
attributes :operand1, :operand2
@@ -72,6 +110,20 @@ module Arel
self.class.new(operand1.find_correlate_in(relation), operand2.find_correlate_in(relation))
end
end
+
+ class CompoundPredicate < Binary; end
+
+ class And < CompoundPredicate
+ def complement
+ Or.new(operand1.complement, operand2.complement)
+ end
+ end
+
+ class Or < CompoundPredicate
+ def complement
+ And.new(operand1.complement, operand2.complement)
+ end
+ end
class Equality < Binary
def ==(other)
@@ -79,16 +131,64 @@ module Arel
((operand1 == other.operand1 and operand2 == other.operand2) or
(operand1 == other.operand2 and operand2 == other.operand1))
end
+
+ def complement
+ Inequality.new(operand1, operand2)
+ end
end
- class Inequality < Equality; end
- class GreaterThanOrEqualTo < Binary; end
- class GreaterThan < Binary; end
- class LessThanOrEqualTo < Binary; end
- class LessThan < Binary; end
- class Match < Binary; end
- class NotMatch < Binary; end
- class In < Binary; end
- class NotIn < Binary; end
+ class Inequality < Equality
+ def complement
+ Equality.new(operand1, operand2)
+ end
+ end
+
+ class GreaterThanOrEqualTo < Binary
+ def complement
+ LessThan.new(operand1, operand2)
+ end
+ end
+
+ class GreaterThan < Binary
+ def complement
+ LessThanOrEqualTo.new(operand1, operand2)
+ end
+ end
+
+ class LessThanOrEqualTo < Binary
+ def complement
+ GreaterThan.new(operand1, operand2)
+ end
+ end
+
+ class LessThan < Binary
+ def complement
+ GreaterThanOrEqualTo.new(operand1, operand2)
+ end
+ end
+
+ class Match < Binary
+ def complement
+ NotMatch.new(operand1, operand2)
+ end
+ end
+
+ class NotMatch < Binary
+ def complement
+ Match.new(operand1, operand2)
+ end
+ end
+
+ class In < Binary
+ def complement
+ NotIn.new(operand1, operand2)
+ end
+ end
+
+ class NotIn < Binary
+ def complement
+ In.new(operand1, operand2)
+ end
+ end
end
end
diff --git a/lib/arel/engines/memory/predicates.rb b/lib/arel/engines/memory/predicates.rb
index c0ee862626..0e88810e7d 100644
--- a/lib/arel/engines/memory/predicates.rb
+++ b/lib/arel/engines/memory/predicates.rb
@@ -13,41 +13,40 @@ module Arel
end
class Not < Unary
- def operator; '!' end
- end
-
- class CompoundPredicate < Binary
def eval(row)
- eval "operand1.eval(row) #{operator} operand2.eval(row)"
+ !operand.eval(row)
end
end
-
- class Or < CompoundPredicate
- def operator; :or end
- end
-
- class And < CompoundPredicate
- def operator; :and end
- end
- class GroupedPredicate < Polyadic
+ class Polyadic < Predicate
def eval(row)
- group = additional_operands.inject([]) do |results, operand|
- results << operator.new(operand1, operand)
- end
- group.send(compounder) do |operation|
+ predicates.send(compounder) do |operation|
operation.eval(row)
end
end
end
- class Any < GroupedPredicate
+ class Any < Polyadic
def compounder; :any? end
end
- class All < GroupedPredicate
+ class All < Polyadic
def compounder; :all? end
end
+
+ class CompoundPredicate < Binary
+ def eval(row)
+ eval "operand1.eval(row) #{operator} operand2.eval(row)"
+ end
+ end
+
+ class Or < CompoundPredicate
+ def operator; :or end
+ end
+
+ class And < CompoundPredicate
+ def operator; :and end
+ end
class Equality < Binary
def operator; :== end
diff --git a/lib/arel/engines/sql/core_extensions/nil_class.rb b/lib/arel/engines/sql/core_extensions/nil_class.rb
index 3f70677ba6..d4bb0e4c33 100644
--- a/lib/arel/engines/sql/core_extensions/nil_class.rb
+++ b/lib/arel/engines/sql/core_extensions/nil_class.rb
@@ -4,6 +4,10 @@ module Arel
def equality_predicate_sql
'IS'
end
+
+ def inequality_predicate_sql
+ 'IS NOT'
+ end
def not_predicate_sql
'IS NOT'
diff --git a/lib/arel/engines/sql/core_extensions/object.rb b/lib/arel/engines/sql/core_extensions/object.rb
index b71ef29fd5..5415c84706 100644
--- a/lib/arel/engines/sql/core_extensions/object.rb
+++ b/lib/arel/engines/sql/core_extensions/object.rb
@@ -8,6 +8,10 @@ module Arel
def equality_predicate_sql
'='
end
+
+ def inequality_predicate_sql
+ '!='
+ end
def not_predicate_sql
'!='
diff --git a/lib/arel/engines/sql/predicates.rb b/lib/arel/engines/sql/predicates.rb
index b459168620..df8700a500 100644
--- a/lib/arel/engines/sql/predicates.rb
+++ b/lib/arel/engines/sql/predicates.rb
@@ -30,21 +30,19 @@ module Arel
def predicate_sql; "AND" end
end
- class GroupedPredicate < Polyadic
+ class Polyadic < Predicate
def to_sql(formatter = nil)
"(" +
- additional_operands.inject([]) { |predicates, operand|
- predicates << operator.new(operand1, operand).to_sql(formatter)
- }.join(" #{predicate_sql} ") +
+ predicates.map {|p| p.to_sql(formatter)}.join(" #{predicate_sql} ") +
")"
end
end
- class Any < GroupedPredicate
+ class Any < Polyadic
def predicate_sql; "OR" end
end
- class All < GroupedPredicate
+ class All < Polyadic
def predicate_sql; "AND" end
end
@@ -55,7 +53,9 @@ module Arel
end
class Inequality < Equality
- def predicate_sql; '!=' end
+ def predicate_sql
+ operand2.inequality_predicate_sql
+ end
end
class GreaterThanOrEqualTo < Binary
diff --git a/lib/arel/engines/sql/primitives.rb b/lib/arel/engines/sql/primitives.rb
index 78e1ed7f0b..41769fa510 100644
--- a/lib/arel/engines/sql/primitives.rb
+++ b/lib/arel/engines/sql/primitives.rb
@@ -29,10 +29,18 @@ module Arel
def inclusion_predicate_sql
value.inclusion_predicate_sql
end
+
+ def exclusion_predicate_sql
+ value.exclusion_predicate_sql
+ end
def equality_predicate_sql
value.equality_predicate_sql
end
+
+ def inequality_predicate_sql
+ value.inequality_predicate_sql
+ end
def not_predicate_sql
value.not_predicate_sql