oso-oso 0.3.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f319a22c3568b2a11da1be2ff92d458acac50a1
4
- data.tar.gz: b42631f294bbf9f5f45c70c8c8beef6bf13756ce
3
+ metadata.gz: 70321ccb3dc8e6468b89109aa7d9bbb97868d1be
4
+ data.tar.gz: 8b7c1873eba4dae6d1150f8f91a74a3d5a72e51c
5
5
  SHA512:
6
- metadata.gz: 60774d89d1abec361c3d4ad96c2c5e1dd56fde369f4c49cb36fbdd6c9cb3f78d3ea51ee394febbf5472d1500dace92a8f346281fd3c04c8a88e9e040d467e5fd
7
- data.tar.gz: 2e08087f17170f44fcd528c5f8f756d5979a1b30765b4417506195f10b6b8587f70500582efea0d1780a8124091b7ccc71a7c0d66d00ef601dbd48077d5a3707
6
+ metadata.gz: 639ea45ef8a86ebf97ae20ee1977499e18f04827b05d869369c7ec01792eb7a7747c6bae9f7d7789af4993a4b04edef5b375c9caa504c6e09afe701ed931874f
7
+ data.tar.gz: 7dc68ed639125472e808ff375059ceb6d70837fad20c05672a183452bef78eeee56dcc0dc58579d21536749bc8e2fbc2628baa349473984c824d23b38d1270f4
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Exclude:
4
+ - '**/*~'
5
+ - 'bin/oso'
6
+ - 'vendor/**/*'
7
+ NewCops: enable
data/Gemfile CHANGED
@@ -3,4 +3,3 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
-
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- oso-oso (0.3.1)
4
+ oso-oso (0.6.0)
5
5
  ffi (~> 1.0)
6
6
 
7
7
  GEM
@@ -12,18 +12,18 @@ GEM
12
12
  benchmark (0.1.0)
13
13
  byebug (11.1.3)
14
14
  coderay (1.1.3)
15
- diff-lcs (1.3)
15
+ diff-lcs (1.4.4)
16
16
  e2mmap (0.1.0)
17
17
  ffi (1.13.1)
18
18
  jaro_winkler (1.5.4)
19
19
  maruku (0.7.3)
20
20
  method_source (1.0.0)
21
21
  mini_portile2 (2.4.0)
22
- nokogiri (1.10.9)
22
+ nokogiri (1.10.10)
23
23
  mini_portile2 (~> 2.4.0)
24
- parallel (1.19.1)
25
- parser (2.7.1.3)
26
- ast (~> 2.4.0)
24
+ parallel (1.19.2)
25
+ parser (2.7.1.4)
26
+ ast (~> 2.4.1)
27
27
  pry (0.13.1)
28
28
  coderay (~> 1.1)
29
29
  method_source (~> 1.0)
@@ -49,19 +49,19 @@ GEM
49
49
  diff-lcs (>= 1.2.0, < 2.0)
50
50
  rspec-support (~> 3.9.0)
51
51
  rspec-support (3.9.3)
52
- rubocop (0.85.1)
52
+ rubocop (0.89.1)
53
53
  parallel (~> 1.10)
54
- parser (>= 2.7.0.1)
54
+ parser (>= 2.7.1.1)
55
55
  rainbow (>= 2.2.2, < 4.0)
56
56
  regexp_parser (>= 1.7)
57
57
  rexml
58
- rubocop-ast (>= 0.0.3)
58
+ rubocop-ast (>= 0.3.0, < 1.0)
59
59
  ruby-progressbar (~> 1.7)
60
60
  unicode-display_width (>= 1.4.0, < 2.0)
61
- rubocop-ast (0.0.3)
62
- parser (>= 2.7.0.1)
61
+ rubocop-ast (0.3.0)
62
+ parser (>= 2.7.1.4)
63
63
  ruby-progressbar (1.10.1)
64
- solargraph (0.39.8)
64
+ solargraph (0.39.14)
65
65
  backport (~> 1.1)
66
66
  benchmark
67
67
  bundler (>= 1.17.2)
@@ -88,7 +88,8 @@ DEPENDENCIES
88
88
  pry-byebug (~> 3.9.0)
89
89
  rake (~> 12.0)
90
90
  rspec (~> 3.0)
91
- solargraph (~> 0.39.8)
91
+ rubocop (~> 0.89.1)
92
+ solargraph (~> 0.39.14)
92
93
  yard (~> 0.9.25)
93
94
 
94
95
  BUNDLED WITH
data/Makefile CHANGED
@@ -1,13 +1,19 @@
1
- .PHONY: install rust test
1
+ .PHONY: rust install test lint typecheck repl
2
2
 
3
3
  rust:
4
4
  $(MAKE) -C ../.. rust-build
5
5
 
6
- install: rust
6
+ install:
7
7
  bundle install
8
8
 
9
- test: install
9
+ test: install rust
10
10
  bundle exec rake spec
11
11
 
12
- repl:
12
+ lint: install
13
+ bundle exec rubocop
14
+
15
+ typecheck: install
16
+ bundle exec solargraph typecheck
17
+
18
+ repl: install rust
13
19
  bundle exec oso
data/Rakefile CHANGED
@@ -6,4 +6,3 @@ require 'rspec/core/rake_task'
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
8
  task default: :spec
9
-
data/bin/oso CHANGED
@@ -4,4 +4,4 @@
4
4
  require 'bundler/setup'
5
5
  require 'oso'
6
6
 
7
- Oso.new.repl(load: true)
7
+ Oso.new.repl(ARGV)
Binary file
Binary file
@@ -3,7 +3,7 @@
3
3
  module Oso
4
4
  # An HTTP resource.
5
5
  class Http
6
- def initialize(hostname: nil, path: nil, query: nil)
6
+ def initialize(hostname, path, query)
7
7
  @hostname = hostname
8
8
  @path = path
9
9
  @query = query
@@ -3,7 +3,7 @@
3
3
  require_relative 'polar/polar'
4
4
 
5
5
  module Oso
6
- # Oso authorization API.
6
+ # oso authorization API.
7
7
  class Oso < Polar::Polar
