-
Notifications
You must be signed in to change notification settings - Fork 106
/
active_storage_loader_sti.rb
90 lines (78 loc) · 2.82 KB
/
active_storage_loader_sti.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
####
# This is a loader for Active Storage attachments,
# a little bit more complex than the `active_storage_loader.rb` example
# because it supports STI (Single Table Inheritance) models where the
# `has_one_attached` or `has_many_attached` is defined on any of the ancestor classes of the model.
####
# The model with an attached image and many attached pictures:
####
# class Event < ApplicationRecord
# has_one_attached :image
# has_many_attached :pictures
# end
####
# An example data type using the AttachmentLoader:
####
# class Types::EventType < Types::BaseObject
# graphql_name 'Event'
#
# field :id, ID, null: false
# field :image, String, null: true
# field :pictures, String, null: true
#
# def image
# AttachmentLoader.for(:Event, :image).load(object.id).then do |image|
# Rails.application.routes.url_helpers.url_for(
# image.variant({ quality: 75 })
# )
# end
# end
#
# def pictures
# AttachmentLoader.for(:Event, :pictures, association_type: :has_many_attached).load(object.id).then do |pictures|
# pictures.map do |picture|
# Rails.application.routes.url_helpers.url_for(
# picture.variant({ quality: 75 })
# )
# end
# end
# end
# end
class ActiveStorageLoader < GraphQL::Batch::Loader
attr_reader :record_type, :attachment_name, :association_type
def initialize(record_type, attachment_name, association_type: :has_one_attached)
super()
@record_type = record_type
@attachment_name = attachment_name
@association_type = association_type
end
def perform(record_ids)
attachments = ActiveStorage::Attachment.includes(:blob, :record).where(
record_type: ancestors_record_types, record_id: record_ids, name: attachment_name
)
if @association_type == :has_one_attached
attachments.each do |attachment|
fulfill(attachment.record_id, attachment)
end
record_ids.each { |id| fulfill(id, nil) unless fulfilled?(id) }
else
record_ids.each do |record_id|
fulfill(record_id, attachments.select { |attachment| attachment.record_id == record_id })
end
end
end
private
def ancestors_record_types
# Get all ancestor classes of record_type that are descendants of ActiveRecord::Base
# This is necessary because in a Single Table Inheritance (STI) setup,
# the `has_one_attached` or `has_many_attached`
# could be defined on any of the ancestor classes of the model, not just the model itself,
# which determines whether the `record_type` string is stored as the model's class name
# or the ancestor's class name.
# So we for any of the ancestor classes to ensure we don't miss the attachment
# we are looking for:
@record_type.to_s.constantize.ancestors.select do |ancestor|
ancestor < ActiveRecord::Base
end.map(&:to_s)
end
end