module RailsStuff::RandomUniqAttr

Provides save way to generate uniq random values for ActiveRecord models. You need to make field nullable and add unique index on it. The way it works:

Constants

DEFAULT_GENERATOR
MAX_ATTEMPTS

Public Class Methods

friendly_token(length = 32) click to toggle source

Made from `Devise.firendly_token` with increased length.

# File lib/rails_stuff/random_uniq_attr.rb, line 17
def friendly_token(length = 32)
  SecureRandom.urlsafe_base64(length).tr('lIO0', 'sxyz')
end

Public Instance Methods

random_uniq_attr(field, **options, &block) click to toggle source

Generates necessary methods and setups on-create callback for the `field`. You can optionally pass custom generator function:

random_uniq_attr(:code) { |instance| my_random(instance) }
# File lib/rails_stuff/random_uniq_attr.rb, line 27
def random_uniq_attr(field, **options, &block)
  set_method = :"set_#{field}"
  generate_method = :"generate_#{field}"
  max_attempts = options.fetch(:max_attempts) { MAX_ATTEMPTS }

  after_create set_method, unless: :"#{field}?"

  # def self.generate_key
  define_singleton_method generate_method, &(block || DEFAULT_GENERATOR)

  # def set_key
  define_method(set_method) do
    attempt = 0
    begin
      raise 'Available only for persisted record' unless persisted?
      transaction(requires_new: true) do
        new_value = self.class.send(generate_method, self)
        update_column field, new_value # rubocop:disable Rails/SkipsModelValidations
      end
    rescue ActiveRecord::RecordNotUnique
      attempt += 1
      raise if attempt > max_attempts
      retry
    end
  end
end