Class: Ruby::Rego::Environment

Inherits:
Object
  • Object
show all
Includes:
EnvironmentOverrides, EnvironmentReferenceResolution
Defined in:
lib/ruby/rego/environment.rb

Overview

Execution environment for evaluating Rego policies. :reek:TooManyInstanceVariables rubocop:disable Metrics/ClassLength

Defined Under Namespace

Classes: State

Constant Summary collapse

RESERVED_BINDINGS =
{
  "input" => :input,
  "data" => :data
}.freeze
RESERVED_NAMES =
RESERVED_BINDINGS.keys.freeze

Constants included from EnvironmentOverrides

Ruby::Rego::EnvironmentOverrides::UNSET

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from EnvironmentReferenceResolution

#reference_key_for, #resolve_reference

Methods included from EnvironmentOverrides

#with_overrides

Constructor Details

#initialize(input: {}, data: {}, rules: {}, builtin_registry: Builtins::BuiltinRegistry.instance) ⇒ Environment

Create an evaluation environment.

Parameters:

  • input (Object) (defaults to: {})

    input document

  • data (Object) (defaults to: {})

    data document

  • rules (Hash) (defaults to: {})

    rule index

  • builtin_registry (Builtins::BuiltinRegistry, Builtins::BuiltinRegistryOverlay) (defaults to: Builtins::BuiltinRegistry.instance)

    registry



57
58
59
60
61
62
63
# File 'lib/ruby/rego/environment.rb', line 57

def initialize(input: {}, data: {}, rules: {}, builtin_registry: Builtins::BuiltinRegistry.instance)
  @memoization = Memoization::Store.new
  @builtin_registry = builtin_registry
  @locals = [fresh_scope] # @type var locals: Array[Hash[String, Value]]
  @scope_pool = [] # @type var @scope_pool: Array[Hash[String, Value]]
  apply_state(State.new(input: input, data: data, rules: rules, builtin_registry: builtin_registry))
end

Instance Attribute Details

#builtin_registryBuiltins::BuiltinRegistry, Builtins::BuiltinRegistryOverlay (readonly)

Builtin registry in use.



44
45
46
# File 'lib/ruby/rego/environment.rb', line 44

def builtin_registry
  @builtin_registry
end

#dataValue (readonly)

Data document as a Rego value.

Returns:



34
35
36
# File 'lib/ruby/rego/environment.rb', line 34

def data
  @data
end

#inputValue (readonly)

Input document as a Rego value.

Returns:



29
30
31
# File 'lib/ruby/rego/environment.rb', line 29

def input
  @input
end

#memoizationMemoization::Store (readonly)

Memoization store for evaluation caches.

Returns:



49
50
51
# File 'lib/ruby/rego/environment.rb', line 49

def memoization
  @memoization
end

#rulesHash (readonly)

Rule index by name.

Returns:

  • (Hash)


39
40
41
# File 'lib/ruby/rego/environment.rb', line 39

def rules
  @rules
end

Class Method Details

.from_state(state) ⇒ Environment

Build an environment from a state struct.

Parameters:

Returns:



69
70
71
72
73
74
75
76
# File 'lib/ruby/rego/environment.rb', line 69

def self.from_state(state)
  new(
    input: state.input,
    data: state.data,
    rules: state.rules,
    builtin_registry: state.builtin_registry
  )
end

Instance Method Details

#bind(name, value) ⇒ Value

Bind a local name to a value.

Parameters:

  • name (String, Symbol)

    binding name

  • value (Object)

    value to bind

Returns:

  • (Value)

    bound value

Raises:



136
137
138
139
140
141
142
143
# File 'lib/ruby/rego/environment.rb', line 136

def bind(name, value)
  name = name.to_s
  raise Error, "Cannot bind reserved name: #{name}" if RESERVED_NAMES.include?(name)

  value = Value.from_ruby(value)
  locals.last[name] = value
  value
end

#local_bound?(name) ⇒ Boolean

Check whether a name is bound in any local scope.

Parameters:

  • name (String, Symbol)

    binding name

Returns:

  • (Boolean)


166
167
168
169
170
171
172
173
174
175
# File 'lib/ruby/rego/environment.rb', line 166

def local_bound?(name)
  name = name.to_s
  return false if RESERVED_NAMES.include?(name)

  locals.reverse_each do |scope|
    return true if scope.key?(name)
  end

  false
end

#lookup(name) ⇒ Value

Lookup a binding from the current scope chain.

:reek:TooManyStatements

Parameters:

  • name (String, Symbol)

    binding name

Returns:

  • (Value)

    resolved value or undefined



150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ruby/rego/environment.rb', line 150

def lookup(name)
  name = name.to_s
  reserved = RESERVED_BINDINGS[name]
  return public_send(reserved) if reserved

  locals.reverse_each do |scope|
    return scope[name] if scope.key?(name)
  end

  UndefinedValue.new
end

#pop_scopevoid

This method returns an undefined value.

Pop the latest local scope.



95
96
97
98
99
100
101
102
# File 'lib/ruby/rego/environment.rb', line 95

def pop_scope
  return nil if locals.length <= 1

  scope = locals.pop # @type var scope: Hash[String, Value]
  scope.clear
  scope_pool << scope
  nil
end

#prepare_for_poolEnvironment

Reset the environment for pool reuse.

Returns:



126
127
128
129
# File 'lib/ruby/rego/environment.rb', line 126

def prepare_for_pool
  empty_hash = {} # @type var empty_hash: Hash[untyped, untyped]
  reset(State.new(input: empty_hash, data: empty_hash, rules: rules, builtin_registry: builtin_registry))
end

#push_scopeEnvironment

Push a new scope for local bindings.

Returns:



84
85
86
87
88
89
90
# File 'lib/ruby/rego/environment.rb', line 84

def push_scope
  scope = scope_pool.pop
  scope ||= fresh_scope
  scope.clear
  locals << scope
  self
end

#reset(state) ⇒ Environment

Reset environment state for reuse without mutation semantics.

Parameters:

  • state (State)

    reset state

Returns:



119
120
121
# File 'lib/ruby/rego/environment.rb', line 119

def reset(state)
  reset!(state)
end

#reset!(state) ⇒ Environment

Reset environment state for reuse.

Parameters:

  • state (State)

    reset state

Returns:



108
109
110
111
112
113
# File 'lib/ruby/rego/environment.rb', line 108

def reset!(state)
  apply_state(state)
  reset_scopes
  memoization.reset!
  self
end

#with_bindings(bindings) ⇒ Object

Execute a block with additional temporary bindings.

Parameters:

  • bindings (Hash{String, Symbol => Object})

    bindings to apply

Yield Returns:

  • (Object)

Returns:

  • (Object)

    block result



182
183
184
185
186
187
188
# File 'lib/ruby/rego/environment.rb', line 182

def with_bindings(bindings)
  push_scope
  bindings.each { |name, value| bind(name, value) }
  yield
ensure
  pop_scope
end

#with_builtin_registry(registry) {|environment| ... } ⇒ Object

Execute a block with an overridden builtin registry.

Parameters:

Yield Parameters:

Returns:

  • (Object)

    block result



195
196
197
198
199
200
201
202
203
# File 'lib/ruby/rego/environment.rb', line 195

def with_builtin_registry(registry)
  original = @builtin_registry
  memoization.with_context do
    @builtin_registry = registry
    yield self
  end
ensure
  @builtin_registry = original
end