0

I hope the title is not too unclear.

I am making arails app and I have a question about rails validation. Consider this code in the User,rb model file:

validates :name,
          presence: true,
          length: { maximum: 50 },
          uniqueness: { case_sensitive: false }

I am using the friendly_id gem to generate slugs for the users. I wont allow users to change their name. What I now need is to ensure that Names are unique in such a way that there will be no UUID's appended to slugs if two people have the same name converted in ascii approximation.

Current behaviour is:

User 1 signs up with a name and gets a slug like this:

name: "Jaiel" slug: "jaiel"

User 2 now does the same name but a bit different:

name: "Jàìèl" slug: "jaiel-6558c3f1-e6a1-4199-a53e-4ccc565657d4"

The problem here as you see I want such a uniqueness validation that User 2 would have been rejected because both names would generate the slug "jaiel" for their friendly_id's

I would appreciate your help on that matter

Thanks

Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
Ilja KO
  • 1,272
  • 12
  • 26

1 Answers1

0

Take a look into ActiveSupport::Inflector.transliterate:

ActiveSupport::Inflector.transliterate('Ærøskøbing')
#=> "AEroskobing"

Also, with this type of validation you might want to go with custom validation (alongside the one you already have):

class User
  validate :unique_slug

  private

  def unique_slug
    names = self.class.all.map(&:asci_name)

    raise ActiveRecord::RecordInvalid.new(self) if names.include?(asci_name)
  end

  def asci_name
    ActiveSupport::Inflector.transliterate(name)
  end
end

Obviously, this is super inefficient to query whole table on each validation, but this is just to point you into one of the possible directions.

Another option would be going for a callback. Transliterating the name upon creation:

before_validation: :transliterate_name

def transliterate_name
  self.name = ActiveSupport::Inflector.transliterate(name)
end

It will first transliterate the name, then validate uniqueness of already transliterated name with the validation you have. Looks like a solution, definitely not as heavy as initial one.

Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
  • all right, thanks but I want it efficient. So I will jsut restrict my users to only choose ascii characters as name with :format validation. But thanks for the effort – Ilja KO Aug 28 '18 at 21:53
  • Another option would be going for (again, not beloved) callback. Like asci-ing the name upon creation. Like `before_validation: :transliterate_name` (it will validate uniqueness with the validation which you already have and not allow non-uniq-already-asci names. – Andrey Deineko Aug 28 '18 at 21:57
  • but wouldnt it alter the name anayways to ascii so that the user would type JÀÌÈl but in the end it will turn out in the DB as JAIEl anyway(or jaiel I dont know) – Ilja KO Aug 28 '18 at 22:01
  • it would, this way you don't restrict user to type anything, you just handle the name encoding yourself. It looked like you were going to restrict users to asci characters anyway – Andrey Deineko Aug 28 '18 at 22:02
  • mmmh both have their negatives. But I want a wysiwyg approach so that the user isnt surprised in the end to see his name being ascii-nized allthough he typed it in unicode chars – Ilja KO Aug 28 '18 at 22:04
  • or maybe I could save the name first in a seperate variable and after_validation I could change the ascii name back to be saved in the DB? – Ilja KO Aug 28 '18 at 22:05
  • There's nothing impossible in programming :) Just decide on your priorities and follow them. I'm going to sleep now, if you have more specific questions - i'll check them tomorrow – Andrey Deineko Aug 28 '18 at 22:07
  • Just let user know (infobox on the form) "hey, enter asci letters. If not your name will be converted to asci anyway". Done. User not surprised ;) – Andrey Deineko Aug 28 '18 at 22:08
  • I wonder if you could use friendly_id's `.normalize_friendly_id(string)` method, instead of `.transliterate()`, the get exactly the same transformations – John Skiles Skinner Aug 28 '18 at 22:17
  • I did it now with `before_validate` and `after_validate` with a class variable `@@tempname` to hold the original name while validating. It works like a charm now and the user gets a WYSIWYG experience and unique slugs without stupid UUIDs at the end of their urls. Thanks for your input – Ilja KO Aug 28 '18 at 22:39
  • n/p, glad to help – Andrey Deineko Aug 29 '18 at 05:29