8
8
  def initialize
9
9
  super
@@ -11,6 +11,13 @@ module Oso
11
11
  register_class(PathMapper, name: 'PathMapper')
12
12
  end
13
13
 
14
+ # Query the knowledge base to determine whether an actor is allowed to
15
+ # perform an action upon a resource.
16
+ #
17
+ # @param actor [Object] Subject.
18
+ # @param action [Object] Verb.
19
+ # @param resource [Object] Object.
20
+ # @return [Boolean] An access control decision.
14
21
  def allowed?(actor:, action:, resource:)
15
22
  query_rule('allow', actor, action, resource).next
16
23
  true
@@ -4,7 +4,7 @@ module Oso
4
4
  # Map from a template string with capture groups of the form
5
5
  # `{name}` to a dictionary of the form `{name: captured_value}`
6
6
  class PathMapper
7
- def initialize(template:)
7
+ def initialize(template)
8
8
  capture_group = /({([^}]+)})/
9
9
 
10
10
  template = template.dup
@@ -5,16 +5,12 @@ module Oso
5
5
  # Base error type for Oso::Polar.
6
6
  class Error < ::RuntimeError
7
7
  attr_reader :stack_trace
8
-
8
+
9
9
  # @param message [String]
10
10
  # @param details [Hash]
11
11
  def initialize(message = nil, details: nil)
12
12
  @details = details
13
- if details and details.key?("stack_trace")
14
- @stack_trace = details['stack_trace']
15
- else
16
- @stack_trace = nil
17
- end
13
+ @stack_trace = details&.fetch('stack_trace', nil)
18
14
  super(message)
19
15
  end
20
16
  end
@@ -31,6 +27,7 @@ module Oso
31
27
  class UnsupportedError < PolarRuntimeError; end
32
28
  class PolarTypeError < PolarRuntimeError; end
33
29
  class StackOverflowError < PolarRuntimeError; end
30
+ class FileLoadingError < PolarRuntimeError; end
34
31
 
35
32
  # Errors originating from this side of the FFI boundary.
36
33
 
@@ -38,15 +35,22 @@ module Oso
38
35
  class MissingConstructorError < PolarRuntimeError; end
39
36
  class UnregisteredInstanceError < PolarRuntimeError; end
40
37
  class DuplicateInstanceRegistrationError < PolarRuntimeError; end
38
+
39
+ # TODO: I think this should probably have some arguments to say what the call is
41
40
  class InvalidCallError < PolarRuntimeError; end
42
41
  class InvalidConstructorError < PolarRuntimeError; end
43
42
  class InvalidQueryTypeError < PolarRuntimeError; end
44
- class InlineQueryFailedError < PolarRuntimeError; end
45
43
  class NullByteInPolarFileError < PolarRuntimeError; end
46
44
  class UnexpectedPolarTypeError < PolarRuntimeError; end
45
+ class InlineQueryFailedError < PolarRuntimeError; # rubocop:disable Style/Documentation
46
+ # @param source [String]
47
+ def initialize(source)
48
+ super("Inline query failed: #{source}")
49
+ end
50
+ end
47
51
  class PolarFileExtensionError < PolarRuntimeError # rubocop:disable Style/Documentation
48
- def initialize
49
- super('Polar files must have .pol or .polar extension.')
52
+ def initialize(file)
53
+ super("Polar files must have .polar extension. Offending file: #{file}")
50
54
  end
51
55
  end
52
56
  class PolarFileNotFoundError < PolarRuntimeError # rubocop:disable Style/Documentation
@@ -59,7 +63,7 @@ module Oso
59
63
  # @param as [String]
60
64
  # @param old [Class]
61
65
  # @param new [Class]
62
- def initialize(name:, old:, new:) # rubocop:disable Naming/MethodParameterName
66
+ def initialize(name:, old:, new:)
63
67
  super("Attempted to alias #{new} as '#{name}', but #{old} already has that alias.")
64
68
  end
65
69
  end
@@ -5,7 +5,7 @@ require 'ffi'
5
5
  module Oso
6
6
  module Polar
7
7
  module FFI
8
- LIB = ::FFI::Platform::LIBPREFIX + 'polar.' + ::FFI::Platform::LIBSUFFIX
8
+ LIB = "#{::FFI::Platform::LIBPREFIX}polar.#{::FFI::Platform::LIBSUFFIX}"
9
9
  RELEASE_PATH = File.expand_path(File.join(__dir__, "../../../ext/oso-oso/lib/#{LIB}"))
10
10
  DEV_PATH = File.expand_path(File.join(__dir__, "../../../../../target/debug/#{LIB}"))
11
11
  # If the lib exists in the ext/ dir, use it. Otherwise, fall back to
@@ -37,7 +37,20 @@ module Oso
37
37
  # Wrapper class for Error FFI pointer + operations.
38
38
  class Error < ::FFI::AutoPointer
39
39
  def self.release(ptr)
40
- Rust.free(ptr)
40
+ Rust.free(ptr) unless ptr.null?
41
+ end
42
+ end
43
+ # Wrapper class for Message FFI pointer + operations.
44
+ class Message < ::FFI::AutoPointer
45
+ def self.release(ptr)
46
+ Rust.free(ptr) unless ptr.null?
47
+ end
48
+ end
49
+
50
+ # Wrapper class for Source FFI pointer.
51
+ class Source < ::FFI::AutoPointer
52
+ def self.release(ptr)
53
+ Rust.free(ptr) unless ptr.null?
41
54
  end
42
55
  end
43
56
  end
@@ -49,3 +62,5 @@ require 'oso/polar/ffi/polar'
49
62
  require 'oso/polar/ffi/query'
50
63
  require 'oso/polar/ffi/query_event'
51
64
  require 'oso/polar/ffi/error'
65
+ require 'oso/polar/ffi/message'
66
+ require 'oso/polar/ffi/source'
@@ -22,7 +22,7 @@ module Oso
22
22
  #
23
23
  # @return [::Oso::Polar::Error] if there's an FFI error.
24
24
  # @return [::Oso::Polar::FFIErrorNotFound] if there isn't one.
