lib/prefixed_ids.rb in prefixed_ids-1.0.1 vs lib/prefixed_ids.rb in prefixed_ids-1.2.0

- old
+ new

@@ -1,29 +1,89 @@ require "prefixed_ids/version" require "prefixed_ids/engine" +require "hashids" + module PrefixedIds - MINIMUM_TOKEN_LENGTH = 24 + class Error < StandardError; end - class MinimumLengthError < StandardError; end + autoload :PrefixId, "prefixed_ids/prefix_id" + TOKEN = 123 + DELIMITER = "_" + + mattr_accessor :alphabet, default: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" + mattr_accessor :minimum_length, default: 24 + + mattr_accessor :models, default: {} + + def self.find(prefix_id) + prefix, _ = split_id(prefix_id) + models.fetch(prefix).find_by_prefix_id(prefix_id) + rescue KeyError + raise Error, "Unable to find model with prefix `#{prefix}`. Available prefixes are: #{models.keys.join(", ")}" + end + + # Splits a prefixed ID into its prefix and ID + def self.split_id(prefix_id) + prefix, _, id = prefix_id.to_s.rpartition(DELIMITER) + [prefix, id] + end + + # Adds `has_prefix_id` method + module Rails + extend ActiveSupport::Concern + + included do + class_attribute :_prefix_id + end + + class_methods do + def has_prefix_id(prefix, override_find: true, override_param: true, **options) + include Attribute + include Finder if override_find + include ToParam if override_param + self._prefix_id = PrefixId.new(self, prefix, **options) + + # Register with PrefixedIds to support PrefixedIds#find + PrefixedIds.models[prefix.to_s] = self + end + end + end + + # Included when a module uses `has_prefix_id` module Attribute extend ActiveSupport::Concern - module ClassMethods - def has_prefix_id(prefix, attribute: :prefix_id, length: MINIMUM_TOKEN_LENGTH) - if length < MINIMUM_TOKEN_LENGTH - raise MinimumLengthError, "Token requires a minimum length of #{MINIMUM_TOKEN_LENGTH} characters." - end + class_methods do + def find_by_prefix_id(id) + find_by(id: _prefix_id.decode(id)) + end - # Load securerandom only when has_secure_token is used. - require "active_support/core_ext/securerandom" - define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_prefix_id(prefix, length: length) } - before_create { send("#{attribute}=", self.class.generate_unique_prefix_id(prefix, length: length)) unless send("#{attribute}?") } + def find_by_prefix_id!(id) + find_by!(id: _prefix_id.decode(id)) end + end - def generate_unique_prefix_id(prefix, length: MINIMUM_TOKEN_LENGTH) - prefix.to_s + "_" + SecureRandom.base58(length) + def prefix_id + self.class._prefix_id.encode(id) + end + end + + module Finder + extend ActiveSupport::Concern + + class_methods do + def find(*ids) + super(*ids.map { |id| _prefix_id.decode(id) }) end + end + end + + module ToParam + extend ActiveSupport::Concern + + def to_param + _prefix_id.encode(id) end end end