Module: Sequel::Packer::EagerLoading

Defined in:
lib/sequel/packer/eager_loading.rb

Class Method Summary collapse

Class Method Details

.eager_load(model_class, model_or_models, normalized_eager_hash) ⇒ Object

This methods allows eager loading associations after a record, or multiple records, have been fetched from the database. It is useful when you know you will be accessing model associations, but models have already been materialized.

This method accepts a normalized_eager_hash, as specified by Sequel::Packer::EagerHash.

This method will handle procs used to filter association datasets, but if an association has already been loaded for every model, the dataset will not be refetched and the proc will not be applied.

This method borrows a lot from the #eager_load Sequel::Dataset method defined in Sequels lib/sequel/model/associations.rb.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/sequel/packer/eager_loading.rb', line 18

def self.eager_load(model_class, model_or_models, normalized_eager_hash)
  models = model_or_models.is_a?(Array) ?
    model_or_models :
    [model_or_models]

  # Cache to avoid building id maps multiple times.
  key_hash = {}

  normalized_eager_hash.each do |association, nested_associations|
    eager_block = nil

    if EagerHash.is_proc_hash?(nested_associations)
      eager_block, nested_associations = nested_associations.entries[0]
    end

    reflection = model_class.association_reflections[association]

    # If all of the models have already loaded the association, we'll just
    # recursively call ::eager_load to load nested associations.
    if models.all? {|m| m.associations.key?(association)}
      if nested_associations
        associated_records = if reflection.returns_array?
          models.flat_map {|m| m.send(association)}.uniq
        else
          models.map {|m| m.send(association)}.compact
        end

        eager_load(
          reflection.associated_class,
          associated_records,
          nested_associations,
        )
      end
    else
      key = reflection.eager_loader_key
      id_map = nil

      if key && !key_hash[key]
        id_map = Hash.new {|h, k| h[k] = []}

        models.each do |model|
          case key
          when Symbol
            model_id = model.get_column_value(key)
            id_map[model_id] << model if model_id
          when Array
            model_id = key.map {|col| model.get_column_value(col)}
            id_map[model_id] << model if model_id.all?
          else
            raise(
              Sequel::Error,
              "unhandled eager_loader_key #{key.inspect} for " +
                "association #{association}",
            )
          end
        end
      end

      loader = reflection[:eager_loader]

      loader.call(
        key_hash: key_hash,
        rows: models,
        associations: nested_associations,
        self: self,
        eager_block: eager_block,
        id_map: id_map,
      )

      if reflection[:after_load]
        models.each do |model|
          model.send(
            :run_association_callbacks,
            reflection,
            :after_load,
            model.associations[association],
          )
        end
      end
    end
  end
end