From 6e638bba594b6164190d2a6fb96ffa07a20b11f3 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Sat, 18 Aug 2012 22:33:25 -0400 Subject: Add equality to ALL THE THINGS (that matter) People are often trying to use ARel nodes inside ActiveRecord, and when they do so, lots of things can break, because ActiveRecord relies on Array#uniq and sometimes hash key equality to handle values that end up in wheres, havings, etc. By implementing equality for all the nodes, we should hopefully be able to prevent any nodes (even nodes containing other nodes) from failing an equality check they should otherwise pass, and alleviate many of these errors. Fixes #130 --- lib/arel/nodes/and.rb | 10 ++++++++++ lib/arel/nodes/binary.rb | 11 +++++++++++ lib/arel/nodes/extract.rb | 11 +++++++++++ lib/arel/nodes/false.rb | 7 +++++++ lib/arel/nodes/function.rb | 11 +++++++++++ lib/arel/nodes/insert_statement.rb | 12 ++++++++++++ lib/arel/nodes/named_function.rb | 9 +++++++++ lib/arel/nodes/select_core.rb | 20 ++++++++++++++++++++ lib/arel/nodes/select_statement.rb | 16 +++++++++++++++- lib/arel/nodes/terminal.rb | 7 +++++++ lib/arel/nodes/true.rb | 7 +++++++ lib/arel/nodes/unary.rb | 10 ++++++++++ lib/arel/nodes/update_statement.rb | 15 +++++++++++++++ lib/arel/nodes/window.rb | 30 +++++++++++++++++++++++++++++- lib/arel/table.rb | 13 +++++++++++++ 15 files changed, 187 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/arel/nodes/and.rb b/lib/arel/nodes/and.rb index b4443c3d27..0d0fb3ee82 100644 --- a/lib/arel/nodes/and.rb +++ b/lib/arel/nodes/and.rb @@ -18,6 +18,16 @@ module Arel def right children[1] end + + def hash + children.hash + end + + def eql? other + self.class == other.class && + self.children == other.children + end + alias :== :eql? end end end diff --git a/lib/arel/nodes/binary.rb b/lib/arel/nodes/binary.rb index bcd46db398..d55c7a5478 100644 --- a/lib/arel/nodes/binary.rb +++ b/lib/arel/nodes/binary.rb @@ -13,6 +13,17 @@ module Arel @left = @left.clone if @left @right = @right.clone if @right end + + def hash + [@left, @right].hash + end + + def eql? other + self.class == other.class && + self.left == other.left && + self.right == other.right + end + alias :== :eql? end %w{ diff --git a/lib/arel/nodes/extract.rb b/lib/arel/nodes/extract.rb index 1c9ee78816..92fbde62e1 100644 --- a/lib/arel/nodes/extract.rb +++ b/lib/arel/nodes/extract.rb @@ -18,6 +18,17 @@ module Arel self.alias = SqlLiteral.new(aliaz) self end + + def hash + super ^ [@field, @alias].hash + end + + def eql? other + super && + self.field == other.field && + self.alias == other.alias + end + alias :== :eql? end end end diff --git a/lib/arel/nodes/false.rb b/lib/arel/nodes/false.rb index 611e19633b..6df70e43ce 100644 --- a/lib/arel/nodes/false.rb +++ b/lib/arel/nodes/false.rb @@ -1,6 +1,13 @@ module Arel module Nodes class False < Arel::Nodes::Node + def hash + self.class.hash + end + + def eql? other + self.class == other.class + end end end end diff --git a/lib/arel/nodes/function.rb b/lib/arel/nodes/function.rb index 5f6056a6b6..90bbf4a77b 100644 --- a/lib/arel/nodes/function.rb +++ b/lib/arel/nodes/function.rb @@ -16,6 +16,17 @@ module Arel self.alias = SqlLiteral.new(aliaz) self end + + def hash + [@expressions, @alias, @distinct].hash + end + + def eql? other + self.class == other.class && + self.expressions == other.expressions && + self.alias == other.alias && + self.distinct == other.distinct + end end %w{ diff --git a/lib/arel/nodes/insert_statement.rb b/lib/arel/nodes/insert_statement.rb index 37c12f011a..518160cce4 100644 --- a/lib/arel/nodes/insert_statement.rb +++ b/lib/arel/nodes/insert_statement.rb @@ -14,6 +14,18 @@ module Arel @columns = @columns.clone @values = @values.clone if @values end + + def hash + [@relation, @columns, @values].hash + end + + def eql? other + self.class == other.class && + self.relation == other.relation && + self.columns == other.columns && + self.values == other.values + end + alias :== :eql? end end end diff --git a/lib/arel/nodes/named_function.rb b/lib/arel/nodes/named_function.rb index 56669bf858..c792f0af98 100644 --- a/lib/arel/nodes/named_function.rb +++ b/lib/arel/nodes/named_function.rb @@ -7,6 +7,15 @@ module Arel super(expr, aliaz) @name = name end + + def hash + super ^ @name.hash + end + + def eql? other + super && self.name == other.name + end + alias :== :eql? end end end diff --git a/lib/arel/nodes/select_core.rb b/lib/arel/nodes/select_core.rb index 9b8c4a2a1f..3b400c768d 100644 --- a/lib/arel/nodes/select_core.rb +++ b/lib/arel/nodes/select_core.rb @@ -37,6 +37,26 @@ module Arel @having = @having.clone if @having @windows = @windows.clone end + + def hash + [ + @source, @top, @set_quantifier, @projections, + @wheres, @groups, @having, @windows + ].hash + end + + def eql? other + self.class == other.class && + self.source == other.source && + self.top == other.top && + self.set_quantifier == other.set_quantifier && + self.projections == other.projections && + self.wheres == other.wheres && + self.groups == other.groups && + self.having == other.having && + self.windows == other.windows + end + alias :== :eql? end end end diff --git a/lib/arel/nodes/select_statement.rb b/lib/arel/nodes/select_statement.rb index c99842f22f..32bdd7080c 100644 --- a/lib/arel/nodes/select_statement.rb +++ b/lib/arel/nodes/select_statement.rb @@ -5,7 +5,6 @@ module Arel attr_accessor :limit, :orders, :lock, :offset, :with def initialize cores = [SelectCore.new] - #puts caller @cores = cores @orders = [] @limit = nil @@ -19,6 +18,21 @@ module Arel @cores = @cores.map { |x| x.clone } @orders = @orders.map { |x| x.clone } end + + def hash + [@cores, @orders, @limit, @lock, @offset, @with].hash + end + + def eql? other + self.class == other.class && + self.cores == other.cores && + self.orders == other.orders && + self.limit == other.limit && + self.lock == other.lock && + self.offset == other.offset && + self.with == other.with + end + alias :== :eql? end end end diff --git a/lib/arel/nodes/terminal.rb b/lib/arel/nodes/terminal.rb index c6b4f4e1e2..f4cdfdfe17 100644 --- a/lib/arel/nodes/terminal.rb +++ b/lib/arel/nodes/terminal.rb @@ -1,6 +1,13 @@ module Arel module Nodes class Distinct < Arel::Nodes::Node + def hash + self.class.hash + end + + def eql? other + self.class == other.class + end end end end diff --git a/lib/arel/nodes/true.rb b/lib/arel/nodes/true.rb index 63dd5562e1..082963e5e6 100644 --- a/lib/arel/nodes/true.rb +++ b/lib/arel/nodes/true.rb @@ -1,6 +1,13 @@ module Arel module Nodes class True < Arel::Nodes::Node + def hash + self.class.hash + end + + def eql? other + self.class == other.class + end end end end diff --git a/lib/arel/nodes/unary.rb b/lib/arel/nodes/unary.rb index 7828cceae5..42c31267dd 100644 --- a/lib/arel/nodes/unary.rb +++ b/lib/arel/nodes/unary.rb @@ -7,6 +7,16 @@ module Arel def initialize expr @expr = expr end + + def hash + @expr.hash + end + + def eql? other + self.class == other.class && + self.expr == other.expr + end + alias :== :eql? end %w{ diff --git a/lib/arel/nodes/update_statement.rb b/lib/arel/nodes/update_statement.rb index c08f1b2c5e..d6831dc242 100644 --- a/lib/arel/nodes/update_statement.rb +++ b/lib/arel/nodes/update_statement.rb @@ -18,6 +18,21 @@ module Arel @wheres = @wheres.clone @values = @values.clone end + + def hash + [@relation, @wheres, @values, @orders, @limit, @key].hash + end + + def eql? other + self.class == other.class && + self.relation == other.relation && + self.wheres == other.wheres && + self.values == other.values && + self.orders == other.orders && + self.limit == other.limit && + self.key == other.key + end + alias :== :eql? end end end diff --git a/lib/arel/nodes/window.rb b/lib/arel/nodes/window.rb index 383d6b8778..3c05f47f14 100644 --- a/lib/arel/nodes/window.rb +++ b/lib/arel/nodes/window.rb @@ -32,6 +32,17 @@ module Arel super @orders = @orders.map { |x| x.clone } end + + def hash + [@orders, @framing].hash + end + + def eql? other + self.class == other.class && + self.orders == other.orders && + self.framing == other.framing + end + alias :== :eql? end class NamedWindow < Window @@ -46,6 +57,15 @@ module Arel super @name = other.name.clone end + + def hash + super ^ @name.hash + end + + def eql? other + super && self.name == other.name + end + alias :== :eql? end class Rows < Unary @@ -60,7 +80,15 @@ module Arel end end - class CurrentRow < Arel::Nodes::Node; end + class CurrentRow < Node + def hash + self.class.hash + end + + def eql? other + self.class == other.class + end + end class Preceding < Unary def initialize(expr = nil) diff --git a/lib/arel/table.rb b/lib/arel/table.rb index 7a1983b3d2..6f1ab7e90f 100644 --- a/lib/arel/table.rb +++ b/lib/arel/table.rb @@ -123,6 +123,19 @@ Arel 4.0.0 with no replacement. PEW PEW PEW!!! InsertManager.new(@engine) end + def hash + [@name, @engine, @aliases, @table_alias].hash + end + + def eql? other + self.class == other.class && + self.name == other.name && + self.engine == other.engine && + self.aliases == other.aliases && + self.table_alias == other.table_alias + end + alias :== :eql? + private def attributes_for columns -- cgit v1.2.3