Skip to content

Conversation

Paul-Bob
Copy link

@Paul-Bob Paul-Bob commented Aug 4, 2025

Fixes #1034
Possibly fixes #1018 (not confirmed)

Description

This PR adds support for finding records using an array of slugs, similar to how ActiveRecord handles arrays of numeric IDs. If FriendlyId is the "Swiss Army bulldozer" of slugging plugins, this enhancement adds laser-guided precision to that bulldozer!

Problem

Previously, while single slug lookups worked fine:

GlossaryTerm.find("one")        # ✅ Works
GlossaryTerm.find([1, 2])       # ✅ Works (numeric IDs)

Array slug lookups would fail:

GlossaryTerm.find(["one"])      # ❌ ActiveRecord::RecordNotFound
GlossaryTerm.find(["one", "two"]) # ❌ ActiveRecord::RecordNotFound

Solution

Now you can find records using:

  • Array of slugs: GlossaryTerm.find(["one", "two"])
  • Mixed arrays: GlossaryTerm.find(["one", 2, "three"]) (slugs + numeric IDs)
  • Partial results: GlossaryTerm.find(["one", "missing"], allow_nil: true)
  • Relations: GlossaryTerm.active.find(["one", "two"])

Key Features

  • Maintains backward compatibility
  • Proper error handling with ActiveRecord::RecordNotFound
  • Supports allow_nil: true for partial results
  • Works with scoped relations
  • Handles empty arrays gracefully
  • Efficient queries (separate slug and ID lookups)

Testing

Added comprehensive test coverage including:

  • Array slug finding
  • Mixed array handling
  • Error cases
  • Edge cases (empty arrays, single elements)
  • Relation scoping
  • All existing tests continue to pass

This enhancement makes FriendlyId even more developer-friendly while maintaining it's robustness.

@Paul-Bob
Copy link
Author

Paul-Bob commented Aug 4, 2025

Hi @norman, thank you for the amazing work on this gem!

I would appreciate a review on this PR. Could you please let me know if there is a chance for this to get merged, or if I should switch to a forked solution instead?

@norman
Copy link
Owner

norman commented Aug 4, 2025

Hello! Thanks for taking the time to submit such a well-thought out pull request to this library.

I haven't been involved with the project for quite a few years, @parndt has done almost all maintaining for a long time. I won't offer an opinion of whether this should be merged, but I will offer some context.

FriendlyId used to have this feature and I removed it (I think in version 4) because it was was hard to maintain:

  • It made it more complex to work with FriendlyId::History
  • Performance was sometimes quite poor for real-world apps using it
  • I received semi-frequent inquiries about bugs and edge cases

When I removed this feature, very few people complained, so I felt that I had probably been spending a lot of time maintaining a complicated feature that few people were using, and I never looked back.

My thoughts at the time were that if you really needed to look up multiple slugs in a high performance way, that was an unusual enough case for this library that we maintainers could reasonably expect that you should write your own custom query against the slug column like:

MyModel.where(slug: my_array_of_slugs)

or do something similar against the slugs table if you're using History.

If you feel strongly that you want this released soon, you might want to consider writing it as a plugin or an add-on to FriendyId in a separate gem, that would probably make it easier for you to get people to use it and wouldn't impose the same maintainence burden for you that a full-on fork would entail. FriendlyId was designed to be extensible from the beginning, with most of its core functionality implemented as add-ons.

@adrianthedev
Copy link

Hey @norman.
Thanks for chiming in. I appreciate the context.

I am Adrian, the author of Avo and a teammate of @Paul-Bob.
I suggested to him to PR this change.

Let me give you some context into why we would love to have this change and why we probably can't do what you suggest very easily.

Avo is a bring-your-own library kind of situation. We don't have friendly_id baked in as we don't want the user to have to have a library that they are not using.
They choose to use it (and we use it extensively), so it's difficult for us to write a plugin and confidently require it.
It's wasteful to require a plugin gem for a library that they might not use.

Regarding the proposed change (, it's again something out of our control.
We don't directly instrument friendly_id under the hood. The user does.
And we are forced to give out instruction in our documentation (sample below) which we both know nobody reads and then it kind of falls into one of "Avo is broken" or "Avo doesn't work nicely with friendly_id" buckets.

# we tell them to add this bit of code in resources which use friendly_id
# we'd rather have it "just work"
self.find_record_method = -> {
  if id.is_a?(Array)
    Post.where(slug: id)
  else
    Post.find(id)
  end
}

So yeah, this would improve compatibility with existing libraries which use the find query no matter which underlying slugging library is used, it preserves backward compatibility while enabling flexibility, and it reduces misattributed frustration from downstream developers which is more important in my view.

Personally, I'd love to be able to do things even if they are less performant than to have to jump through hoops to implement something to patch the original behavior.

Thanks for listening, and we are open to other suggestions as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Array support for finders Rails 7.1 composite key support
3 participants