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 3729 def initialize(dataset) 3730 opts = dataset.opts 3731 eager_graph = opts[:eager_graph] 3732 @master = eager_graph[:master] 3733 requirements = eager_graph[:requirements] 3734 reflection_map = @reflection_map = eager_graph[:reflections] 3735 reciprocal_map = @reciprocal_map = eager_graph[:reciprocals] 3736 limit_map = @limit_map = eager_graph[:limits] 3737 @unique = eager_graph[:cartesian_product_number] > 1 3738 3739 alias_map = @alias_map = {} 3740 type_map = @type_map = {} 3741 after_load_map = @after_load_map = {} 3742 reflection_map.each do |k, v| 3743 alias_map[k] = v[:name] 3744 after_load_map[k] = v[:after_load] if v[:after_load] 3745 type_map[k] = if v.returns_array? 3746 true 3747 elsif (limit_and_offset = limit_map[k]) && !limit_and_offset.last.nil? 3748 :offset 3749 end 3750 end 3751 after_load_map.freeze 3752 alias_map.freeze 3753 type_map.freeze 3754 3755 # Make dependency map hash out of requirements array for each association. 3756 # This builds a tree of dependencies that will be used for recursion 3757 # to ensure that all parts of the object graph are loaded into the 3758 # appropriate subordinate association. 3759 dependency_map = @dependency_map = {} 3760 # Sort the associations by requirements length, so that 3761 # requirements are added to the dependency hash before their 3762 # dependencies. 3763 requirements.sort_by{|a| a[1].length}.each do |ta, deps| 3764 if deps.empty? 3765 dependency_map[ta] = {} 3766 else 3767 deps = deps.dup 3768 hash = dependency_map[deps.shift] 3769 deps.each do |dep| 3770 hash = hash[dep] 3771 end 3772 hash[ta] = {} 3773 end 3774 end 3775 freezer = lambda do |h| 3776 h.freeze 3777 h.each_value(&freezer) 3778 end 3779 freezer.call(dependency_map) 3780 3781 datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?} 3782 column_aliases = opts[:graph][:column_aliases] 3783 primary_keys = {} 3784 column_maps = {} 3785 models = {} 3786 row_procs = {} 3787 datasets.each do |ta, ds| 3788 models[ta] = ds.model 3789 primary_keys[ta] = [] 3790 column_maps[ta] = {} 3791 row_procs[ta] = ds.row_proc 3792 end 3793 column_aliases.each do |col_alias, tc| 3794 ta, column = tc 3795 column_maps[ta][col_alias] = column 3796 end 3797 column_maps.each do |ta, h| 3798 pk = models[ta].primary_key 3799 if pk.is_a?(Array) 3800 primary_keys[ta] = [] 3801 h.select{|ca, c| primary_keys[ta] << ca if pk.include?(c)} 3802 else 3803 h.select{|ca, c| primary_keys[ta] = ca if pk == c} 3804 end 3805 end 3806 @column_maps = column_maps.freeze 3807 @primary_keys = primary_keys.freeze 3808 @row_procs = row_procs.freeze 3809 3810 # For performance, create two special maps for the master table, 3811 # so you can skip a hash lookup. 3812 @master_column_map = column_maps[master] 3813 @master_primary_keys = primary_keys[master] 3814 3815 # Add a special hash mapping table alias symbols to 5 element arrays that just 3816 # contain the data in other data structures for that table alias. This is 3817 # used for performance, to get all values in one hash lookup instead of 3818 # separate hash lookups for each data structure. 3819 ta_map = {} 3820 alias_map.each_key do |ta| 3821 ta_map[ta] = [row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]].freeze 3822 end 3823 @ta_map = ta_map.freeze 3824 freeze 3825 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 3829 def load(hashes) 3830 # This mapping is used to make sure that duplicate entries in the 3831 # result set are mapped to a single record. For example, using a 3832 # single one_to_many association with 10 associated records, 3833 # the main object column values appear in the object graph 10 times. 3834 # We map by primary key, if available, or by the object's entire values, 3835 # if not. The mapping must be per table, so create sub maps for each table 3836 # alias. 3837 @records_map = records_map = {} 3838 alias_map.keys.each{|ta| records_map[ta] = {}} 3839 3840 master = master() 3841 3842 # Assign to local variables for speed increase 3843 rp = row_procs[master] 3844 rm = records_map[master] = {} 3845 dm = dependency_map 3846 3847 records_map.freeze 3848 3849 # This will hold the final record set that we will be replacing the object graph with. 3850 records = [] 3851 3852 hashes.each do |h| 3853 unless key = master_pk(h) 3854 key = hkey(master_hfor(h)) 3855 end 3856 unless primary_record = rm[key] 3857 primary_record = rm[key] = rp.call(master_hfor(h)) 3858 # Only add it to the list of records to return if it is a new record 3859 records.push(primary_record) 3860 end 3861 # Build all associations for the current object and it's dependencies 3862 _load(dm, primary_record, h) 3863 end 3864 3865 # Remove duplicate records from all associations if this graph could possibly be a cartesian product 3866 # Run after_load procs if there are any 3867 post_process(records, dm) if @unique || !after_load_map.empty? || !limit_map.empty? 3868 3869 records_map.each_value(&:freeze) 3870 freeze 3871 3872 records 3873 end