Module: Ruby::Rego::Builtins::Numbers

Extended by:
RegistryHelpers
Defined in:
lib/ruby/rego/builtins/numbers.rb

Overview

Built-in numeric helpers (abs, round, ceil, floor, numbers.range, numbers.range_step).

Constant Summary collapse

NUMBER_FUNCTIONS =
{
  "abs" => { arity: 1, handler: :abs },
  "round" => { arity: 1, handler: :round },
  "ceil" => { arity: 1, handler: :ceil },
  "floor" => { arity: 1, handler: :floor },
  "numbers.range" => { arity: 2, handler: :range },
  "numbers.range_step" => { arity: 3, handler: :range_step }
}.freeze
MAX_RANGE_SIZE =

Upper bound on numbers.range length. OPA halts large ranges via context cancellation; this pure-Ruby evaluator has no such escape valve, so an untrusted policy could otherwise force unbounded allocation. Exceeding the limit yields an undefined result rather than exhausting memory.

1_000_000

Class Method Summary collapse

Methods included from RegistryHelpers

register_configured_functions

Class Method Details

.abs(value) ⇒ Ruby::Rego::NumberValue

Parameters:

Returns:



42
43
44
# File 'lib/ruby/rego/builtins/numbers.rb', line 42

def self.abs(value)
  NumberValue.new(normalize(numeric(value, "abs").abs))
end

.ceil(value) ⇒ Ruby::Rego::NumberValue

Parameters:

Returns:



54
55
56
# File 'lib/ruby/rego/builtins/numbers.rb', line 54

def self.ceil(value)
  NumberValue.new(numeric(value, "ceil").ceil)
end

.floor(value) ⇒ Ruby::Rego::NumberValue

Parameters:

Returns:



60
61
62
# File 'lib/ruby/rego/builtins/numbers.rb', line 60

def self.floor(value)
  NumberValue.new(numeric(value, "floor").floor)
end

.range(start_value, end_value) ⇒ Ruby::Rego::ArrayValue

Inclusive integer range, ascending or descending.

Matches OPA: integer-valued floats (e.g. 3.0) are accepted as bounds, while a non-integer bound (e.g. 1.5) yields an undefined result.

Parameters:

Returns:



72
73
74
75
76
77
78
# File 'lib/ruby/rego/builtins/numbers.rb', line 72

def self.range(start_value, end_value)
  start_bound = NumericHelpers.integer_value(start_value, context: "numbers.range")
  end_bound = NumericHelpers.integer_value(end_value, context: "numbers.range")
  ensure_size_within_limit((end_bound - start_bound).abs + 1, "numbers.range")

  ArrayValue.new(range_elements(start_bound, end_bound).map { |number| NumberValue.new(number) })
end

.range_step(start_value, end_value, step_value) ⇒ Ruby::Rego::ArrayValue

numbers.range_step(low, high, step): like numbers.range but advancing by a positive integer step; the endpoint is included only when it lands exactly on a step. A non-positive or non-integer step yields undefined (matching OPA).

:reek:TooManyStatements

Parameters:

Returns:



89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ruby/rego/builtins/numbers.rb', line 89

def self.range_step(start_value, end_value, step_value)
  start_bound = NumericHelpers.integer_value(start_value, context: "numbers.range_step")
  end_bound = NumericHelpers.integer_value(end_value, context: "numbers.range_step")
  step = NumericHelpers.integer_value(step_value, context: "numbers.range_step")
  # ensure_positive_step must precede step_count, which divides by step.
  ensure_positive_step(step)
  ensure_size_within_limit(step_count(start_bound, end_bound, step), "numbers.range_step")

  elements = step_elements(start_bound, end_bound, step)
  ArrayValue.new(elements.map { |number| NumberValue.new(number) })
end

.register!Ruby::Rego::Builtins::BuiltinRegistry



32
33
34
35
36
# File 'lib/ruby/rego/builtins/numbers.rb', line 32

def self.register!
  registry = BuiltinRegistry.instance
  register_configured_functions(registry, NUMBER_FUNCTIONS)
  registry
end

.round(value) ⇒ Ruby::Rego::NumberValue

Parameters:

Returns:



48
49
50
# File 'lib/ruby/rego/builtins/numbers.rb', line 48

def self.round(value)
  NumberValue.new(numeric(value, "round").round)
end