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) degraded? - true if a matched count_distinct rule was skipped by the missing-key fail-open path; the check still completed 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.



41
42
43
44
45
46
47
# File 'lib/labkit/rate_limit/result.rb', line 41

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

Instance Attribute Details

#evaluationsObject (readonly)

Returns the value of attribute evaluations.



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

def evaluations
  @evaluations
end

Class Method Details

.errorObject



37
38
39
# File 'lib/labkit/rate_limit/result.rb', line 37

def self.error
  new(error: true)
end

Instance Method Details

#actionObject



95
96
97
# File 'lib/labkit/rate_limit/result.rb', line 95

def action
  block? ? :block : :allow
end

#add_evaluation(evaluation) ⇒ Object



49
50
51
52
53
# File 'lib/labkit/rate_limit/result.rb', line 49

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

#block?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/labkit/rate_limit/result.rb', line 70

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

#degraded!Object

Unlike error?, the check keeps going: the flag marks the outcome as degraded without changing it.



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

def degraded!
  @degraded = true
  self
end

#degraded?Boolean

Returns:

  • (Boolean)


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

def degraded?
  @degraded
end

#error?Boolean

Returns:

  • (Boolean)


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

def error?
  @error
end

#exceeded?Boolean

Returns:

  • (Boolean)


74
75
76
77
78
# File 'lib/labkit/rate_limit/result.rb', line 74

def exceeded?
  return false if skipped?

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

#infoObject



103
104
105
106
107
# File 'lib/labkit/rate_limit/result.rb', line 103

def info
  return nil if skipped?

  most_constraining&.info
end

#matched?Boolean

Returns:

  • (Boolean)


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

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.



112
113
114
# File 'lib/labkit/rate_limit/result.rb', line 112

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

#ruleObject



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

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.



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

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

#skipped?Boolean

Returns:

  • (Boolean)


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

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.



120
121
122
123
124
125
126
127
128
# File 'lib/labkit/rate_limit/result.rb', line 120

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