module Identifiable::Model

Public Instance Methods

_identifiable_validate_column_cannot_be_id() click to toggle source

The column parameter cannot be :id, since that's reserved for the primary key, so raise an error if the column parameter is :id.

# File lib/identifiable/model.rb, line 48
def _identifiable_validate_column_cannot_be_id
  return unless @identifiable_column == :id

  raise Identifiable::Errors::ColumnCannotBeIdError
end
_identifiable_validate_column_must_be_a_symbol() click to toggle source

We only accept symbols for the column parameter, so we need to raise an error if anything other than a symbol is passed in.

# File lib/identifiable/model.rb, line 40
def _identifiable_validate_column_must_be_a_symbol
  return if @identifiable_column.is_a? Symbol

  raise Identifiable::Errors::ColumnMustBeASymbolError, @identifiable_column
end
_identifiable_validate_column_must_be_in_the_table() click to toggle source

The column parameter must be in the model's table, so check that the column corresponds to a column in the model's table, and raise an error if it is not.

# File lib/identifiable/model.rb, line 57
def _identifiable_validate_column_must_be_in_the_table
  return if column_names.include? @identifiable_column.to_s

  raise Identifiable::Errors::ColumnMustExistInTheTableError.new(@identifiable_column, valid_columns: column_names)
end
_identifiable_validate_length_must_be_an_integer() click to toggle source

The length parameter must be an Integer, so if it's something else then raise an error.

# File lib/identifiable/model.rb, line 73
def _identifiable_validate_length_must_be_an_integer
  return if @identifiable_length.is_a? Integer

  raise Identifiable::Errors::LengthMustBeAnIntegerError
end
_identifiable_validate_length_must_be_in_a_valid_range() click to toggle source

The length parameter can only be within a certain range to prevent public IDs that are too short or too long, so raise an error if the length is too short or too long.

# File lib/identifiable/model.rb, line 82
def _identifiable_validate_length_must_be_in_a_valid_range
  raise Identifiable::Errors::LengthIsTooShortError if @identifiable_length < 4
  raise Identifiable::Errors::LengthIsTooLongError if @identifiable_length > 128
end
_identifiable_validate_style_must_be_a_valid_style() click to toggle source

We can only use valid styles, so check that the style parameter is a valid style, and raise an error if it is not.

# File lib/identifiable/model.rb, line 65
def _identifiable_validate_style_must_be_a_valid_style
  return if Identifiable::Stylist::VALID_STYLES.include? @identifiable_style

  raise Identifiable::Errors::StyleMustBeAValidStyleError, @identifiable_style
end
find_by_public_id(public_id) click to toggle source
# File lib/identifiable/model.rb, line 27
def find_by_public_id(public_id)
  where(Hash[identifiable_column, public_id]).first
end
find_by_public_id!(public_id) click to toggle source
# File lib/identifiable/model.rb, line 31
def find_by_public_id!(public_id)
  result = find_by_public_id(public_id)
  raise ActiveRecord::RecordNotFound unless result.present?

  result
end
identifiable(column: :public_id, style: :numeric, length: 8) click to toggle source
# File lib/identifiable/model.rb, line 12
def identifiable(column: :public_id, style: :numeric, length: 8)
  @identifiable_column = column
  @identifiable_style = style
  @identifiable_length = length

  _identifiable_validate_column_must_be_a_symbol
  _identifiable_validate_column_cannot_be_id
  _identifiable_validate_column_must_be_in_the_table
  _identifiable_validate_style_must_be_a_valid_style
  _identifiable_validate_length_must_be_an_integer
  _identifiable_validate_length_must_be_in_a_valid_range

  before_create :set_public_id!
end
set_public_id!() click to toggle source

If we don't have a public ID yet, this method fetches the stylist for this class and finds a new, valid public id, and assigns it to the public ID column.

# File lib/identifiable/model.rb, line 91
def set_public_id!
  # We don't want to set the public id if there's already a value in the
  # column, so exit early if that's the case.
  return unless self[self.class.identifiable_column].blank?

  # Create a new stylist for this record, that'll return random IDs
  # matching the class' style and length options.
  stylist = Identifiable::Stylist.new(record: self)
  new_public_id = nil

  # Loop until we find an unused public ID or we run out of attempts.
  100.times do
    new_public_id = stylist.random_id
    break if self.class.find_by_public_id(new_public_id).nil?

    new_public_id = nil
  end

  # If we ran out of attempts, this probably means the length is too short,
  # since we kept colliding with existing records. Raise an error to let
  # the developers know that they need to up the length of the public ID.
  raise Identifiable::Errors::RanOutOfAttemptsToSetPublicIdError if new_public_id.nil?

  # If we got this far, we've got a new valid public ID, time to set it!
  self[self.class.identifiable_column] = new_public_id
end