aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorMatthew Draper <matthew@trebex.net>2014-06-23 11:42:46 +0800
committerSean Griffin <sean@thoughtbot.com>2015-01-28 13:35:55 -0700
commitb0b37942d729b6bdcd2e3178eda7fa1de203b3d0 (patch)
treecbf5e9fe861b6093166213b322e3d5f6bf0c8156 /activerecord/lib
parent56a3d5ec9183a9bcbf140d4102d45e3928f2617a (diff)
downloadrails-b0b37942d729b6bdcd2e3178eda7fa1de203b3d0.tar.gz
rails-b0b37942d729b6bdcd2e3178eda7fa1de203b3d0.tar.bz2
rails-b0b37942d729b6bdcd2e3178eda7fa1de203b3d0.zip
Added #or to ActiveRecord::Relation
Post.where('id = 1').or(Post.where('id = 2')) # => SELECT * FROM posts WHERE (id = 1) OR (id = 2) [Matthew Draper & Gael Muller]
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/null_relation.rb8
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb59
3 files changed, 68 insertions, 1 deletions
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index b406da14dc..802adca908 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -75,5 +75,13 @@ module ActiveRecord
def exists?(_id = false)
false
end
+
+ def or(other)
+ if other.is_a?(NullRelation)
+ super
+ else
+ other.or(self)
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 91c9a0db99..4e597590e9 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -7,7 +7,7 @@ module ActiveRecord
delegate :find_by, :find_by!, to: :all
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, to: :all
delegate :find_each, :find_in_batches, to: :all
- delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
+ delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :or,
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly,
:having, :create_with, :uniq, :distinct, :references, :none, :unscope, to: :all
delegate :count, :average, :minimum, :maximum, :sum, :calculate, to: :all
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 0078b0f32e..78ee8b4580 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -582,6 +582,65 @@ module ActiveRecord
unscope(where: conditions.keys).where(conditions)
end
+ # Returns a new relation, which is the logical union of this relation and the one passed as an
+ # argument.
+ #
+ # The two relations must be structurally compatible: they must be scoping the same model, and
+ # they must differ only by +where+ (if no +group+ has been defined) or +having+ (if a +group+ is
+ # present). Neither relation may have a +limit+, +offset+, or +uniq+ set.
+ #
+ # Post.where("id = 1").or(Post.where("id = 2"))
+ # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
+ #
+ def or(other)
+ spawn.or!(other)
+ end
+
+ def or!(other)
+ combining = group_values.any? ? :having : :where
+
+ unless structurally_compatible?(other, combining)
+ raise ArgumentError, 'Relation passed to #or must be structurally compatible'
+ end
+
+ unless other.is_a?(NullRelation)
+ left_values = send("#{combining}_values")
+ right_values = other.send("#{combining}_values")
+
+ common = left_values & right_values
+ mine = left_values - common
+ theirs = right_values - common
+
+ if mine.any? && theirs.any?
+ mine = mine.map { |x| String === x ? Arel.sql(x) : x }
+ theirs = theirs.map { |x| String === x ? Arel.sql(x) : x }
+
+ mine = [Arel::Nodes::And.new(mine)] if mine.size > 1
+ theirs = [Arel::Nodes::And.new(theirs)] if theirs.size > 1
+
+ common << Arel::Nodes::Or.new(mine.first, theirs.first)
+ end
+
+ send("#{combining}_values=", common)
+ end
+
+ self
+ end
+
+ def structurally_compatible?(other, allowed_to_vary)
+ Relation::SINGLE_VALUE_METHODS.all? do |name|
+ send("#{name}_value") == other.send("#{name}_value")
+ end &&
+ (Relation::MULTI_VALUE_METHODS - [allowed_to_vary, :extending]).all? do |name|
+ send("#{name}_values") == other.send("#{name}_values")
+ end &&
+ (extending_values - [NullRelation]) == (other.extending_values - [NullRelation]) &&
+ !limit_value &&
+ !offset_value &&
+ !uniq_value
+ end
+ private :structurally_compatible?
+
# Allows to specify a HAVING clause. Note that you can't use HAVING
# without also specifying a GROUP clause.
#