demarches-normaliennes/app/graphql/loaders/association.rb
2023-05-03 10:10:38 +02:00

69 lines
2 KiB
Ruby

# references:
# https://github.com/Shopify/graphql-batch/blob/master/examples/association_loader.rb
# https://gist.github.com/palkan/03eb5306a1a3e8addbe8df97a298a466
# https://evilmartians.com/chronicles/active-storage-meets-graphql-pt-2-exposing-attachment-urls
module Loaders
class Association < GraphQL::Batch::Loader
def self.validate(model, association_name)
new(model, association_name)
nil
end
def initialize(model, association_schema)
@model = model
@association_schema = association_schema
@association_name = extract_association_id(association_schema)
validate
end
def load(record)
raise TypeError, "#{@model} loader can't load association for #{record.class}" unless record.is_a?(@model)
return Promise.resolve(read_association(record)) if association_loaded?(record)
super
end
# We want to load the associations on all records, even if they have the same id
def cache_key(record)
record.object_id
end
def perform(records)
preload_association(records.uniq)
records.each { |record| fulfill(record, read_association(record)) }
end
private
def validate
unless @model.reflect_on_association(@association_name)
raise ArgumentError, "No association #{@association_name} on #{@model}"
end
end
def preload_association(records)
::ActiveRecord::Associations::Preloader.new(
records: records,
associations: @association_schema
).call
end
def read_association(record)
record.public_send(@association_name)
end
def association_loaded?(record)
record.association(@association_name).loaded?
end
def extract_association_id(id_or_hash)
return id_or_hash unless id_or_hash.is_a?(Hash)
if id_or_hash.keys.size != 1
raise ArgumentError, "You can only preload exactly one association! You passed: #{id_or_hash}"
end
id_or_hash.keys.first
end
end
end