25
- def self.get # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
25
+ def self.get # rubocop:disable Metrics/MethodLength
26
26
  error = Rust.get
27
27
  return ::Oso::Polar::FFIErrorNotFound if error.null?
28
28
 
@@ -48,7 +48,7 @@ module Oso
48
48
  # @param msg [String]
49
49
  # @param details [Hash<String, Object>]
50
50
  # @return [::Oso::Polar::ParseError] the object converted into the expected format.
51
- private_class_method def self.parse_error(kind, msg:, details:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
51
+ private_class_method def self.parse_error(kind, msg:, details:) # rubocop:disable Metrics/MethodLength
52
52
  case kind
53
53
  when 'ExtraToken'
54
54
  ::Oso::Polar::ParseError::ExtraToken.new(msg, details: details)
@@ -83,6 +83,8 @@ module Oso
83
83
  ::Oso::Polar::PolarTypeError.new(msg, details: details)
84
84
  when 'StackOverflow'
85
85
  ::Oso::Polar::StackOverflowError.new(msg, details: details)
86
+ when 'FileLoading'
87
+ ::Oso::Polar::FileLoadingError.new(msg, details: details)
86
88
  else
87
89
  ::Oso::Polar::PolarRuntimeError.new(msg, details: details)
88
90
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Oso
4
+ module Polar
5
+ module FFI
6
+ # Wrapper class for Message FFI pointer + operations.
7
+ class Message < ::FFI::AutoPointer
8
+ # @return [String]
9
+ def to_s
10
+ @to_s ||= read_string.force_encoding('UTF-8')
11
+ end
12
+
13
+ Rust = Module.new do
14
+ extend ::FFI::Library
15
+ ffi_lib FFI::LIB_PATH
16
+
17
+ attach_function :free, :string_free, [Message], :int32
18
+ end
19
+
20
+ def process
21
+ message = JSON.parse(to_s)
22
+ kind = message['kind']
23
+ msg = message['msg']
24
+
25
+ case kind
26
+ when 'Print'
27
+ puts(msg)
28
+ when 'Warning'
29
+ warn(format('[warning] %<msg>s', msg: msg))
30
+ end
31
+ end
32
+
33
+ private_constant :Rust
34
+ end
35
+ end
36
+ end
37
+ end
@@ -10,12 +10,13 @@ module Oso
10
10
  ffi_lib FFI::LIB_PATH
11
11
 
12
12
  attach_function :new, :polar_new, [], FFI::Polar
13
- attach_function :load_str, :polar_load, [FFI::Polar, :string, :string], :int32
13
+ attach_function :load, :polar_load, [FFI::Polar, :string, :string], :int32
14
14
  attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar, :uint32], FFI::Query
15
15
  attach_function :new_id, :polar_get_external_id, [FFI::Polar], :uint64
16
16
  attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32], FFI::Query
17
17
  attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string, :uint32], FFI::Query
18
18
  attach_function :register_constant, :polar_register_constant, [FFI::Polar, :string, :string], :int32
19
+ attach_function :next_message, :polar_next_polar_message, [FFI::Polar], FFI::Message
19
20
  attach_function :free, :polar_free, [FFI::Polar], :int32
20
21
  end
21
22
  private_constant :Rust
@@ -32,8 +33,10 @@ module Oso
32
33
  # @param src [String]
33
34
  # @param filename [String]
34
35
  # @raise [FFI::Error] if the FFI call returns an error.
35
- def load_str(src, filename: nil)
36
- raise FFI::Error.get if Rust.load_str(self, src, filename).zero?
36
+ def load(src, filename: nil)
37
+ loaded = Rust.load(self, src, filename)
38
+ process_messages
39
+ raise FFI::Error.get if loaded.zero?
37
40
  end
38
41
 
39
42
  # @return [FFI::Query] if there are remaining inline queries.
@@ -41,6 +44,7 @@ module Oso
41
44
  # @raise [FFI::Error] if the FFI call returns an error.
42
45
  def next_inline_query
43
46
  query = Rust.next_inline_query(self, 0)
47
+ process_messages
44
48
  query.null? ? nil : query
45
49
  end
46
50
 
@@ -60,6 +64,7 @@ module Oso
60
64
  # @raise [FFI::Error] if the FFI call returns an error.
61
65
  def new_query_from_str(str)
62
66
  query = Rust.new_query_from_str(self, str, 0)
67
+ process_messages
63
68
  raise FFI::Error.get if query.null?
64
69
 
65
70
  query
@@ -70,6 +75,7 @@ module Oso
70
75
  # @raise [FFI::Error] if the FFI call returns an error.
71
76
  def new_query_from_term(term)
72
77
  query = Rust.new_query_from_term(self, JSON.dump(term), 0)
78
+ process_messages
73
79
  raise FFI::Error.get if query.null?
74
80
 
75
81
  query
@@ -79,7 +85,21 @@ module Oso
79
85
  # @param value [Hash<String, Object>]
80
86
  # @raise [FFI::Error] if the FFI call returns an error.
81
87
  def register_constant(name, value:)
82
- raise FFI::Error.get if Rust.register_constant(self, name, JSON.dump(value)).zero?
88
+ registered = Rust.register_constant(self, name, JSON.dump(value))
89
+ raise FFI::Error.get if registered.zero?
90
+ end
91
+
92
+ def next_message
93
+ Rust.next_message(self)
94
+ end
95
+
96
+ def process_messages
97
+ loop do
98
+ message = next_message
99
+ break if message.null?
100
+
101
+ message.process
102
+ end
83
103
  end
84
104
  end
85
105
  end
@@ -14,6 +14,8 @@ module Oso
14
14
  attach_function :question_result, :polar_question_result, [FFI::Query, :uint64, :int32], :int32
15
15
  attach_function :application_error, :polar_application_error, [FFI::Query, :string], :int32
16
16
  attach_function :next_event, :polar_next_query_event, [FFI::Query], FFI::QueryEvent
17
+ attach_function :next_message, :polar_next_query_message, [FFI::Query], FFI::Message
18
+ attach_function :source, :polar_query_source_info, [FFI::Query], FFI::Source
17
19
  attach_function :free, :query_free, [FFI::Query], :int32
