aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/associations.rb')
-rwxr-xr-xactiverecord/lib/active_record/associations.rb97
1 files changed, 24 insertions, 73 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index fcc041beca..1013c75643 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -486,63 +486,7 @@ module ActiveRecord
#
# When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
# before the actual model exists.
- #
- # == Adding Joins For Associations to Queries Using the :joins option
- #
- # ActiveRecord::Base#find provides a :joins option, which takes either a string or values accepted by the :include option.
- # if the value is a string, the it should contain a SQL fragment containing a join clause.
- #
- # Non-string values of :joins will add an automatic join clause to the query in the same way that the :include option does but with two critical
- # differences:
- #
- # 1. A normal (inner) join will be performed instead of the outer join generated by :include.
- # this means that only objects which have objects attached to the association will be included in the result.
- # For example, suppose we have the following tables (in yaml format):
- #
- # Authors
- # fred:
- # id: 1
- # name: Fred
- # steve:
- # id: 2
- # name: Steve
- #
- # Contributions
- # only:
- # id: 1
- # author_id: 1
- # description: Atta Boy Letter for Steve
- # date: 2007-10-27 14:09:54
- #
- # and corresponding AR Classes
- #
- # class Author: < ActiveRecord::Base
- # has_many :contributions
- # end
- #
- # class Contribution < ActiveRecord::Base
- # belongs_to :author
- # end
- #
- # The query Author.find(:all) will return both authors, but Author.find(:all, :joins => :contributions) will
- # only return authors who have at least one contribution, in this case only the first.
- # This is only a degenerate case of the more typical use of :joins with a non-string value.
- # For example to find authors who have at least one contribution before a certain date we can use:
- #
- # Author.find(:all, :joins => :contributions, :conditions => ["contributions.date <= ?", 1.week.ago.to_s(:db)])
- #
- # 2. Only instances of the class to which the find is sent will be instantiated. ActiveRecord objects will not
- # be instantiated for rows reached through the associations.
- #
- # The difference between using :joins vs :include to name associated records is that :joins allows associated tables to
- # participate in selection criteria in the query without incurring the overhead of instantiating associated objects.
- # This can be important when the number of associated objects in the database is large, and they will not be used, or
- # only those associated with a paricular object or objects will be used after the query, making two queries more
- # efficient than one.
- #
- # Note that while using a string value for :joins marks the result objects as read-only, the objects resulting
- # from a call to find with a non-string :joins option value will be writable.
- #
+ #
# == Table Aliasing
#
# ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
@@ -1177,13 +1121,7 @@ module ActiveRecord
def find_with_associations(options = {})
catch :invalid_query do
- if ar_joins = scope(:find, :ar_joins)
- options = options.dup
- options[:ar_joins] = ar_joins
- end
- includes = merge_includes(scope(:find, :include), options[:include])
- includes = merge_includes(includes, options[:ar_joins])
- join_dependency = JoinDependency.new(self, includes, options[:joins], options[:ar_joins])
+ join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
rows = select_all_rows(options, join_dependency)
return join_dependency.instantiate(rows)
end
@@ -1437,9 +1375,8 @@ module ActiveRecord
class JoinDependency # :nodoc:
attr_reader :joins, :reflections, :table_aliases
- def initialize(base, associations, joins, ar_joins = nil)
+ def initialize(base, associations, joins)
@joins = [JoinBase.new(base, joins)]
- @ar_joins = ar_joins
@associations = associations
@reflections = []
@base_records_hash = {}
@@ -1463,9 +1400,9 @@ module ActiveRecord
unless @base_records_hash[primary_id]
@base_records_in_order << (@base_records_hash[primary_id] = join_base.instantiate(row))
end
- construct(@base_records_hash[primary_id], @associations, join_associations.dup, row) unless @ar_joins
+ construct(@base_records_hash[primary_id], @associations, join_associations.dup, row)
end
- remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations) unless @ar_joins
+ remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations)
return @base_records_in_order
end
@@ -1507,7 +1444,7 @@ module ActiveRecord
reflection = parent.reflections[associations.to_s.intern] or
raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
@reflections << reflection
- @joins << (@ar_joins ? ARJoinAssociation : JoinAssociation).new(reflection, self, parent)
+ @joins << build_join_association(reflection, parent)
when Array
associations.each do |association|
build(association, parent)
@@ -1522,6 +1459,11 @@ module ActiveRecord
end
end
+ # overridden in InnerJoinDependency subclass
+ def build_join_association(reflection, parent)
+ JoinAssociation.new(reflection, self, parent)
+ end
+
def construct(parent, associations, joins, row)
case associations
when Symbol, String
@@ -1786,21 +1728,30 @@ module ActiveRecord
def interpolate_sql(sql)
instance_eval("%@#{sql.gsub('@', '\@')}@")
- end
+ end
+
+ private
- private
def join_type
"LEFT OUTER JOIN"
end
-
end
- class ARJoinAssociation < JoinAssociation
+ end
+
+ class InnerJoinDependency < JoinDependency # :nodoc:
+ protected
+ def build_join_association(reflection, parent)
+ InnerJoinAssociation.new(reflection, self, parent)
+ end
+
+ class InnerJoinAssociation < JoinAssociation
private
def join_type
"INNER JOIN"
end
end
end
+
end
end
end