Module: Spree::PrefixedId

Extended by:
ActiveSupport::Concern
Included in:
AdminUserMethods, Base, UserMethods
Defined in:
app/models/concerns/spree/prefixed_id.rb

Overview

Adds Stripe-style prefixed IDs to Spree models using Sqids encoding. IDs are computed on the fly from integer primary keys – no database column needed.

e.g., Product with id=12345 -> “prod_86Rf07xd4z”

class Product < Spree.base_class
  has_prefix_id :prod
end

Constant Summary collapse

SQIDS =
Sqids.new(min_length: 10)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.decode_prefixed_id(prefixed_id_string) ⇒ Object



73
74
75
76
77
78
79
80
81
82
# File 'app/models/concerns/spree/prefixed_id.rb', line 73

def self.decode_prefixed_id(prefixed_id_string)
  return nil if prefixed_id_string.blank?

  parts = prefixed_id_string.to_s.split('_', 2)
  return nil if parts.length != 2

  _prefix, encoded = parts
  ids = SQIDS.decode(encoded)
  ids.first
end

.prefixed_id?(value) ⇒ Boolean

Module-level methods for use without a model context (e.g., from ParamsNormalizer)

Returns:

  • (Boolean)


69
70
71
# File 'app/models/concerns/spree/prefixed_id.rb', line 69

def self.prefixed_id?(value)
  value.is_a?(String) && value.match?(/\A[a-z]+_[a-zA-Z0-9]+\z/)
end

Instance Method Details

#assign_attributes(new_attributes) ⇒ Object

Automatically resolve prefixed ID strings for belongs_to foreign keys. e.g., product.assign_attributes(tax_category_id: “tc_86Rf07xd4z”) will decode the prefixed ID to the integer primary key.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'app/models/concerns/spree/prefixed_id.rb', line 26

def assign_attributes(new_attributes)
  return super if new_attributes.blank?

  attrs = new_attributes.to_h
  needs_resolution = attrs.any? do |key, value|
    key_s = key.to_s
    (value.is_a?(String) && key_s.end_with?('_id') && Spree::PrefixedId.prefixed_id?(value)) ||
      (value.is_a?(Array) && key_s.end_with?('_ids') && value.any? { |v| Spree::PrefixedId.prefixed_id?(v) })
  end

  return super unless needs_resolution

  resolved = attrs.each_with_object({}.with_indifferent_access) do |(key, value), hash|
    key_s = key.to_s
    if value.is_a?(String) && key_s.end_with?('_id') && Spree::PrefixedId.prefixed_id?(value)
      hash[key] = self.class.resolve_prefixed_id_for_attribute(key_s, value)
    elsif value.is_a?(Array) && key_s.end_with?('_ids')
      hash[key] = self.class.resolve_prefixed_ids_for_attribute(key_s, value)
    else
      hash[key] = value
    end
  end

  super(resolved)
end

#prefixed_idObject

Returns the Stripe-style prefixed ID, or nil for unsaved records.



53
54
55
56
57
# File 'app/models/concerns/spree/prefixed_id.rb', line 53

def prefixed_id
  return nil unless id.present?

  "#{self.class._prefix_id_prefix}_#{Spree::PrefixedId::SQIDS.encode([id])}"
end

#to_paramObject

Use prefixed_id for URL params when available. Skip if FriendlyId is used (it has its own to_param using slug).



61
62
63
64
65
66
# File 'app/models/concerns/spree/prefixed_id.rb', line 61

def to_param
  return super if self.class.respond_to?(:friendly_id_config)
  return super unless self.class._prefix_id_prefix.present?

  prefixed_id.presence || super
end