class Graphql::EagerLoad::Builder

Given

- An initial set of includes, a hash
- GraphQL query selections
- The model corresponding to the type returned by a GraphQL resolver

This module will iterate and recurse into the selctions to find out if any of the fields requested for a given type corresponds to an association on the ActiveRecord model associated with the field's type.

For example given this query

salesOpportunities() {

soldEstimateGroup {
  estimates {
   lineItems
  }
}

}

We'd return this “includes” hash

{sold_estimate_group: {estimates: {line_items: {}}}}

And our resolver will use it like `scope.includes(includes)` to prevent N+1s

Attributes

model[R]
selection[R]

Public Class Methods

call(selections:, model:) click to toggle source
# File lib/graphql/eager_load/builder.rb, line 34
def self.call(selections:, model:)
  selections.each.with_object({}) do |selection, includes|
    builder = new(selection: selection, model: model)

    if builder.association?
      hash = builder.includes
      hash[:blob] ||= {} if builder.active_storage_attachment?
      includes[builder.association_name] = hash
    else
      includes.merge!(builder.includes)
    end
  end
end
new(selection:, model:) click to toggle source
# File lib/graphql/eager_load/builder.rb, line 48
def initialize(selection:, model:)
  @selection = selection
  @model = model
end

Public Instance Methods

active_storage_attachment?() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 65
def active_storage_attachment?
  model.reflect_on_attachment(field_name).present?
end
association?() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 57
def association?
  association.present?
end
association_name() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 61
def association_name
  association.name
end
includes() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 53
def includes
  self.class.call(selections: selection.selections, model: includes_model)
end

Private Instance Methods

association() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 79
def association
  return if use_custom_method?
  return model.reflect_on_association("#{field_name}_attachment") if active_storage_attachment?

  model.reflect_on_association(field_name)
end
custom_method_defined?() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 96
def custom_method_defined?
  field_owner.instance_methods.include?(field_name)
end
field_name() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 104
def field_name
  selection.name
end
field_owner() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 100
def field_owner
  selection.field.owner
end
ignore_custom_method?() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 90
def ignore_custom_method?
  return false unless field_owner.respond_to?(:allow_include_builder_fields)

  field_owner.allow_include_builder_fields&.include?(field_name.to_sym)
end
includes_model() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 73
def includes_model
  return ActiveStorage::Attachment if active_storage_attachment?

  association&.klass || model
end
use_custom_method?() click to toggle source
# File lib/graphql/eager_load/builder.rb, line 86
def use_custom_method?
  custom_method_defined? && !ignore_custom_method?
end