Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #9640: preload_ar.2.diff

File preload_ar.2.diff, 7.8 kB (added by fcheung, 8 months ago)

extra test, stylistic changes

  • activerecord/lib/active_record/base.rb

    old new  
    419419      #   end 
    420420      def find(*args) 
    421421        options = args.extract_options! 
     422        associations_to_preload = options.delete(:preload) 
    422423        validate_find_options(options) 
    423424        set_readonly_option!(options) 
    424425 
    425         case args.first 
     426        results = case args.first 
    426427          when :first then find_initial(options) 
    427428          when :all   then find_every(options) 
    428429          else             find_from_ids(args, options) 
    429430        end 
     431        if associations_to_preload 
     432          preload_associations(results, associations_to_preload) 
     433        end 
     434        results 
    430435      end 
    431436       
    432437      # Works like find(:all), but requires a complete SQL string. Examples: 
     
    16401645          quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "  
    16411646          quoted_value  
    16421647        end 
     1648         
     1649        #loads the named associations for the activerecord object (or objects) given 
     1650        # 
     1651        # 
     1652        def preload_associations(top_objects, associations) 
     1653          top_objects = [top_objects].flatten.compact 
     1654          case associations 
     1655          when Array then associations.each {|association| preload_associations(top_objects, association)} 
     1656          when Symbol, String then preload_one_association(top_objects, associations.to_sym) 
     1657          when Hash then 
     1658            associations.each do |parent, child| 
     1659              raise "parent must be an association name" unless parent.is_a?( String) || parent.is_a?( Symbol) 
     1660              preload_associations(top_objects, parent) 
     1661              reflection = reflections[parent] 
     1662              parents = top_objects.map {|top_object| top_object.send(reflection.name)}.flatten 
     1663              unless parents.empty? 
     1664                parents.first.class.preload_associations(parents, child) 
     1665              end 
     1666            end 
     1667          end 
     1668           
     1669        end 
     1670         
     1671        def preload_collection_object(id_to_object_map, reflection_name, associated_object, key) 
     1672          association_proxy = id_to_object_map[associated_object[key].to_i].send(reflection_name) 
     1673          association_proxy.loaded 
     1674          if associated_object.is_a? Array 
     1675            association_proxy.target.push(*associated_object) 
     1676          else 
     1677            association_proxy.target << associated_object 
     1678          end 
     1679        end 
     1680 
     1681        def preload_single_object(id_to_object_map, reflection_name, associated_object, key) 
     1682          id_to_object_map[associated_object[key].to_i].send("set_#{reflection_name}_target",associated_object) 
     1683        end 
     1684         
     1685        def preload_one_association(top_objects, association)  
     1686          reflection = reflections[association] 
     1687          raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection 
     1688                     
     1689          reflection_name = reflection.name 
     1690          options = reflection.options 
     1691          table_name = reflection.klass.table_name 
     1692          primary_key_name = reflection.primary_key_name 
     1693          foreign_key = options[:foreign_key] || reflection.active_record.to_s.classify.foreign_key 
     1694                     
     1695          id_to_object_map = top_objects.index_by &:id 
     1696          ids = top_objects.collect &:id 
     1697           
     1698          case reflection.macro 
     1699          when :belongs_to 
     1700            conditions = "t0.#{self.primary_key} IN (?)" 
     1701            conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 
     1702            associated_objects = reflection.klass.find :all, :conditions => [conditions, ids], 
     1703            :joins => "INNER JOIN #{self.table_name} AS t0 ON #{table_name}.#{primary_key} = t0.#{primary_key_name}", 
     1704            :select => "#{options[:select] || table_name+'.*'}, t0.#{self.primary_key} as _parent_object_id", 
     1705            :order => options[:order] 
     1706            associated_objects.each do |associated_object| 
     1707              preload_single_object(id_to_object_map, reflection_name, associated_object, '_parent_object_id') 
     1708            end 
     1709          when :has_one 
     1710            top_objects.each {|object| object.send("set_#{reflection.name}_target", nil)} 
     1711            conditions = "#{table_name}.#{foreign_key} IN (?)" 
     1712            conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 
     1713            associated_objects =  reflection.klass.find :all, :conditions => [conditions, ids], 
     1714            :select => (options[:select] || "#{table_name}.*"), 
     1715            :order => options[:order] 
     1716            associated_objects.each do |associated_object| 
     1717              preload_single_object(id_to_object_map, reflection_name, associated_object, foreign_key) 
     1718            end 
     1719 
     1720          when :has_many 
     1721            top_objects.each {|object| object.send(reflection.name).loaded} 
     1722             
     1723            if options[:through] 
     1724              through_reflection = reflections[options[:through]]               
     1725              through_primary_key = through_reflection.primary_key_name 
     1726              top_objects.first.class.preload_one_association top_objects, options[:through] 
     1727              through_objects = top_objects.compact.map {|object| object.send options[:through]}.flatten 
     1728               
     1729              unless through_objects.empty? 
     1730                source = reflection.source_reflection.name 
     1731                through_objects.first.class.preload_one_association through_objects, source 
     1732               
     1733                through_objects.compact.each do |through_object| 
     1734                  association_proxy = id_to_object_map[through_object[through_primary_key].to_i].send(reflection_name) 
     1735                  association_proxy.loaded 
     1736                  associated_object = through_object.send(source) 
     1737                  if associated_object.is_a? Array 
     1738                    association_proxy.target.push(*associated_object) 
     1739                  else 
     1740                    association_proxy.target << associated_object 
     1741                  end 
     1742                end 
     1743              end 
     1744               
     1745            else 
     1746              conditions = "#{table_name}.#{foreign_key} IN (?)" 
     1747              conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 
     1748              associated_objects =  reflection.klass.find :all, :conditions => [conditions, ids], 
     1749                                  :select => (options[:select] || "#{table_name}.*"), 
     1750                                  :order => options[:order] 
     1751              associated_objects.each do |associated_object| 
     1752                preload_collection_object(id_to_object_map, reflection_name, associated_object, foreign_key) 
     1753              end 
     1754            end 
     1755          when :has_and_belongs_to_many 
     1756            top_objects.each {|object| object.send(reflection.name).loaded} 
     1757            conditions = "t0.#{reflection.primary_key_name}  IN (?)" 
     1758            conditions << " AND (#{options[:conditions]})" if options[:conditions] 
     1759            associated_objects = reflection.klass.find :all, :conditions => [conditions, ids], 
     1760            :joins => "INNER JOIN #{options[:join_table]} as t0 ON #{reflection.klass.table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", 
     1761            :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as _parent_object_id", 
     1762            :order => options[:order] 
     1763            associated_objects.each do |associated_object| 
     1764              preload_collection_object(id_to_object_map, reflection_name, associated_object, '_parent_object_id') 
     1765            end 
     1766          else 
     1767            raise "Unsupported association type #{reflection.name}!" 
     1768          end 
     1769           
     1770        end 
     1771         
    16431772    end 
    16441773 
    16451774    public