diff options
Diffstat (limited to 'activerecord/lib/active_record/associations.rb')
-rwxr-xr-x | activerecord/lib/active_record/associations.rb | 97 |
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 |