Class: LcpRuby::Presenter::IncludesResolver::LoadingStrategy

Inherits:
Object
  • Object
show all
Defined in:
lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb

Overview

Holds the resolved eager loading instructions and applies them to an AR scope.

Three separate lists mirror the three ActiveRecord eager loading methods:

- includes:   separate query by default (AR may use LEFT JOIN when combined with where/references)
- eager_load: always LEFT OUTER JOIN (needed for WHERE/ORDER on association columns)
- joins:      INNER JOIN (used alongside includes for has_many query deps)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(includes: [], eager_load: [], joins: [], api_preloads: []) ⇒ LoadingStrategy

Returns a new instance of LoadingStrategy.



13
14
15
16
17
18
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 13

def initialize(includes: [], eager_load: [], joins: [], api_preloads: [])
  @includes = includes
  @eager_load = eager_load
  @joins = joins
  @api_preloads = api_preloads
end

Instance Attribute Details

#api_preloadsObject (readonly)

Returns the value of attribute api_preloads.



11
12
13
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 11

def api_preloads
  @api_preloads
end

#eager_loadObject (readonly)

Returns the value of attribute eager_load.



11
12
13
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 11

def eager_load
  @eager_load
end

#includesObject (readonly)

Returns the value of attribute includes.



11
12
13
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 11

def includes
  @includes
end

#joinsObject (readonly)

Returns the value of attribute joins.



11
12
13
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 11

def joins
  @joins
end

Instance Method Details

#apply(scope) ⇒ Object

Chains all loading instructions onto the given AR scope. When virtual columns add custom .select() to the scope, switches :display dependencies to .preload() to avoid breaking the custom SELECT.



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
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 23

def apply(scope)
  has_custom_select = scope.respond_to?(:select_values) && scope.select_values.any?

  if @includes.any?
    if has_custom_select
      # .preload() always uses separate queries, unaffected by custom SELECT
      scope = scope.preload(*@includes)
    else
      scope = scope.includes(*@includes)
    end
  end

  if @eager_load.any?
    if has_custom_select
      # Merge eager_loaded table columns into existing SELECT so they
      # survive the custom .select() from virtual columns
      @eager_load.each do |assoc_name|
        assoc_name = assoc_name.to_s if assoc_name.is_a?(Symbol)
        next unless assoc_name.is_a?(String)
        reflection = scope.klass.reflect_on_association(assoc_name.to_sym)
        next unless reflection
        scope = scope.select("#{reflection.klass.table_name}.*")
      end
    end
    scope = scope.eager_load(*@eager_load)
  end

  scope = scope.joins(*@joins) if @joins.any?
  scope
end

#apply_api_preloads(records) ⇒ Object

Batch-preloads API associations for materialized records. Call after the AR scope has been loaded (e.g. after pagination).



56
57
58
59
60
61
62
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 56

def apply_api_preloads(records)
  return if @api_preloads.empty? || records.blank?

  @api_preloads.each do |preload|
    DataSource::ApiPreloader.preload(records, preload[:name], preload[:association])
  end
end

#empty?Boolean

Returns:

  • (Boolean)


64
65
66
# File 'lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb', line 64

def empty?
  @includes.empty? && @eager_load.empty? && @joins.empty? && @api_preloads.empty?
end