Module: RailsStuff::RandomUniqAttr

Defined in:
lib/rails_stuff/random_uniq_attr.rb

Overview

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:

  • Instance is saved as usual

  • If random fields are not empty, it does nothing

  • Generates random value and tries to update instance

  • If ‘RecordNotUnique` is occured, it keeps trying to generate new values.

Constant Summary collapse

DEFAULT_GENERATOR =
->(*) { SecureRandom.hex(32) }
MAX_ATTEMPTS =
10

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.friendly_token(length = 32) ⇒ Object

Made from ‘Devise.firendly_token` with increased length.



17
18
19
# File 'lib/rails_stuff/random_uniq_attr.rb', line 17

def friendly_token(length = 32)
  SecureRandom.urlsafe_base64(length).tr('lIO0', 'sxyz')
end

Instance Method Details

#random_uniq_attr(field, **options, &block) ⇒ Object

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) }


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 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