My Rails build contains no pathnames. The application consists of two main objects: collections
and items
. So if I have a link: https://foo.com/foo, this could potentially identify either a collection or an item. The difference is made clear within the context and UI flow throughout the application.
My question: is there a way to have the Friendly ID gem generate unique slugs by seeing if a slug has already been taken by another object? I understand you can generate candidates so that friendly ID will not duplicate a slug for a given object, but I need friendly ID to check both existing collection slugs and item slugs before generating a new slug.
I hope that didn't sound too confusing. To re-word more concisely: is there a method available for friendly ID to check for slugs in multiple objects before generating a new slug?
NOTE This is all untested, just working from docs and reading the source code.
You could inherit the FriendlyId::SlugGenerator class, and override the available? method to check for the existing records in the opposing model:
class CrossModelSlugGenerator << FriendlyId::SlugGenerator
def available?(slug)
if (@scope.class == "Item::ActiveRecord_Relation")
# Search for collections with this slug and return false if they exist.
elsif (@scope.class == "Collection::ActiveRecord_Relation")
# Search for items with the this slug and return false if they exist.
end
# Otherwise do a normal slug check
!@scope.exists_by_friendly_id?(slug)
end
end
You can see the full code of the SlugGenerator class here:
https://github.com/norman/friendly_id/blob/master/lib/friendly_id/slug_generator.rb
Then you would have to tell the Friendly ID configuration to use that class instead. Create an initializer in config/intitializers/friendly_id.rb:
FriendlyId.defaults do |config|
config.slug_generator_class = "CrossModelSlugGenerator"
end
Try that out and see if it works out for you. Again, I haven't tested any of it, but it seems like it should work out.
EDIT - You may need to wrap the class in the FriendlyId module like this:
You might need an include somewhere, possibly in your class definition. Also, try wrapping the class into the FriendlyId module, so maybe something like this:
include "friendly_id"
module FriendlyId
class CrossModelSlugGenerator << SlugGenerator
...
end
end
With this change, you may also need to explicitly specify the module in the config class name:
FriendlyId.defaults do |config|
config.slug_generator_class = "FriendlyId::CrossModelSlugGenerator"
end