Class: OccamsRecord::EagerLoaders::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/occams-record/eager_loaders/context.rb

Overview

A container for all eager loading on a particular Active Record model. Usually the context is initialized with the model, and all eager loaders are immediately initialized. Any errors (like a wrong association name ) will be thrown immediately and before any queries are run.

However, in certain situations the model cannot be known until runtime (e.g. eager loading off of a polymorphic association). In these cases the model won’t be set, or the eager loaders fully initialized, until the parent queries have run. This means that certain errors (like a wrong association name) won’t be noticed until very late, after queries have started running.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model = nil, tracer: nil, polymorphic: false) ⇒ Context

Initialize a new eager loading context.

Parameters:

  • mode (ActiveRecord::Base)

    the model that contains the associations that will be referenced.

  • tracer (OccamsRecord::EagerLoaders::Tracer) (defaults to: nil)

    the eager loader that owns this context (if any)

  • polymorphic (Boolean) (defaults to: false)

    When true, model is allowed to change, and it’s assumed that not every loader is applicable to every model.



27
28
29
30
31
32
# File 'lib/occams-record/eager_loaders/context.rb', line 27

def initialize(model = nil, tracer: nil, polymorphic: false)
  @model, @polymorphic = model, polymorphic
  @tracer = tracer || OccamsRecord::EagerLoaders::Tracer.new("root")
  @loaders = []
  @dynamic_loaders = []
end

Instance Attribute Details

#modelActiveRecord::Base

Returns:

  • (ActiveRecord::Base)


15
16
17
# File 'lib/occams-record/eager_loaders/context.rb', line 15

def model
  @model
end

#tracerOccamsRecord::EagerLoaders::Tracer (readonly)



18
19
20
# File 'lib/occams-record/eager_loaders/context.rb', line 18

def tracer
  @tracer
end

Instance Method Details

#<<(loader) ⇒ OccamsRecord::EagerLoaders::Base

Append an already-initialized eager loader.

Parameters:

Returns:



63
64
65
66
# File 'lib/occams-record/eager_loaders/context.rb', line 63

def <<(loader)
  @loaders << loader
  loader
end

#add(assoc, scope = nil, select: nil, use: nil, as: nil, from: nil, optimizer: :select, active_record_fallback: nil) { ... } ⇒ OccamsRecord::EagerLoaders::Base

Specify an association to be eager-loaded. For maximum memory savings, only SELECT the colums you actually need.

ActiveRecord::Relation on which you may call all the normal query hethods (select, where, etc) as well as any scopes you’ve defined on the model.

Parameters:

  • assoc (Symbol)

    name of association

  • scope (Proc) (defaults to: nil)

    a scope to apply to the query (optional). It will be passed an

  • select (String) (defaults to: nil)

    a custom SELECT statement, minus the SELECT (optional)

  • use (Array<Module>) (defaults to: nil)

    optional Module to include in the result class (single or array)

  • as (Symbol) (defaults to: nil)

    Load the association usign a different attribute name

  • from (Symbol) (defaults to: nil)

    Opposite of ‘as`. `assoc` is the custom name and `from` is the name of association on the ActiveRecord model.

  • optimizer (Symbol) (defaults to: :select)

    Only used for ‘through` associations. Options are :none (load all intermediate records) | :select (load all intermediate records but only SELECT the necessary columns)

  • active_record_fallback (Symbol) (defaults to: nil)

    If passed, missing methods will be forwarded to an ActiveRecord instance. Options are :lazy (allow lazy loading in the AR record) or :strict (require strict loading)

Yields:

  • a block where you may perform eager loading on this association (optional)

Returns:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/occams-record/eager_loaders/context.rb', line 84

def add(assoc, scope = nil, select: nil, use: nil, as: nil, from: nil, optimizer: :select, active_record_fallback: nil, &builder)
  if from
    real_assoc = from
    custom_name = assoc
  elsif as
    real_assoc = assoc
    custom_name = as
  else
    real_assoc = assoc
    custom_name = nil
  end

  if @model
    loader = build_loader!(real_assoc, custom_name, scope, select, use, optimizer, builder, active_record_fallback)
    @loaders << loader
    loader
  else
    @dynamic_loaders << [real_assoc, custom_name, scope, select, use, optimizer, builder, active_record_fallback]
    nil
  end
end

#eachObject

Iterate over each loader



121
122
123
124
# File 'lib/occams-record/eager_loaders/context.rb', line 121

def each
  return @loaders.each unless block_given?
  @loaders.each { |l| yield l }
end

#namesArray<String>

Return the names of the associations being loaded.

Returns:

  • (Array<String>)


52
53
54
55
# File 'lib/occams-record/eager_loaders/context.rb', line 52

def names
  @loaders.map(&:name) |
    @loaders.select { |l| l.respond_to? :through_name }.map(&:through_name) # TODO make not hacky
end

#run!(rows, query_logger: nil, measurements: nil) ⇒ Object

Performs all eager loading in this context (and in any nested ones).

Parameters:

  • rows (Array<ActiveRecord::Base>)

    the parent rows to load child rows into

  • query_logger (Array) (defaults to: nil)

    optional query logger



112
113
114
115
116
117
118
# File 'lib/occams-record/eager_loaders/context.rb', line 112

def run!(rows, query_logger: nil, measurements: nil)
  raise "Cannot run eager loaders when @model has not been set!" if @dynamic_loaders.any? and @model.nil?
  @loaders.each { |loader|
    loader.run(rows, query_logger: query_logger, measurements: measurements)
  }
  nil
end