18
20
  end
19
21
  private_constant :Rust
@@ -22,6 +24,7 @@ module Oso
22
24
  # @raise [FFI::Error] if the FFI call returns an error.
23
25
  def debug_command(cmd)
24
26
  res = Rust.debug_command(self, cmd)
27
+ process_messages
25
28
  raise FFI::Error.get if res.zero?
26
29
  end
27
30
 
@@ -54,10 +57,33 @@ module Oso
54
57
  # @raise [FFI::Error] if the FFI call returns an error.
55
58
  def next_event
56
59
  event = Rust.next_event(self)
60
+ process_messages
57
61
  raise FFI::Error.get if event.null?
58
62
 
59
63
  ::Oso::Polar::QueryEvent.new(JSON.parse(event.to_s))
60
64
  end
65
+
66
+ def next_message
67
+ Rust.next_message(self)
68
+ end
69
+
70
+ def process_messages
71
+ loop do
72
+ message = next_message
73
+ break if message.null?
74
+
75
+ message.process
76
+ end
77
+ end
78
+
79
+ # @return [String]
80
+ # @raise [FFI::Error] if the FFI call returns an error.
81
+ def source
82
+ res = Rust.source(self)
83
+ raise FFI::Error.get if res.null?
84
+
85
+ res.to_s
86
+ end
61
87
  end
62
88
  end
63
89
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Oso
4
+ module Polar
5
+ module FFI
6
+ # Wrapper class for Source FFI pointer.
7
+ class Source < ::FFI::AutoPointer
8
+ # @return [String]
9
+ def to_s
10
+ @to_s ||= read_string.force_encoding('UTF-8')
11
+ end
12
+
13
+ Rust = Module.new do
14
+ extend ::FFI::Library
15
+ ffi_lib FFI::LIB_PATH
16
+
17
+ attach_function :free, :string_free, [Message], :int32
18
+ end
19
+
20
+ private_constant :Rust
21
+ end
22
+ end
23
+ end
24
+ end
@@ -3,8 +3,9 @@
3
3
  module Oso
4
4
  module Polar
5
5
  # Translate between Polar and the host language (Ruby).
6
- class Host
6
+ class Host # rubocop:disable Metrics/ClassLength
7
7
  protected
8
+
8
9
  # @return [FFI::Polar]
9
10
  attr_reader :ffi_polar
10
11
  # @return [Hash<String, Class>]
@@ -15,6 +16,7 @@ module Oso
15
16
  attr_reader :instances
16
17
 
17
18
  public
19
+
18
20
  def initialize(ffi_polar)
19
21
  @ffi_polar = ffi_polar
20
22
  @classes = {}
@@ -44,9 +46,10 @@ module Oso
44
46
  #
45
47
  # @param cls [Class] the class to cache
46
48
  # @param name [String] the name to cache the class as. Defaults to the name of the class.
49
+ # @param constructor [Proc] optional custom constructor function. Defaults to the :new method.
47
50
  # @return [String] the name the class is cached as.
48
51
  # @raise [UnregisteredClassError] if the class has not been registered.
49
- def cache_class(cls, name:, constructor:)
52
+ def cache_class(cls, name:, constructor:) # rubocop:disable Metrics/MethodLength
50
53
  name = cls.name if name.nil?
51
54
  raise DuplicateClassAliasError, name: name, old: get_class(name), new: cls if classes.key? name
52
55
 
@@ -66,14 +69,14 @@ module Oso
66
69
  # @param name [String]
67
70
  # @return [Symbol] if constructor is the default of `:new`.
68
71
  # @return [Proc] if a custom constructor was registered.
69
- # @raise [UnregisteredConstructorError] if the constructor has not been registered.
72
+ # @raise [MissingConstructorError] if the constructor has not been registered.
70
73
  def get_constructor(name)
71
74
  raise MissingConstructorError, name unless constructors.key? name
72
75
 
73
76
  constructors[name]
74
77
  end
75
78
 
76
- # Check if an instance has been cached.
79
+ # Check if an instance exists in the {#instances} cache.
77
80
  #
78
81
  # @param id [Integer]
79
82
  # @return [Boolean]
@@ -97,7 +100,8 @@ module Oso
97
100
  instances[id]
98
101
  end
99
102
 
100
- # Cache a Ruby instance, fetching a {#new_id} if one isn't provided.
103
+ # Cache a Ruby instance in the {#instances} cache, fetching a {#new_id}
104
+ # if one isn't provided.
101
105
  #
102
106
  # @param instance [Object]
103
107
  # @param id [Integer]
@@ -111,22 +115,23 @@ module Oso
111
115
  # Construct and cache a Ruby instance.
112
116
  #
113
117
  # @param cls_name [String]
114
- # @param fields [Hash<String, Hash>]
118
+ # @param args [Array<Object>]
119
+ # @param kwargs [Hash<String, Object>]
115
120
  # @param id [Integer]
116
121
  # @raise [PolarRuntimeError] if instance construction fails.
117
- def make_instance(cls_name, fields:, id:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
122
+ def make_instance(cls_name, args:, kwargs:, id:) # rubocop:disable Metrics/MethodLength
118
123
  constructor = get_constructor(cls_name)
119
- fields = Hash[fields.map { |k, v| [k.to_sym, to_ruby(v)] }]
124
+ # The kwargs.empty? checks are for Ruby < 2.7.
120
125
  instance = if constructor == :new
121
- if fields.empty?
122
- get_class(cls_name).__send__(:new)
126
+ if kwargs.empty?
127
+ get_class(cls_name).__send__(:new, *args)
123
128
  else
124
- get_class(cls_name).__send__(:new, **fields)
129
+ get_class(cls_name).__send__(:new, *args, **kwargs)
125
130
  end
126
- elsif fields.empty?
127
- constructor.call
131
+ elsif kwargs.empty?
132
+ constructor.call(*args)
128
133
  else
129
- constructor.call(**fields)
134
+ constructor.call(*args, **kwargs)
130
135
  end
131
136
  cache_instance(instance, id: id)
132
137
  rescue StandardError => e
@@ -142,22 +147,20 @@ module Oso
142
147
  # @return [Boolean]
143
148
  def subspecializer?(instance_id, left_tag:, right_tag:)
144
149
  mro = get_instance(instance_id).class.ancestors
145
- mro.index(get_class(left_tag)) < mro.index(get_class(right_tag))
146
- rescue StandardError
147
- false
150
+ left_index = mro.index(get_class(left_tag))
151
+ right_index = mro.index(get_class(right_tag))
152
+ left_index && right_index && left_index < right_index
148
153
  end
149
154
 
150
155
  # Check if instance is an instance of class.
151
156
  #
152
- # @param instance_id [Integer]
157
+ # @param instance [Hash<String, Object>]
153
158
  # @param class_tag [String]
154
159
  # @return [Boolean]
155
- def isa?(instance_id, class_tag:)
156
- instance = get_instance(instance_id)
160
+ def isa?(instance, class_tag:)
161
+ instance = to_ruby(instance)
157
162
  cls = get_class(class_tag)
158
163
  instance.is_a? cls
159
- rescue PolarRuntimeError
160
- false
161
164
  end
162
165
 
163
166
  # Check if two instances unify
@@ -169,8 +172,6 @@ module Oso
169
172
  left_instance = get_instance(left_instance_id)
170
173
  right_instance = get_instance(right_instance_id)
171
174
  left_instance == right_instance
172
- rescue PolarRuntimeError
173
- false
174
175
  end
175
176
 
176
177
  # Turn a Ruby value into a Polar term that's ready to be sent across the
@@ -178,22 +179,29 @@ module Oso
178
179
  #
179
180
  # @param value [Object]
180
181
  # @return [Hash<String, Object>]
181
- def to_polar_term(value) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
182
+ def to_polar(value) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
182
183
  value = case true # rubocop:disable Lint/LiteralAsCondition
183
184
  when value.instance_of?(TrueClass) || value.instance_of?(FalseClass)
184
185
  { 'Boolean' => value }
185
186
  when value.instance_of?(Integer)
186
187
  { 'Number' => { 'Integer' => value } }
187
188
  when value.instance_of?(Float)
189
+ if value == Float::INFINITY
190
+ value = 'Infinity'
191
+ elsif value == -Float::INFINITY
192
+ value = '-Infinity'
193
+ elsif value.nan?
194
+ value = 'NaN'
195
+ end
188
196
  { 'Number' => { 'Float' => value } }
189
197
  when value.instance_of?(String)
190
198
  { 'String' => value }
191
199
  when value.instance_of?(Array)
192
- { 'List' => value.map { |el| to_polar_term(el) } }
200
+ { 'List' => value.map { |el| to_polar(el) } }
193
201
  when value.instance_of?(Hash)
194
- { 'Dictionary' => { 'fields' => value.transform_values { |v| to_polar_term(v) } } }
202
+ { 'Dictionary' => { 'fields' => value.transform_values { |v| to_polar(v) } } }
195
203
  when value.instance_of?(Predicate)
196
- { 'Call' => { 'name' => value.name, 'args' => value.args.map { |el| to_polar_term(el) } } }
204
+ { 'Call' => { 'name' => value.name, 'args' => value.args.map { |el| to_polar(el) } } }
197
205
  when value.instance_of?(Variable)
198
206
  # This is supported so that we can query for unbound variables
199
207
  { 'Variable' => value }
@@ -211,13 +219,28 @@ module Oso
211
219
  # @option data [Hash<String, Object>] :value
212
220
  # @return [Object]
213
221
  # @raise [UnexpectedPolarTypeError] if type cannot be converted to Ruby.
214
- def to_ruby(data) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
222
+ def to_ruby(data) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
215
223
  tag, value = data['value'].first
216
224
  case tag
217
225
  when 'String', 'Boolean'
218
226
  value
219
227
  when 'Number'
220
- value.values.first
228
+ num = value.values.first
229
+ if value.key? 'Float'
230
+ case num
231
+ when 'Infinity'
232
+ return Float::INFINITY
233
+ when '-Infinity'
234
+ return -Float::INFINITY
235
+ when 'NaN'
236
+ return Float::NAN
237
+ else
238
+ unless value['Float'].is_a? Float # rubocop:disable Metrics/BlockNesting
239
+ raise PolarRuntimeError, "Expected a floating point number, got \"#{value['Float']}\""
240
+ end
241
+ end
242
+ end
243
+ num
221
244
  when 'List'
222
245
  value.map { |el| to_ruby(el) }
223
246
  when 'Dictionary'
@@ -3,36 +3,72 @@
3
3
  require 'json'
4
4
  require 'pp'
5
5
  require 'set'
6
+ require 'digest/md5'
7
+
8
+ # Missing Ruby type.
9
+ module PolarBoolean; end
10
+ # Monkey-patch Ruby true type.
11
+ class TrueClass; include PolarBoolean; end
12
+ # Monkey-patch Ruby false type.
13
+ class FalseClass; include PolarBoolean; end
14
+
15
+ # https://github.com/ruby/ruby/blob/bb9ecd026a6cadd5d0f85ac061649216806ed935/lib/bundler/vendor/thor/lib/thor/shell/color.rb#L99-L105
16
+ def supports_color
17
+ $stdout.tty? && $stderr.tty? && ENV['NO_COLOR'].nil?
18
+ end
19
+
20
+ if supports_color
21
+ RESET = "\x1b[0m"
22
+ FG_BLUE = "\x1b[34m"
23
+ FG_RED = "\x1b[31m"
24
+ else
25
+ RESET = ''
26
+ FG_BLUE = ''
27
+ FG_RED = ''
28
+ end
29
+
30
+ def print_error(error)
31
+ warn FG_RED + error.class.name.split('::').last + RESET
32
+ warn error.message
33
+ end
6
34
 
7
35
  module Oso
8
36
  module Polar
9
37
  # Create and manage an instance of the Polar runtime.
10
- class Polar
38
+ class Polar # rubocop:disable Metrics/ClassLength
11
39
  # @return [Host]
12
40
  attr_reader :host
13
41
 
14
42
  def initialize
15
43
  @ffi_polar = FFI::Polar.create
16
44
  @host = Host.new(ffi_polar)
17
- @load_queue = Set.new
45
+
46
+ # Register built-in classes.
47
+ register_class PolarBoolean, name: 'Boolean'
48
+ register_class Integer
49
+ register_class Float
50
+ register_class Array, name: 'List'
51
+ register_class Hash, name: 'Dictionary'
52
+ register_class String
18
53
  end
19
54
 
20
55
  # Replace the current Polar instance but retain all registered classes and constructors.
21
56
  def clear
22
- load_queue.clear
23
57
  @ffi_polar = FFI::Polar.create
24
58
  end
25
59
 
26
- # Enqueue a Polar policy file for loading into the KB.
60
+ # Load a Polar policy file.
27
61
  #
28
62
  # @param name [String]
29
63
  # @raise [PolarFileExtensionError] if provided filename has invalid extension.
30
64
  # @raise [PolarFileNotFoundError] if provided filename does not exist.
31
65
  def load_file(name)
32
- raise PolarFileExtensionError unless ['.pol', '.polar'].include? File.extname(name)
33
- raise PolarFileNotFoundError, name unless File.file?(name)
66
+ raise PolarFileExtensionError, name unless File.extname(name) == '.polar'
34
67
 
35
- load_queue << name
68
+ file_data = File.open(name, &:read)
69
+ load_str(file_data, filename: name)
70
+ rescue Errno::ENOENT
71
+ raise PolarFileNotFoundError, name
36
72
  end
37
73
 
38
74
  # Load a Polar string into the KB.
@@ -45,7 +81,7 @@ module Oso
45
81
  def load_str(str, filename: nil) # rubocop:disable Metrics/MethodLength
46
82
  raise NullByteInPolarFileError if str.chomp("\0").include?("\0")
47
83
 
48
- ffi_polar.load_str(str, filename: filename)
84
+ ffi_polar.load(str, filename: filename)
49
85
  loop do
50
86
  next_query = ffi_polar.next_inline_query
51
87
  break if next_query.nil?
@@ -53,24 +89,28 @@ module Oso
53
89
  begin
54
90
  Query.new(next_query, host: host).results.next
55
91
  rescue StopIteration
56
- raise InlineQueryFailedError
92
+ raise InlineQueryFailedError, next_query.source
57
93
  end
58
94
  end
59
95
  end
60
96
 
61
- # Query for a predicate, parsing it if necessary.
97
+ # Query for a Polar predicate or string.
62
98
  #
63
- # @param query [String or Predicate]
64
- # @return Enumerator of resulting bindings
65
- # @raise [Error] if the FFI call raises one.
99
+ # @overload query(query)
100
+ # @param query [String]
101
+ # @return [Enumerator] of resulting bindings
102
+ # @raise [Error] if the FFI call raises one.
103
+ # @overload query(query)
104
+ # @param query [Predicate]
105
+ # @return [Enumerator] of resulting bindings
106
+ # @raise [Error] if the FFI call raises one.
66
107
  def query(query)
67
- load_queued_files
68
108
  new_host = host.dup
69
109
  case query
70
110
  when String
71
111
  ffi_query = ffi_polar.new_query_from_str(query)
72
112
  when Predicate
73
- ffi_query = ffi_polar.new_query_from_term(new_host.to_polar_term(query))
113
+ ffi_query = ffi_polar.new_query_from_term(new_host.to_polar(query))
74
114
  else
75
115
  raise InvalidQueryTypeError
76
116
  end
@@ -81,54 +121,12 @@ module Oso
81
121
  #
82
122
  # @param name [String]
83
123
  # @param args [Array<Object>]
124
+ # @return [Enumerator] of resulting bindings
84
125
  # @raise [Error] if the FFI call raises one.
85
126
  def query_rule(name, *args)
86
127
  query(Predicate.new(name, args: args))
87
128
  end
88
129
 
89
- # Start a REPL session.
90
- #
91
- # @raise [Error] if the FFI call raises one.
92
- def repl(load: false) # rubocop:disable Metrics/MethodLength
93
- ARGV.map { |f| load_file(f) } if load
94
- load_queued_files
95
-
96
- loop do
97
- print('query> ')
98
- begin
99
- query = STDIN.readline.chomp.chomp(';')
100
- rescue EOFError
101
- return
102
- end
103
-
104
- begin
105
- ffi_query = ffi_polar.new_query_from_str(query)
106
- rescue ParseError => e
107
- puts("Parse error: " + e.to_s)
108
- next
109
- end
110
-
111
- begin
112
- results = Query.new(ffi_query, host: host).results.to_a
113
- rescue PolarRuntimeError => e
114
- puts(e.to_s)
115
- next
116
- end
117
-
118
- if results.empty?
119
- pp false
120
- else
121
- results.each do |result|
122
- if result.empty?
123
- pp true
124
- else
125
- pp result
126
- end
127
- end
128
- end
129
- end
130
- end
131
-
132
130
  # Register a Ruby class with Polar.
133
131
  #
134
132
  # @param cls [Class]
@@ -136,31 +134,98 @@ module Oso
136
134
  # @param from_polar [Proc]
137
135
  # @raise [InvalidConstructorError] if provided an invalid 'from_polar' constructor.
138
136
  def register_class(cls, name: nil, from_polar: nil)
139
- if block_given?
140
- from_polar = Proc.new
141
- end
137
+ from_polar = Proc.new if block_given?
142
138
  name = host.cache_class(cls, name: name, constructor: from_polar)
143
139
  register_constant(name, value: cls)
144
140
  end
145
141
 
146
142
  def register_constant(name, value:)
147
- ffi_polar.register_constant(name, value: host.to_polar_term(value))
143
+ ffi_polar.register_constant(name, value: host.to_polar(value))
148
144
  end
149
145
 
150
- # Load all queued files, flushing the {#load_queue}.
151
- def load_queued_files
152
- load_queue.reject! do |filename|
153
- File.open(filename) { |file| load_str(file.read, filename: filename) }
154
- true
155
- end
146
+ # Start a REPL session.
147
+ #
148
+ # @param files [Array<String>]
149
+ # @raise [Error] if the FFI call raises one.
150
+ def repl(files = [])
151
+ files.map { |f| load_file(f) }
152
+ prompt = "#{FG_BLUE}query>#{RESET} "
153
+ # Try loading the readline module from the Ruby stdlib. If we get a
154
+ # LoadError, fall back to the standard REPL with no readline support.
155
+ require 'readline'
156
+ repl_readline(prompt)
157
+ rescue LoadError
158
+ repl_standard(prompt)
156
159
  end
157
160
 
158
161
  private
159
162
 
160
163
  # @return [FFI::Polar]
161
164
  attr_reader :ffi_polar
162
- # @return [Array<String>]
163
- attr_reader :load_queue
165
+ # @return [Hash<String, String>]
166
+ attr_reader :loaded_names
167
+ # @return [Hash<String, String>]
168
+ attr_reader :loaded_contents
169
+
170
+ # The R and L in REPL for systems where readline is available.
171
+ def repl_readline(prompt)
172
+ while (buf = Readline.readline(prompt, true))
173
+ if /^\s*$/ =~ buf # Don't add empty entries to history.
174
+ Readline::HISTORY.pop
175
+ next
176
+ end
177
+ process_line(buf)
178
+ end
179
+ rescue Interrupt # rubocop:disable Lint/SuppressedException
180
+ end
181
+
182
+ # The R and L in REPL for systems where readline is not available.
183
+ def repl_standard(prompt)
184
+ loop do
185
+ puts prompt
186
+ begin
187
+ buf = $stdin.readline
188
+ rescue EOFError
189
+ return
190
+ end
191
+ process_line(buf)
192
+ end
193
+ rescue Interrupt # rubocop:disable Lint/SuppressedException
194
+ end
195
+
196
+ # Process a line of user input in the REPL.
197
+ #
198
+ # @param buf [String]
199
+ def process_line(buf) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/AbcSize
200
+ query = buf.chomp.chomp(';')
201
+ begin
202
+ ffi_query = ffi_polar.new_query_from_str(query)
203
+ rescue ParseError => e
204
+ print_error(e)
205
+ return
206
+ end
207
+
208
+ begin
209
+ results = Query.new(ffi_query, host: host).results.to_a
210
+ rescue PolarRuntimeError => e
211
+ print_error(e)
212
+ return
213
+ end
214
+
215
+ if results.empty?
216
+ puts false
217
+ else
218
+ results.each do |result|
219
+ if result.empty?
220
+ puts true
221
+ else
222
+ result.each do |variable, value|
223
+ puts "#{variable} = #{value.inspect}"
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
164
229
  end
165
230
  end
166
231
  end
@@ -3,11 +3,12 @@
3
3
  module Oso
4
4
  module Polar
5
5
  # A single Polar query.
6
- class Query
6
+ class Query # rubocop:disable Metrics/ClassLength
7
+ # @return [Enumerator]
7
8
  attr_reader :results
8
9
 
9
10
  # @param ffi_query [FFI::Query]
10
- # @param ffi_polar [FFI::Polar]
11
+ # @param host [Oso::Polar::Host]
11
12
  def initialize(ffi_query, host:)
12
13
  @calls = {}
13
14
  @ffi_query = ffi_query
@@ -38,21 +39,22 @@ module Oso
38
39
  #
39
40
  # @param method [#to_sym]
40
41
  # @param call_id [Integer]
41
- # @param instance [Hash]
42
+ # @param instance [Hash<String, Object>]
42
43
  # @param args [Array<Hash>]
43
44
  # @raise [InvalidCallError] if the method doesn't exist on the instance or
44
45
  # the args passed to the method are invalid.
45
- def register_call(method, call_id:, instance:, args:)
46
+ def register_call(attribute, call_id:, instance:, args:, kwargs:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
46
47
  return if calls.key?(call_id)
47
48
 
49
+ instance = host.to_ruby(instance)
48
50
  args = args.map { |a| host.to_ruby(a) }
49
- if instance['value'].key? 'ExternalInstance'
50
- instance_id = instance['value']['ExternalInstance']['instance_id']
51
- instance = host.get_instance(instance_id)
52
- else
53
- instance = host.to_ruby(instance)
54
- end
55
- result = instance.__send__(method, *args)
51
+ kwargs = Hash[kwargs.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
52
+ # The kwargs.empty? check is for Ruby < 2.7.
53
+ result = if kwargs.empty?
54
+ instance.__send__(attribute, *args)
55
+ else
56
+ instance.__send__(attribute, *args, **kwargs)
57
+ end
56
58
  result = [result].to_enum unless result.is_a? Enumerator # Call must be a generator.
57
59
  calls[call_id] = result.lazy
58
60
  rescue ArgumentError, NoMethodError
@@ -68,19 +70,18 @@ module Oso
68
70
  ffi_query.call_result(result, call_id: call_id)
69
71
  end
70
72
 
71
- # Retrieve the next result from a registered call and pass it to {#to_polar_term}.
73
+ # Retrieve the next result from a registered call and pass it to {#to_polar}.
72
74
  #
73
75
  # @param id [Integer]
74
76
  # @return [Hash]
75
77
  # @raise [StopIteration] if the call has been exhausted.
76
78
  def next_call_result(id)
77
- host.to_polar_term(calls[id].next)
79
+ host.to_polar(calls[id].next)
78
80
  end
79
81
 
80
- # Send result of predicate check across FFI boundary.
82
+ # Send application error across FFI boundary.
81
83
  #
82
- # @param result [Boolean]
83
- # @param call_id [Integer]
84
+ # @param message [String]
84
85
  # @raise [Error] if the FFI call raises one.
85
86
  def application_error(message)
86
87
  ffi_query.application_error(message)
@@ -92,10 +93,10 @@ module Oso
92
93
  # @param method [#to_sym]
93
94
  # @param args [Array<Hash>]
94
95
  # @param call_id [Integer]
95
- # @param instance_id [Integer]
96
+ # @param instance [Hash<String, Object>]
96
97
  # @raise [Error] if the FFI call raises one.
97
- def handle_call(method, call_id:, instance:, args:)
98
- register_call(method, call_id: call_id, instance: instance, args: args)
98
+ def handle_call(attribute, call_id:, instance:, args:, kwargs:)
99
+ register_call(attribute, call_id: call_id, instance: instance, args: args, kwargs: kwargs)
99
100
  result = JSON.dump(next_call_result(call_id))
100
101
  call_result(result, call_id: call_id)
101
102
  rescue InvalidCallError => e
@@ -105,12 +106,26 @@ module Oso
105
106
  call_result(nil, call_id: call_id)
106
107
  end
107
108
 
109
+ def handle_make_external(data) # rubocop:disable Metrics/AbcSize
110
+ id = data['instance_id']
111
+ raise DuplicateInstanceRegistrationError, id if host.instance? id
112
+
113
+ constructor = data['constructor']['value']
114
+ raise InvalidConstructorError unless constructor.key? 'Call'
115
+
116
+ cls_name = constructor['Call']['name']
117
+ args = constructor['Call']['args'].map { |arg| host.to_ruby(arg) }
118
+ kwargs = constructor['Call']['kwargs'] || {}
119
+ kwargs = Hash[kwargs.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
120
+ host.make_instance(cls_name, args: args, kwargs: kwargs, id: id)
121
+ end
122
+
108
123
  # Create a generator that can be polled to advance the query loop.
109
124
  #
110
125
  # @yieldparam [Hash<String, Object>]
111
126
  # @return [Enumerator]
112
127
  # @raise [Error] if any of the FFI calls raise one.
113
- def start # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
128
+ def start # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
114
129
  Enumerator.new do |yielder| # rubocop:disable Metrics/BlockLength
115
130
  loop do # rubocop:disable Metrics/BlockLength
116
131
  event = ffi_query.next_event
@@ -120,18 +135,14 @@ module Oso
120
135
  when 'Result'
121
136
  yielder << event.data['bindings'].transform_values { |v| host.to_ruby(v) }
122
137
  when 'MakeExternal'
123
- id = event.data['instance_id']
124
- raise DuplicateInstanceRegistrationError, id if host.instance? id
125
-
126
- cls_name = event.data['instance']['tag']
127
- fields = event.data['instance']['fields']['fields']
128
- host.make_instance(cls_name, fields: fields, id: id)
138
+ handle_make_external(event.data)
129
139
  when 'ExternalCall'
130
140
  call_id = event.data['call_id']
131
141
  instance = event.data['instance']
132
- method = event.data['attribute']
133
- args = event.data['args']
134
- handle_call(method, call_id: call_id, instance: instance, args: args)
142
+ attribute = event.data['attribute']
143
+ args = event.data['args'] || []
144
+ kwargs = event.data['kwargs'] || {}
145
+ handle_call(attribute, call_id: call_id, instance: instance, args: args, kwargs: kwargs)
135
146
  when 'ExternalIsSubSpecializer'
136
147
  instance_id = event.data['instance_id']
137
148
  left_tag = event.data['left_class_tag']
@@ -139,9 +150,9 @@ module Oso
139
150
  answer = host.subspecializer?(instance_id, left_tag: left_tag, right_tag: right_tag)
140
151
  question_result(answer, call_id: event.data['call_id'])
141
152
  when 'ExternalIsa'
142
- instance_id = event.data['instance_id']
153
+ instance = event.data['instance']
143
154
  class_tag = event.data['class_tag']
144
- answer = host.isa?(instance_id, class_tag: class_tag)
155
+ answer = host.isa?(instance, class_tag: class_tag)
145
156
  question_result(answer, call_id: event.data['call_id'])
146
157
  when 'ExternalUnify'
147
158
  left_instance_id = event.data['left_instance_id']
@@ -152,11 +163,11 @@ module Oso
152
163
  puts event.data['message'] if event.data['message']
153
164
  print 'debug> '
154
165
  begin
155
- input = STDIN.readline.chomp.chomp(';')
166
+ input = $stdin.readline.chomp.chomp(';')
156
167
  rescue EOFError
157
168
  next
158
169
  end
159
- command = JSON.dump(host.to_polar_term(input))
170
+ command = JSON.dump(host.to_polar(input))
160
171
  ffi_query.debug_command(command)
161
172
  else
162
173
  raise "Unhandled event: #{JSON.dump(event.inspect)}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Oso
4
- VERSION = '0.3.1'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency 'pry-byebug', '~> 3.9.0'
35
35
  spec.add_development_dependency 'rake', '~> 12.0'
36
36
  spec.add_development_dependency 'rspec', '~> 3.0'
37
- spec.add_development_dependency 'solargraph', '~> 0.39.8'
37
+ spec.add_development_dependency 'rubocop', '~> 0.89.1'
38
+ spec.add_development_dependency 'solargraph', '~> 0.39.14'
38
39
  spec.add_development_dependency 'yard', '~> 0.9.25'
39
40
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oso-oso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oso Security, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-31 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -66,20 +66,34 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.89.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.89.1
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: solargraph
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: 0.39.8
89
+ version: 0.39.14
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: 0.39.8
96
+ version: 0.39.14
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: yard
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +118,7 @@ extra_rdoc_files: []
104
118
  files:
105
119
  - ".gitignore"
106
120
  - ".rspec"
121
+ - ".rubocop.yml"
107
122
  - ".solargraph.yml"
108
123
  - Gemfile
109
124
  - Gemfile.lock
@@ -122,9 +137,11 @@ files:
122
137
  - lib/oso/polar/errors.rb
123
138
  - lib/oso/polar/ffi.rb
124
139
  - lib/oso/polar/ffi/error.rb
140
+ - lib/oso/polar/ffi/message.rb
125
141
  - lib/oso/polar/ffi/polar.rb
126
142
  - lib/oso/polar/ffi/query.rb
127
143
  - lib/oso/polar/ffi/query_event.rb
144
+ - lib/oso/polar/ffi/source.rb
128
145
  - lib/oso/polar/host.rb
129
146
  - lib/oso/polar/polar.rb
130
147
  - lib/oso/polar/predicate.rb