I’m writing a ROM/Hanami integration for Shrine, and I’m a bit stuck trying to figure out how to best integrate with Hanami entities.
First, I’ll try to illustrate how Shrine handles attachments with code:
class Entities
class Photo < Hanami::Entity
include Shrine::Attachment(:image)
end
end
attacher = Shrine::Attacher.new
attacher.attach(file)
attacher.file #=> #<Shrine::UploadedFile @id="path/to/file" ...>
# returns serialized attachment data that should be persisted
attacher.column_data
#=> '{"id":"...","storage":"...","metadata":{...}}'
photo = photo_repo.create(image_data: attacher.column_data)
# loads attachment from the data attribute
photo.image #=> #<Shrine::UploadedFile @id="path/to/file" ...>
The important bit is, when the record is loaded from the database and you retrieve the attachment, Shrine parses the attachment column data and creates a Shrine::UploadedFile
object.
Now, this parsing and loading can add a bit of overhead, especially when storing additional processed files alongside the main file. To remedy this, when working with mutable structs, Shrine lazily memoizes the results, so that parsing and loading Shrine objects happens only once.
With ROM::Struct
objects we can still use that approach, but with Hanami::Entity
objects we can’t, because Hanami entities are frozen. So, my questions is how to idiomatically handle this kind of performance optimization in Hanami?
I’ve investigated several options:
-
Load the attachment in
#initialize
, before the instance gets frozen. What I don’t like about this approach is that users are then paying the performance penalty of loading the attachment regardless of whether they will use it. -
Initialize a hash instance variable in
#initialize
, and use it later for memoization. This approach should work, because that hash won’t get frozen. Would this approach suit that Hanami philosophy?
To illustrate what I mean by option 2:
class Photo < Hanami::Entity
include Shrine::Attachment(:image)
def initialize(*args)
super
@attachments = {}
end
def image
@attachments[:image] ||= super
end
end