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
Public Class
Public Instance
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] |
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 |
| 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
Initialize all of the data structures used during loading.
# File lib/sequel/model/associations.rb 3967 def initialize(dataset) 3968 opts = dataset.opts 3969 eager_graph = opts[:eager_graph] 3970 @master = eager_graph[:master] 3971 requirements = eager_graph[:requirements] 3972 reflection_map = @reflection_map = eager_graph[:reflections] 3973 reciprocal_map = @reciprocal_map = eager_graph[:reciprocals] 3974 limit_map = @limit_map = eager_graph[:limits] 3975 @unique = eager_graph[:cartesian_product_number] > 1 3976 3977 alias_map = @alias_map = {} 3978 type_map = @type_map = {} 3979 after_load_map = @after_load_map = {} 3980 reflection_map.each do |k, v| 3981 alias_map[k] = v[:name] 3982 after_load_map[k] = v[:after_load] if v[:after_load] 3983 type_map[k] = if v.returns_array? 3984 true 3985 elsif (limit_and_offset = limit_map[k]) && !limit_and_offset.last.nil? 3986 :offset 3987 end 3988 end 3989 after_load_map.freeze 3990 alias_map.freeze 3991 type_map.freeze 3992 3993 # Make dependency map hash out of requirements array for each association. 3994 # This builds a tree of dependencies that will be used for recursion 3995 # to ensure that all parts of the object graph are loaded into the 3996 # appropriate subordinate association. 3997 dependency_map = @dependency_map = {} 3998 # Sort the associations by requirements length, so that 3999 # requirements are added to the dependency hash before their 4000 # dependencies. 4001 requirements.sort_by{|a| a[1].length}.each do |ta, deps| 4002 if deps.empty? 4003 dependency_map[ta] = {} 4004 else 4005 deps = deps.dup 4006 hash = dependency_map[deps.shift] 4007 deps.each do |dep| 4008 hash = hash[dep] 4009 end 4010 hash[ta] = {} 4011 end 4012 end 4013 freezer = lambda do |h| 4014 h.freeze 4015 h.each_value(&freezer) 4016 end 4017 freezer.call(dependency_map) 4018 4019 datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?} 4020 column_aliases = opts[:graph][:column_aliases] 4021 primary_keys = {} 4022 column_maps = {} 4023 models = {} 4024 row_procs = {} 4025 datasets.each do |ta, ds| 4026 models[ta] = ds.model 4027 primary_keys[ta] = [] 4028 column_maps[ta] = {} 4029 row_procs[ta] = ds.row_proc 4030 end 4031 column_aliases.each do |col_alias, tc| 4032 ta, column = tc 4033 column_maps[ta][col_alias] = column 4034 end 4035 column_maps.each do |ta, h| 4036 pk = models[ta].primary_key 4037 if pk.is_a?(Array) 4038 primary_keys[ta] = [] 4039 h.select{|ca, c| primary_keys[ta] << ca if pk.include?(c)} 4040 else 4041 h.select{|ca, c| primary_keys[ta] = ca if pk == c} 4042 end 4043 end 4044 @column_maps = column_maps.freeze 4045 @primary_keys = primary_keys.freeze 4046 @row_procs = row_procs.freeze 4047 4048 # For performance, create two special maps for the master table, 4049 # so you can skip a hash lookup. 4050 @master_column_map = column_maps[master] 4051 @master_primary_keys = primary_keys[master] 4052 4053 # Add a special hash mapping table alias symbols to 5 element arrays that just 4054 # contain the data in other data structures for that table alias. This is 4055 # used for performance, to get all values in one hash lookup instead of 4056 # separate hash lookups for each data structure. 4057 ta_map = {} 4058 alias_map.each_key do |ta| 4059 ta_map[ta] = [row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]].freeze 4060 end 4061 @ta_map = ta_map.freeze 4062 freeze 4063 end
Public Instance methods
Return an array of primary model instances with the associations cache prepopulated for all model objects (both primary and associated).
# File lib/sequel/model/associations.rb 4067 def load(hashes) 4068 # This mapping is used to make sure that duplicate entries in the 4069 # result set are mapped to a single record. For example, using a 4070 # single one_to_many association with 10 associated records, 4071 # the main object column values appear in the object graph 10 times. 4072 # We map by primary key, if available, or by the object's entire values, 4073 # if not. The mapping must be per table, so create sub maps for each table 4074 # alias. 4075 @records_map = records_map = {} 4076 alias_map.keys.each{|ta| records_map[ta] = {}} 4077 4078 master = master() 4079 4080 # Assign to local variables for speed increase 4081 rp = row_procs[master] 4082 rm = records_map[master] = {} 4083 dm = dependency_map 4084 4085 records_map.freeze 4086 4087 # This will hold the final record set that we will be replacing the object graph with. 4088 records = [] 4089 4090 hashes.each do |h| 4091 unless key = master_pk(h) 4092 key = hkey(master_hfor(h)) 4093 end 4094 unless primary_record = rm[key] 4095 primary_record = rm[key] = rp.call(master_hfor(h)) 4096 # Only add it to the list of records to return if it is a new record 4097 records.push(primary_record) 4098 end 4099 # Build all associations for the current object and it's dependencies 4100 _load(dm, primary_record, h) 4101 end 4102 4103 # Remove duplicate records from all associations if this graph could possibly be a cartesian product 4104 # Run after_load procs if there are any 4105 post_process(records, dm) if @unique || !after_load_map.empty? || !limit_map.empty? 4106 4107 records_map.each_value(&:freeze) 4108 freeze 4109 4110 records 4111 end