Class: Labkit::RateLimit::Identifier

Inherits:
Object
  • Object
show all
Defined in:
lib/labkit/rate_limit/identifier.rb

Overview

Identifier is a value object wrapping a hash of key-value pairs that describe the caller (e.g. user, ip, endpoint). Endpoint values are normalised at construction time (query string stripped).

Constant Summary collapse

InvalidKeyError =
Class.new(ArgumentError)
DuplicateNormalizedKeyError =
Class.new(InvalidKeyError)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}) ⇒ Identifier

Returns a new instance of Identifier.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/labkit/rate_limit/identifier.rb', line 21

def initialize(attributes = {})
  normalised = {}
  original_keys = {}

  attributes.each do |key, value|
    unless key.respond_to?(:to_sym)
      # Reject keys such as nil, 42, and [] instead of leaking NoMethodError;
      # identifiers must have a canonical symbol key for matching and serialization.
      raise InvalidKeyError, "Identifier key #{key.inspect} must respond to #to_sym"
    end

    normalised_key = key.to_sym
    if normalised.key?(normalised_key)
      # Reject { user: 1, "user" => 2 } (and its reverse order): silently
      # choosing a value would make the rate-limit bucket insertion-order dependent.
      raise DuplicateNormalizedKeyError,
        "Identifier keys normalize to the same key #{normalised_key.inspect}: " \
          "#{original_keys[normalised_key].inspect} and #{key.inspect}"
    end

    original_keys[normalised_key] = key
    normalised[normalised_key] = value
  end

  normalised[:endpoint] = self.class.normalize_endpoint(normalised[:endpoint]) if normalised.key?(:endpoint)
  @attributes = normalised.freeze
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



19
20
21
# File 'lib/labkit/rate_limit/identifier.rb', line 19

def attributes
  @attributes
end

Class Method Details

.normalize_endpoint(value) ⇒ Object

Normalize an endpoint value: strip query string.



13
14
15
16
17
# File 'lib/labkit/rate_limit/identifier.rb', line 13

def self.normalize_endpoint(value)
  return value unless value.is_a?(String)

  value.split("?", 2).first
end

Instance Method Details

#==(other) ⇒ Object



59
60
61
# File 'lib/labkit/rate_limit/identifier.rb', line 59

def ==(other)
  other.is_a?(Identifier) && other.attributes == @attributes
end

#[](key) ⇒ Object

Return the value for a characteristic key.



50
51
52
# File 'lib/labkit/rate_limit/identifier.rb', line 50

def [](key)
  @attributes[key.to_sym]
end

#to_hObject

Serialize to a plain Hash suitable for JSON logging.



55
56
57
# File 'lib/labkit/rate_limit/identifier.rb', line 55

def to_h
  @attributes.transform_keys(&:to_s)
end