friendly_id icon indicating copy to clipboard operation
friendly_id copied to clipboard

Inconsistent behavior for #parse_friendly_id

Open YushengLi opened this issue 4 years ago • 2 comments

Thanks for the great work in this project 🙇

We have opted-in the #first_by_friendly_id case insensitive change while upgrading to 5.4.0.

With 5.4.2 released, we hope to maintain the same behavior by override #parse_friendly_id in our model, but we have noticed same inconsistent behavior for the finders.

With the following model definition

class Product < ApplicationRecord
  extend FriendlyId
  friendly_id :name, use: %i[slugged finders]
  
  has_many :skus

  scope :not_deleted, -> { where(deleted_at: nil) }
  # ...
  
  def self.parse_friendly_id(value)
    super.downcase
  end
end

We expect the finder to work for the following cases

Product.find('FriendlyID') # => Finds product with #slug friendlyid
Product.includes(:skus).find('FriendlyID')
Product.not_deleted.includes(:skus).find('FriendlyID')

But instead, the first Product.find('FriendlyID') works without issue, yet the second and third statement did not call the correct #parse_friendly_id and returns exception: ActiveRecord::RecordNotFound: can't find record with friendly id: "FriendlyID"

Both Product.includes(:skus) and Product.not_deleted.includes(:skus) returns Product::ActiveRecord_Relation, so I am guessing that Product::ActiveRecord_Relation did not pick up #parse_friendly_id definition from Product model as expected

Strangely, if I called Product.not_deleted.includes(:skus).parse_friendly_id('FriendlyID'), it will return the expected result friendlyid, and if I called Product.not_deleted.includes(:skus).find('FriendlyID') followed by that, the query will return expected result.

We are using

  • FriendlyID: 5.4.2
  • Rails: 5.2.5
  • Ruby 2.7.0

I've also tried moving friendly id settings into initializer with no luck

FriendlyId.defaults do |config|
  config.use :reserved
  config.reserved_words = %w[ #... ]
  config.use :finders
  config.use :slugged
  config.use(Module.new {
    def parse_friendly_id(value)
      super.downcase
    end
  })
end

YushengLi avatar Apr 08 '21 14:04 YushengLi

We want to opt-in for case-insensitive slug and stumbled upon the same behavior. We use slugs without a scope, so our model Organization with

class Organization < ApplicationRecord
  extend FriendlyId

  friendly_id :name, use: :slugged

  def self.parse_friendly_id(value)
    super.downcase
  end
end

yields

> Organization.friendly.find('customer')
=> #<Organization ...>
> Organization.friendly.find('Customer')
ActiveRecord::RecordNotFound (can't find record with friendly id: "Customer")

and

> Organization.friendly.parse_friendly_id('Customer')
NoMethodError (super: no superclass method `parse_friendly_id' for #<Class:0x00007f8259b0c588>)

We run on FriendlyID: 5.4.2, Rails: 5.2.5, and Ruby 2.5.9.

tbolender avatar Jul 22 '21 13:07 tbolender

in addition to that, the example in the documentation has an instance method, not a class method:

# ### Example
#
#     class Person < ActiveRecord::Base
#       extend FriendlyId
#       friendly_id :name_and_location
#
#       def name_and_location
#         "#{name} from #{location}"
#       end
#
#       # Use default slug, but lower case
#       # If `id` is "Jane-Doe" or "JANE-DOE", this finds data by "jane-doe"
#       def parse_friendly_id(slug)
#         super.downcase
#       end
#     end

phoet avatar Mar 24 '23 11:03 phoet