This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the rails category.
Last Updated: 2024-11-21
My User
model has both mandatory email
and store
attributes (in the sense
that neither could be null). Therefore any time I created a user, I had to
make sure both attributes were included:
User.where(
email: ...,
store: ...
).first_or_create!
One day an Australian customer shopped in my Canadian store and called an
endpoint containing this code. Even though a user with their email existed, my
code couldn't find a matching {email, store}
tuple (since it had
{email@example.com,australia}
but not {email@example.com,canada}
), therefore
it tried to create a new user, but failed on the email uniqueness constraint at
the DB level.
I had assumed a user stays in one store forever, which, while mostly true, wasn't true in this case.
More deeply, I should have realized that, even though both attributes were
necessary, only one of them uniquely identifies the row in question.
Therefore I should have refactored to perform the finder logic based on the
database uniqueness constraint, only adding the mandatory store
attribute when
actually creating a record (if necessary)
user = User.find_by_email(params[:email]) || User.create!(
email: params[:email],
store: current_store
)
Do not use first_or_create
with any attributes that might take on multiple
values for a given entity. Instead limit it to rows that will always be unique
for each record.