Manually specifying GID classes
avokhmin opened this issue · comments
Sometimes might want to explicitly declare which class to use for a given class, instead of letting GID infer it.
For example:
class Post
def self.gid_class
Post.name
end
end
class Post::Foo < Post
end
class Post::Bar < Post
end
and
module URI
class GID < Generic
class << self
def create(app, model, params = nil)
model_name = model.try(:gid_class) || model.class.name
build app: app, model_name: model_name, model_id: model.id, params: params
end
end
end
end
The main usage case is GID + ActiveJob. For example, if update a type
of record, all old Jobs will be failed, because they will not be able to find a record by old URL.
So, any ideas how to resolve it, or better to implement something like this?
PS: Solution by https://github.com/elabs/pundit/tree/v1.0.1#manually-specifying-policy-classes
+1
+1
Perhaps there's a better way to make GIobal ID work with STI models. Can you share your job class or something like it that demonstrates the type changes? 😁
For example:
class Item < ActiveRecord::Base
end
# Public: STI Item subclass corresponding to "text" item type.
class Item::Text < Item
end
# Public: STI Item subclass corresponding to "photo" item type.
class Item::Photo < Item
end
item = Item::Text.create(name: 'foo')
item.to_global_id.to_s
=> "gid://app/Item::Text/123"
UpdateBarJob.set(wait_until: 15.minutes.from_now).perform_later(item)
item = item.becomes(Item::Photo)
item.update(image_id: 'baz')
item.to_global_id.to_s
=> "gid://app/Item::Photo/123"
15 minutes later: UpdateBarJob - will do nothing because GID will not be able to find a record.
Thanks! This strikes me as very odd though. In my book the STI type is unlikely to change and the becomes
feels like advanced usage to me. Therefore I worry about introducing this behavior, and recommend you try a custom app locator.
Custom app locators effectively let you change how a lookup is done. This could be a starting point:
class Item < ActiveRecord::Base
def to_global_id(**params)
super(params.merge(type: type))
end
end
# config/initializers/global_id.rb
GlobalID::Locator.use :your_app do |gid|
if gid.params[:type].present?
gid.model_class.becomes(gid.params[:type]).find(gid.model_id)
else
gid.model_class.find(gid.model_id)
end
end
Haven't tested or tried it at all, so have at it 😄
Thanks @kaspth for help!