Skip to content

Instantly share code, notes, and snippets.

@danilovelozo
Last active December 15, 2022 14:42
Show Gist options
  • Save danilovelozo/3e0571f1dc7e46912b863cf8b5e35c1b to your computer and use it in GitHub Desktop.
Save danilovelozo/3e0571f1dc7e46912b863cf8b5e35c1b to your computer and use it in GitHub Desktop.
Battling RecordNotUnique in Rails
class ApplicationRecord < ActiveRecord::Base
attr_writer :created_moments_ago
def created_moments_ago?
@created_moments_ago
end
def self.create_or_take!
where(block_given? && yield).crumby_create!
rescue ActiveRecord::RecordNotUnique
take!
end
def self.crumby_create!
instance = transaction(requires_new: true) { create! }
instance.created_moments_ago = true
instance
end
def self.take_or_create!(&block)
take || create_or_take!(&block)
end
end
# INSERT first. If INSERT fails, try to SELECT
Model.where(unique_attr: val)
.create_or_take! { creation_attrs_hash }
# SELECT first, then INSERT
# if INSERT fails, try to SELECT one more time
Model.where(unique_attr: val)
.take_or_create! { creation_attrs_hash }
model = Model.where(unique: val).take_or_create! { '...' }
model.created_moments_ago?
# Model.where(unique_col: val)
# .first_or_create { |new_record| ... }
# first_or_create
def example_1
begin
Model.where(...).first_or_create { '...' }
rescue ActiveRecord::RecordNotUnique => exception
# Retry first_or_create here.
# Blow up if still not found (broken code).
end
end
def example_2
begin
tries ||= 0
Model.where(...).first_or_create { '...' }
rescue ActiveRecord::RecordNotUnique => exception
retry if (tries += 1) == 1
raise exception
end
end
# create_or_find_by
Model.where(unique_attr: val).create_or_find_by do |new_record|
new_record.assign_attributes(initial_attributes)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment