Class: Oso::Polar::Polar

Inherits:
Object
  • Object
show all
Defined in:
lib/oso/polar/polar.rb

Overview

Create and manage an instance of the Polar runtime.

Direct Known Subclasses

Oso

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePolar

Returns a new instance of Polar.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/oso/polar/polar.rb', line 66

def initialize
  @ffi_polar = FFI::Polar.create
  @host = Host.new(ffi_polar)
  @ffi_polar.enrich_message = @host.method(:enrich_message)

  # Register global constants.
  register_constant nil, name: 'nil'

  # Register built-in classes.
  register_class PolarBoolean, name: 'Boolean'
  register_class Integer
  register_class Float
  register_class Array, name: 'List'
  register_class Hash, name: 'Dictionary'
  register_class String
end

Instance Attribute Details

#hostHost (readonly)

Returns:



64
65
66
# File 'lib/oso/polar/polar.rb', line 64

def host
  @host
end

Instance Method Details

#clear_rulesself

Clear all rules and rule sources from the current Polar instance

Returns:

  • (self)

    for chaining.



94
95
96
97
# File 'lib/oso/polar/polar.rb', line 94

def clear_rules
  ffi_polar.clear_rules
  self
end

#ffiObject



83
84
85
# File 'lib/oso/polar/polar.rb', line 83

def ffi
  @ffi_polar
end

#load_file(filename) ⇒ self

Deprecated.

#load_file has been deprecated in favor of #load_files as of the 0.20 release. Please see changelog for migration instructions: docs.osohq.com/project/changelogs/2021-09-15.html

Load a Polar policy file.

Parameters:

  • filename (String)

Returns:

  • (self)

    for chaining.

Raises:



130
131
132
133
134
135
136
137
# File 'lib/oso/polar/polar.rb', line 130

def load_file(filename)
  warn <<~WARNING
    `Oso#load_file` has been deprecated in favor of `Oso#load_files` as of the 0.20 release.

    Please see changelog for migration instructions: https://docs.osohq.com/project/changelogs/2021-09-15.html
  WARNING
  load_files([filename])
end

#load_files(filenames = []) ⇒ self

Load Polar policy files.

Parameters:

  • filenames (Array<String>) (defaults to: [])

Returns:

  • (self)

    for chaining.

Raises:



108
109
110
111
112
113
114
# File 'lib/oso/polar/polar.rb', line 108

def load_files(filenames = [])
  return if filenames.empty?

  sources = filenames.map { |f| filename_to_source f }
  load_sources(sources)
  self
end

#load_str(str, filename: nil) ⇒ self

Load a Polar string into the KB.

Parameters:

  • str (String)

    Polar string to load.

  • filename (String) (defaults to: nil)

    Name of Polar source file.

Returns:

  • (self)

    for chaining.

Raises:



147
148
149
150
151
152
# File 'lib/oso/polar/polar.rb', line 147

def load_str(str, filename: nil)
  raise NullByteInPolarFileError if str.chomp("\0").include?("\0")

  load_sources([Source.new(str, filename: filename)])
  self
end

#name_to_class(class_name) ⇒ Object



87
88
89
# File 'lib/oso/polar/polar.rb', line 87

def name_to_class(class_name)
  host.types[class_name].klass.get
end

#query(query, host: self.host.dup, bindings: {}) ⇒ Enumerator

Query for a Polar predicate or string.

Parameters:

  • query (String, Predicate)
  • host (Host) (defaults to: self.host.dup)
  • bindings (Hash) (defaults to: {})

Returns:

  • (Enumerator)

    of resulting bindings

Raises:

  • (Error)

    if the FFI call raises one.



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/oso/polar/polar.rb', line 161

def query(query, host: self.host.dup, bindings: {})
  case query
  when String
    ffi_query = ffi_polar.new_query_from_str(query)
  when Predicate
    ffi_query = ffi_polar.new_query_from_term(host.to_polar(query))
  else
    raise InvalidQueryTypeError
  end
  Query.new(ffi_query, host: host, bindings: bindings)
end

#query_rule(name, *args, accept_expression: false, bindings: {}) ⇒ Enumerator

Query for a rule.

Parameters:

  • name (String)
  • args (Array<Object>)

Returns:

  • (Enumerator)

    of resulting bindings

Raises:

  • (Error)

    if the FFI call raises one.



179
180
181
182
183
# File 'lib/oso/polar/polar.rb', line 179

def query_rule(name, *args, accept_expression: false, bindings: {})
  host = self.host.dup
  host.accept_expression = accept_expression
  query(Predicate.new(name, args: args), host: host, bindings: bindings)
end

#query_rule_once(name, *args) ⇒ Boolean

Query for a rule, returning true if it has any results.

Parameters:

  • name (String)
  • args (Array<Object>)

Returns:

  • (Boolean)

    indicating whether the query found at least one result.

Raises:

  • (Error)

    if the FFI call raises one.



191
192
193
# File 'lib/oso/polar/polar.rb', line 191

def query_rule_once(name, *args)
  query_rule(name, *args).any?
end

#register_class(cls, name: nil, fields: nil, combine_query: nil, build_query: nil, exec_query: nil) ⇒ self

Register a Ruby class with Polar.

under a previously-registered name.

Parameters:

  • cls (Class)

    the class to register.

  • name (String) (defaults to: nil)

    the name to register the class as. Defaults to the name of the class.

  • fields (Hash) (defaults to: nil)

    a map from field names on instances of cls to types, or Relation objects.

  • build_query (Proc) (defaults to: nil)

    a method to produce a query for cls objects, given a list of Filters.

  • exec_query (Proc) (defaults to: nil)

    a method to execute a query produced by build_query

  • combine_query (Proc) (defaults to: nil)

    a method to merge two queries produced by build_query

Returns:

  • (self)

    for chaining.

Raises:



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/oso/polar/polar.rb', line 207

def register_class(cls, name: nil, fields: nil, combine_query: nil, build_query: nil, exec_query: nil) # rubocop:disable Metrics/ParameterLists
  name = host.cache_class(
    cls,
    name: name || cls.name,
    fields: fields,
    build_query: build_query || maybe_mtd(cls, :build_query),
    combine_query: combine_query || maybe_mtd(cls, :combine_query),
    exec_query: exec_query || maybe_mtd(cls, :exec_query)
  )
  register_constant(cls, name: name)
  host.register_mros
end

#register_constant(value, name:) ⇒ self

Register a Ruby object with Polar.

Parameters:

  • value (Object)

    the object to register.

  • name (String)

    the name to register the object as.

Returns:

  • (self)

    for chaining.

Raises:

  • (FFI::Error)

    if the FFI call returns an error.



226
227
228
229
# File 'lib/oso/polar/polar.rb', line 226

def register_constant(value, name:)
  ffi_polar.register_constant(host.to_polar(value), name: name)
  self
end

#repl(files = []) ⇒ Object

Start a REPL session.

Parameters:

  • files (Array<String>) (defaults to: [])

Raises:

  • (Error)

    if the FFI call raises one.



235
236
237
238
239
240
241
242
243
244
# File 'lib/oso/polar/polar.rb', line 235

def repl(files = [])
  load_files(files)
  prompt = "#{FG_BLUE}query>#{RESET} "
  # Try loading the readline module from the Ruby stdlib. If we get a
  # LoadError, fall back to the standard REPL with no readline support.
  require 'readline'
  repl_readline(prompt)
rescue LoadError
  repl_standard(prompt)
end