Skip to content

Commit

Permalink
Merge pull request #6058 from RStankov/relation-from-to-accept-other-…
Browse files Browse the repository at this point in the history
…relation-objects

Relation#from to accept other Relation objects
  • Loading branch information
jonleighton committed May 17, 2012
2 parents 03f8a57 + 64872d1 commit 77a4072
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 7 deletions.
7 changes: 7 additions & 0 deletions activerecord/CHANGELOG.md
@@ -1,5 +1,12 @@
## Rails 4.0.0 (unreleased) ##

* Added ability to ActiveRecord::Relation#from to accept other ActiveRecord::Relation objects

Record.from(subquery)
Record.from(subquery, :a)

*Radoslav Stankov*

* Added custom coders support for ActiveRecord::Store. Now you can set
your custom coder like this:

Expand Down
3 changes: 2 additions & 1 deletion activerecord/lib/active_record/relation/merger.rb
Expand Up @@ -43,7 +43,7 @@ def initialize(relation, other)
def normal_values
Relation::SINGLE_VALUE_METHODS +
Relation::MULTI_VALUE_METHODS -
[:where, :order, :bind, :reverse_order, :lock, :create_with, :reordering]
[:where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from]
end

def merge
Expand Down Expand Up @@ -76,6 +76,7 @@ def merge_multi_values
end

def merge_single_values
relation.from_value = values[:from] unless relation.from_value
relation.lock_value = values[:lock] unless relation.lock_value
relation.reverse_order_value = values[:reverse_order]

Expand Down
34 changes: 29 additions & 5 deletions activerecord/lib/active_record/relation/query_methods.rb
Expand Up @@ -300,12 +300,25 @@ def create_with!(value)
self
end

def from(value)
spawn.from!(value)
# Specifies table from which the records will be fetched. For example:
#
# Topic.select('title').from('posts')
# #=> SELECT title FROM posts
#
# Can accept other relation objects. For example:
#
# Topic.select('title').from(Topics.approved)
# # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
#
# Topics.select('a.title').from(Topics.approved, :a)
# # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
#
def from(value, subquery_name = nil)
spawn.from!(value, subquery_name)
end

def from!(value)
self.from_value = value
def from!(value, subquery_name = nil)
self.from_value = [value, subquery_name]
self
end

Expand Down Expand Up @@ -415,7 +428,7 @@ def build_arel
build_select(arel, select_values.uniq)

arel.distinct(uniq_value)
arel.from(from_value) if from_value
arel.from(build_from) if from_value
arel.lock(lock_value) if lock_value

arel
Expand Down Expand Up @@ -464,6 +477,17 @@ def build_where(opts, other = [])
end
end

def build_from
opts, name = from_value
case opts
when Relation
name ||= 'subquery'
opts.arel.as(name.to_s)
else
opts
end
end

def build_joins(manager, joins)
buckets = joins.group_by do |join|
case join
Expand Down
7 changes: 6 additions & 1 deletion activerecord/test/cases/relation_test.rb
Expand Up @@ -203,13 +203,18 @@ def relation
assert_equal [], relation.extending_values
end

(Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with]).each do |method|
(Relation::SINGLE_VALUE_METHODS - [:from, :lock, :reordering, :reverse_order, :create_with]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value")
end
end

test '#from!' do
assert relation.from!('foo').equal?(relation)
assert_equal ['foo', nil], relation.from_value
end

test '#lock!' do
assert relation.lock!('foo').equal?(relation)
assert_equal 'foo', relation.lock_value
Expand Down
7 changes: 7 additions & 0 deletions activerecord/test/cases/relations_test.rb
Expand Up @@ -133,6 +133,13 @@ def test_reload
assert topics.loaded?
end

def test_finiding_with_subquery
relation = Topic.where(:approved => true)
assert_equal relation.to_a, Topic.select('*').from(relation).to_a
assert_equal relation.to_a, Topic.select('subquery.*').from(relation).to_a
assert_equal relation.to_a, Topic.select('a.*').from(relation, :a).to_a
end

def test_finding_with_conditions
assert_equal ["David"], Author.where(:name => 'David').map(&:name)
assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name)
Expand Down

0 comments on commit 77a4072

Please sign in to comment.