Class | Sequel::Model::Associations::EagerGraphLoader
In: | lib/sequel/model/associations.rb
Parent: | Object

This class is the internal implementation of eager_graph. It is responsible for taking an array of plain hashes and returning an array of model objects with all eager_graphed associations already set in the association cache.

### Methods

load new

### Attributes

after_load_map | [R] | Hash with table alias symbol keys and after_load hook values
alias_map | [R] | Hash with table alias symbol keys and association name values
column_maps | [R] | Hash with table alias symbol keys and subhash values mapping column_alias symbols to the symbol of the real name of the column
dependency_map | [R] | Recursive hash with table alias symbol keys mapping to hashes with dependent table alias symbol keys.
limit_map | [R] | Hash with table alias symbol keys and [limit, offset] values
master | [R] | Hash with table alias symbol keys and callable values used to create model instances The table alias symbol for the primary model
primary_keys | [R] | Hash with table alias symbol keys and primary key symbol values (or arrays of primary key symbols for composite key tables)
reciprocal_map | [R] | Hash with table alias symbol keys and reciprocal association symbol values, used for setting reciprocals for one_to_many associations.
records_map | [R] | Hash with table alias symbol keys and subhash values mapping primary key symbols (or array of symbols) to model instances. Used so that only a single model instance is created for each object.
reflection_map | [R] | Hash with table alias symbol keys and AssociationReflection values
row_procs | [R] | Hash with table alias symbol keys and callable values used to create model instances
type_map | [R] | Hash with table alias symbol keys and true/false values, where true means the association represented by the table alias uses an array of values instead of a single value (i.e. true => *_many, false => *_to_one).

### Public Class methods

new(dataset)

Initialize all of the data structures used during loading.

[Source]

          # File lib/sequel/model/associations.rb, line 2013
    2013:         def initialize(dataset)
    2014:           opts = dataset.opts
    2015:           eager_graph = opts[:eager_graph]
    2016:           @master =  eager_graph[:master]
    2017:           requirements = eager_graph[:requirements]
    2018:           reflection_map = @reflection_map = eager_graph[:reflections]
    2019:           reciprocal_map = @reciprocal_map = eager_graph[:reciprocals]
    2020:           @unique = eager_graph[:cartesian_product_number] > 1
    2021:
    2022:           alias_map = @alias_map = {}
    2023:           type_map = @type_map = {}
    2024:           after_load_map = @after_load_map = {}
    2025:           limit_map = @limit_map = {}
    2026:           reflection_map.each do |k, v|
    2027:             alias_map[k] = v[:name]
    2028:             type_map[k] = v.returns_array?
    2029:             after_load_map[k] = v[:after_load] unless v[:after_load].empty?
    2030:             limit_map[k] = v.limit_and_offset if v[:limit]
    2031:           end
    2032:
    2033:           # Make dependency map hash out of requirements array for each association.
    2034:           # This builds a tree of dependencies that will be used for recursion
    2035:           # to ensure that all parts of the object graph are loaded into the
    2036:           # appropriate subordinate association.
    2037:           @dependency_map = {}
    2038:           # Sort the associations by requirements length, so that
    2039:           # requirements are added to the dependency hash before their
    2040:           # dependencies.
    2041:           requirements.sort_by{|a| a[1].length}.each do |ta, deps|
    2042:             if deps.empty?
    2043:               dependency_map[ta] = {}
    2044:             else
    2045:               deps = deps.dup
    2046:               hash = dependency_map[deps.shift]
    2047:               deps.each do |dep|
    2048:                 hash = hash[dep]
    2049:               end
    2050:               hash[ta] = {}
    2051:             end
    2052:           end
    2053:
    2054:           # This mapping is used to make sure that duplicate entries in the
    2055:           # result set are mapped to a single record.  For example, using a
    2056:           # single one_to_many association with 10 associated records,
    2057:           # the main object column values appear in the object graph 10 times.
    2058:           # We map by primary key, if available, or by the object's entire values,
    2059:           # if not. The mapping must be per table, so create sub maps for each table
    2060:           # alias.
    2061:           records_map = {@master=>{}}
    2062:           alias_map.keys.each{|ta| records_map[ta] = {}}
    2063:           @records_map = records_map
    2064:
    2065:           datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
    2066:           column_aliases = opts[:graph_aliases] || opts[:graph][:column_aliases]
    2067:           primary_keys = {}
    2068:           column_maps = {}
    2069:           models = {}
    2070:           row_procs = {}
    2071:           datasets.each do |ta, ds|
    2072:             models[ta] = ds.model
    2073:             primary_keys[ta] = []
    2074:             column_maps[ta] = {}
    2075:             row_procs[ta] = ds.row_proc
    2076:           end
    2077:           column_aliases.each do |col_alias, tc|
    2078:             ta, column = tc
    2079:             column_maps[ta][col_alias] = column
    2080:           end
    2081:           column_maps.each do |ta, h|
    2082:             pk = models[ta].primary_key
    2083:             if pk.is_a?(Array)
    2084:               primary_keys[ta] = []
    2085:               h.select{|ca, c| primary_keys[ta] << ca if pk.include?(c)}
    2086:             else
    2087:               h.select{|ca, c| primary_keys[ta] = ca if pk == c}
    2088:             end
    2089:           end
    2090:           @column_maps = column_maps
    2091:           @primary_keys = primary_keys
    2092:           @row_procs = row_procs
    2093:
    2094:           # For performance, create two special maps for the master table,
    2095:           # so you can skip a hash lookup.
    2096:           @master_column_map = column_maps[master]
    2097:           @master_primary_keys = primary_keys[master]
    2098:
    2099:           # Add a special hash mapping table alias symbols to 5 element arrays that just
    2100:           # contain the data in other data structures for that table alias.  This is
    2101:           # used for performance, to get all values in one hash lookup instead of
    2102:           # separate hash lookups for each data structure.
    2103:           ta_map = {}
    2104:           alias_map.keys.each do |ta|
    2105:             ta_map[ta] = [records_map[ta], row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]]
    2106:           end
    2107:           @ta_map = ta_map
    2108:         end

### Public Instance methods

load(hashes)

Return an array of primary model instances with the associations cache prepopulated for all model objects (both primary and associated).

[Source]

          # File lib/sequel/model/associations.rb, line 2112
    2112:         def load(hashes)
    2113:           master = master()
    2114:
    2115:           # Assign to local variables for speed increase
    2116:           rp = row_procs[master]
    2117:           rm = records_map[master]
    2118:           dm = dependency_map
    2119:
    2120:           # This will hold the final record set that we will be replacing the object graph with.
    2121:           records = []
    2122:
    2123:           hashes.each do |h|
    2124:             unless key = master_pk(h)
    2125:               key = hkey(master_hfor(h))
    2126:             end
    2127:             unless primary_record = rm[key]
    2128:               primary_record = rm[key] = rp.call(master_hfor(h))
    2129:               # Only add it to the list of records to return if it is a new record
    2130:               records.push(primary_record)
    2131:             end
    2132:             # Build all associations for the current object and it's dependencies
    2133:             _load(dm, primary_record, h)
    2134:           end
    2135:
    2136:           # Remove duplicate records from all associations if this graph could possibly be a cartesian product
    2137:           # Run after_load procs if there are any
    2138:           post_process(records, dm) if @unique || !after_load_map.empty? || !limit_map.empty?
    2139:
    2140:           records
    2141:         end

[Validate]