Goldnuggets
Goldnuggets

Reputation: 223

Friendly_id creating duplicate slugs for multiple objects in Rails

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?

Upvotes: 2

Views: 1388

Answers (1)

Jocko
Jocko

Reputation: 537

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
      [email protected]_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

Upvotes: 5

Related Questions