Class: Labkit::RateLimit::Result

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

Overview

Result is the return value of Limiter#check.

It accumulates one Result::Evaluation per matched-and-counted rule; the reader methods report the single most-constraining evaluation (see #most_constraining). A matched :skip rule or a fail-open error replaces the evaluations as the source of the reported outcome.

Result is a mutable accumulator, not a value object: two Results built from the same evaluations are not #==.

matched? - true if at least one rule's match conditions were satisfied exceeded? - true if the reported evaluation's counter exceeded its limit action - the outcome: what the caller should do :block = a :limit rule matched and is over its limit :allow = everything else, including an exceeded :log rule (visible via exceeded?), a matched :skip rule, no rule matched, and error (fail-open). The rule's configured action is available via rule.action. rule - the reported Rule: the :skip rule when one matched, otherwise the most-constraining evaluated Rule (nil when matched? is false). Other rules may also have matched and been counted; see #evaluations. error? - true if Redis was unavailable; result fails open (exceeded? is false) info - Result::Info with per-window counters for the reported rule; nil when matched? is false, error?, or the matched rule is :skip (no counter exists) evaluations - every counted evaluation, in rule declaration order

Defined Under Namespace

Classes: Evaluation, Info

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(error: false) ⇒ Result

Returns a new instance of Result.



39
40
41
42
43
44
# File 'lib/labkit/rate_limit/result.rb', line 39

def initialize(error: false)
  @evaluations = []
  @skip_rule = nil
  @error = error
  @most_constraining = nil
end

Instance Attribute Details

#evaluationsObject (readonly)

Returns the value of attribute evaluations.



33
34
35
# File 'lib/labkit/rate_limit/result.rb', line 33

def evaluations
  @evaluations
end

Class Method Details

.errorObject



35
36
37
# File 'lib/labkit/rate_limit/result.rb', line 35

def self.error
  new(error: true)
end

Instance Method Details

#actionObject



81
82
83
# File 'lib/labkit/rate_limit/result.rb', line 81

def action
  block? ? :block : :allow
end

#add_evaluation(evaluation) ⇒ Object



46
47
48
49
50
# File 'lib/labkit/rate_limit/result.rb', line 46

def add_evaluation(evaluation)
  @most_constraining = nil
  @evaluations << evaluation
  self
end

#block?Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/labkit/rate_limit/result.rb', line 67

def block?
  !skipped? && @evaluations.any?(&:block?)
end

#error?Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/labkit/rate_limit/result.rb', line 77

def error?
  @error
end

#exceeded?Boolean

Returns:

  • (Boolean)


71
72
73
74
75
# File 'lib/labkit/rate_limit/result.rb', line 71

def exceeded?
  return false if skipped?

  !most_constraining.nil? && most_constraining.exceeded?
end

#infoObject



89
90
91
92
93
# File 'lib/labkit/rate_limit/result.rb', line 89

def info
  return nil if skipped?

  most_constraining&.info
end

#matched?Boolean

Returns:

  • (Boolean)


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

def matched?
  skipped? || @evaluations.any?
end

#most_constrainingObject

The evaluation with the strongest claim on the outcome; ties keep the earliest-declared rule (min returns the first of tied elements). Memoized; add_evaluation invalidates.



98
99
100
# File 'lib/labkit/rate_limit/result.rb', line 98

def most_constraining
  @most_constraining ||= @evaluations.min
end

#ruleObject



85
86
87
# File 'lib/labkit/rate_limit/result.rb', line 85

def rule
  @skip_rule || most_constraining&.rule
end

#skip!(rule) ⇒ Object

Records a matched :skip rule: the request is allowed and the skip rule is the one reported, regardless of any evaluations already collected.



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

def skip!(rule)
  @skip_rule = rule
  self
end

#skipped?Boolean

Returns:

  • (Boolean)


63
64
65
# File 'lib/labkit/rate_limit/result.rb', line 63

def skipped?
  !!@skip_rule
end

#to_response_headersObject

Returns RFC-compliant rate limit response headers, or {} when no rule matched or an error occurred. Keys: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset (Unix timestamp). remaining is coerced to Integer for header output even when info.remaining is fractional; the RateLimit header spec requires integer values.



106
107
108
109
110
111
112
113
114
# File 'lib/labkit/rate_limit/result.rb', line 106

def to_response_headers
  return {} unless matched? && !error? && info

  {
    "RateLimit-Limit" => info.resolved_limit.to_i.to_s,
    "RateLimit-Remaining" => info.remaining.to_i.to_s,
    "RateLimit-Reset" => info.reset_at.to_i.to_s
  }
end