Hi,
Imagine having a code
field with a validation rule of this:
validates :code,
presence: true,
length: { maximum: 19 },
format: { with: /\A[A-Z0-9\-]+\z/i, message: "must be a-z|A-Z|0-9|-" },
uniqueness: true
Basically codes like JX26-BYWY-AFRE
are valid.
Now imagine populating this code
with:
before_validation { self.code = Generate.random_code }, on: :create
So far so good, the workflow is when you create a new item, its code
property gets generated and then validated. It will likely be valid because your app code controls how it’s generated.
There is a very small chance the generated code will not be unique through chance and then a validation error for uniqueness on the code will fail as expected.
I very much want to avoid the form submission from failing and displaying a flash message because the field isn’t editable as an end user when the resource is created. Also it’s during an important workflow where having a user have to re-submit the form wouldn’t be an ideal user experience.
What I’m trying to do is come up with a workflow like:
- Create item
- Have its code automatically generated and validated
- If validation fails because the code is not unique generate a new code to replace the bad one
- Loop until the code is unique and eventually let the record get saved
I’d like to avoid re-running .save
in my controller in a loop or rescuing from errors.
I was thinking maybe on after_validation
I could check to see if item.errors
includes a specific uniqueness error on the code and if so then generate a new code and this could be an until loop until the code is valid.
Is there a more efficient way?
I do have a uniqueness constraint at the DB level with Postgres so that’s available to catch but the app level Rails validation will prevent this from happening I think.
I suppose alternatively I could not validate the code at all on create and since the code is generated by app code I can trust it’s valid and then if a unique constraint error is thrown re-generate a new code and try to save it again in a loop until it works? This sounds like I’d have to rescue from errors in my controller tho.
I feel like this is a pretty common problem so I was wondering if there’s a “Rails way” I haven’t come across.