I am very new to friendly_id
and it matches my need to provide friendly URLs 👍
I have a Group
model (i.e. a group of users) for which I generate a unique code
upon creation. FYI, this code
attribute is set as a unique index in my PostgreSQL database.
class Group < ApplicationRecord
include FriendlyId
friendly_id :code
after_create :generate_group_code
private
def normalize_friendly_id(value)
super.upcase
end
def generate_group_code(size = 8)
allowed_chars = ("a".."z").to_a
code = (1..size).map { allowed_chars[rand(26)] }.join while Group.exists?(code: code)
update(code: code)
end
end
I think I have followed the gem's guide properly, I just want the generated code
to be upcased in the URLs (i.e. /group/ABCDEFGH
).
The friendly_id is indeed set as my code
attribute, but it is not upcased. I placed a byebug
in the normalize_friendly_id
method but it is never triggered. What am I missing?
Sunny's way is probably the way to go in general, as the slugged
module is required to edit internal methods such as normalize_friendly_id
.
In my case, I already have a code
attribute that is unique. Using the slugged
module would create a new attribute called slug
, which would be exactly the same as my code
. I want to avoid that duplication.
In the end, I decided to dodge the friendly_id
gem and directly override the to_param
method the my model (inspired by this gist):
class Group < ApplicationRecord
validates :code, format: { with: /\A[a-z]{8}\z/ }, uniqueness: true, on: :update
after_create :generate_group_code
# Override the method to allow '/group/MYGROUPA' paths
def to_param
code.upcase
end
# A group code is made of letters exclusively.
# Converting a String (or nil) to an integer leads to 0.
# In this case, lookup by code, otherwise, lookup by id.
# Note the 'downcase', which allows URLs to be case insensitive.
def self.find(input)
input.to_i == 0 ? find_by_code!(input.downcase) : super
end
private
def generate_group_code(size = 8)
allowed_chars = ("a".."z").to_a
code = (1..size).map { allowed_chars[rand(26)] }.join while Group.exists?(code: code)
update(code: code)
end
end
I'll edit this answer if I encounter any side effect, but for now it works.