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