Idea: Locating embedded resources
stevecrozz opened this issue · comments
The code I am providing here is to illustrate an idea, a seed for discussion, not a proposal to adopt it as-is.
Nevertheless, I have put together some handy code that lets me locate resources that are embedded within other resources, for example, embedded within a serialized column, like a postgres jsonb structure. I could make another gem out of this, but some form of this idea seems pretty generally useful. I wonder if the community here has any thoughts on whether this idea is worth iterating on, or importing into the globalid project.
The concept is a regular URI::GID where a "host model" can be located in the normal way, but adds additional params
to the URI::GID
in order to locate an object that is only locatable within its host. This implementation imposes no requirements on the name or format of those params, only that whatever params exist can be used by the "host model" to locate the required sub-resource. The host model need only implement an instance method, gid_sublocate
in this case, and given the gid property, it can do whatever work is needed to find the specified subresource.
In a nutshell, this means resources that are not normally globally addressable can become globally addressable by leaning on the addressability of some host model.
# Allows objects to be located within other objects and referenced through the
# GlobalID gem. In order to participate, the sublocatable's class must
# implement an instance method `to_global_id` and the host class must implement
# `gid_sublocate`
#
# # Returns a sublocated object if one can be found, for example:
# def gid_sublocator(gid)
# key = gid.params[:some_key]
# serialized_column[key]
# end
#
# # Returns a GlobalID object which will be passed to gid_sublocator to
# # locate this object later, for example:
# def to_global_id(options = {})
# GlobalID.new(
# URI::GID.build(
# app: GlobalID.app,
# model_name: 'Host::Model',
# model_id: 123,
# params: {
# some_key: 'abc'
# }
# )
# )
# end
class GidSublocator < GlobalID::Locator::UnscopedLocator
def locate(gid, options = {})
sublocate(super, gid)
end
def locate_many(gids, options = {})
super.zip(gids).map do |(located, gid)|
sublocate(located, gid)
end
end
private def sublocate(located, gid)
located.try(:gid_sublocate, gid) || located
end
end