<commit_before>module HasUniqueIdentifier
  module InstanceMethods
    DEFAULT_RANDOM_IDENTIFIER_LENGTH = 24

    extend ActiveSupport::Concern

    def to_param
      if self.class.unique_identifier &&
        self.class.unique_identifier.options[:to_param] == true

        return send(self.class.unique_identifier.name)
      end
      super
    end

    included do
      before_create :ensure_unique_identifier

      private

      def ensure_unique_identifier
        return unless self.class.unique_identifier
        return if self[self.class.unique_identifier.name].present?

        length = self.class.unique_identifier.options[:length] ||
          DEFAULT_RANDOM_IDENTIFIER_LENGTH

        send("#{self.class.unique_identifier.name}=",
          loop do
            hash =
              if self.class.unique_identifier.options[:only_numbers]
                number_size = 10 ** length
                SecureRandom.random_number(9 * number_size) + number_size
              else
                SecureRandom.hex(length)
              end
            break hash unless self.class.exists?(self.class.unique_identifier.name => hash)
          end
        )
      end
    end
  end
end<commit_msg>Add safe_chars option<commit_after>module HasUniqueIdentifier
  module InstanceMethods
    DEFAULT_RANDOM_IDENTIFIER_LENGTH = 24
    DEFAULT_SAFE_CHARS = (
      ('A'..'Z').to_a +
      ('a'..'z').to_a +
      ('0'..'9').to_a +
      %w(- _ ~)
    ).freeze

    extend ActiveSupport::Concern

    def to_param
      if self.class.unique_identifier &&
        self.class.unique_identifier.options[:to_param] == true

        return send(self.class.unique_identifier.name)
      end
      super
    end

    included do
      before_create :ensure_unique_identifier

      private

      def ensure_unique_identifier
        return unless self.class.unique_identifier
        return if self[self.class.unique_identifier.name].present?

        length = self.class.unique_identifier.options[:length] ||
          DEFAULT_RANDOM_IDENTIFIER_LENGTH

        unless self.class.unique_identifier.options[:force]
          return if self[self.class.unique_identifier.name].present?
        end
        send("#{self.class.unique_identifier.name}=",
          loop do
            chars =
              if self.class.unique_identifier.options[:safe_chars]
                self.class.unique_identifier.options[:safe_chars]
              elsif self.class.unique_identifier.options[:only_numbers]
                (0..9).to_a
              elsif self.class.unique_identifier.options[:only_letters]
                ('a'..'z').to_a + ('A'..'Z').to_a
              elsif self.class.unique_identifier.options[:no_symbols]
                ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a
              else
                DEFAULT_SAFE_CHARS.dup
              end
            chars.shuffle!

            hash = Array.new(length) { chars.sample }.join
            unless self.class.exists?(self.class.unique_identifier.name => hash.to_s)
              break hash
            end
          end
        )
      end
    end
  end
end
{'fraction_non_alphanumeric': 0.09315772566655958, 'fraction_numerical': 0.004176035978156119, 'mean_word_length': 2.0262390670553936, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3008517', 'n_tokens_mistral': 900, 'n_tokens_neox': 870, 'n_words': 146}