ActiveRecord provides the ids
method to pluck ids from a scope, but what if you need to pluck
Global IDs
Show archive.org snapshot
?
While you could just call map(&:to_global_id)
on your scope, this approach would instantiate each record just to do that. When you have many records, this will at the very least be slow.
Here is a method that does it for you efficiently. It respects Single Table Inheritance (STI).
Put it in your project's ApplicationRecord
to make it available on all models.
class ApplicationRecord
def self.global_ids
# This method creates GID values for the current scope, similar to
# ActiveRecord's `ids` method. For performance, we read id and type
# directly from the database and create GIDs from them.
# Note that we do not return GlobalID objects, but GID URI strings.
columns = [primary_key]
columns << inheritance_column if column_names.include?(inheritance_column)
# To avoid instantiating the model class itself (which is expensive)
# we define temporary classes which hold just enough information
# for the GID to be built. We do this only once per type.
anonymous_classes_by_type = Hash.new do |hash, type|
klass = Struct.new(:id)
klass.define_singleton_method(:name) { type }
hash[type] = klass
end
# We want to use `GlobalID.create` to avoid constructing a GID
# ourselves, and because it includes some sanity checks as well.
pluck(*columns).map do |(id, type)|
klass = anonymous_classes_by_type[type || name]
GlobalID.create(klass.new(id)).to_s
end
end
end
Example usage:
>> User.global_ids
=> ["gid://your-app/User/1", "gid://your-app/User/2", "gid://your-app/User/3"]
>> Page.global_ids
=> ["gid://your-app/Page::BlogPost/1", "gid://your-app/Page::PressRelease/2", "gid://your-app/Page::BlogPost/3"]
Posted by Arne Hartherz to makandra dev (2020-09-18 07:32)