oso-oso 0.5.1 → 0.8.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: cf2c949f57a56b4849f24ed9593c78da58baa5a0
4
- data.tar.gz: 31ac99dcfa3bba1bbbee68474a3fe3702fb7484d
3
+ metadata.gz: c85bc41901e0ed5de32a11d192da0c26b79ac44c
4
+ data.tar.gz: 97f8718e22c0cee489fe7ed0db324d29fa1fb0a4
5
5
  SHA512:
6
- metadata.gz: f1708d82f2531d50456ddcadb967bd024414700808e81b440836eb344f542cfda46af651382a6445a098dce9cf1e1c63c6802df73e3f97713a819d0b07ef5aae
7
- data.tar.gz: d96b91d530555ed98d040c2d3c8e8e0adbdffa352cef6bd49c22e6f225c16bfa9f4c488d37468201e16e0950791581621737b94f1b9d5ffc8b8c4dffeaa4e656
6
+ metadata.gz: ac34d5b51b0f981965098265b2279b9b1f1b73488267e39fffe5eff3e74f484f8f46a9b03e86a6f6fd2326e23af7d93e5d139f57f807fa9e2560dd80392f6b04
7
+ data.tar.gz: d89cf2110f9e7e9e6a6a8070565a59bae49770545fe918fd3ebd7f3b408743446af77a152b4fa4e524aacd999f57d6b0403986adafc04bac6c06228e27d94d39
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- oso-oso (0.5.1)
4
+ oso-oso (0.8.0)
5
5
  ffi (~> 1.0)
6
6
 
7
7
  GEM
Binary file
Binary file
@@ -32,11 +32,13 @@ module Oso
32
32
  # Errors originating from this side of the FFI boundary.
33
33
 
34
34
  class UnregisteredClassError < PolarRuntimeError; end
35
- class MissingConstructorError < PolarRuntimeError; end
36
35
  class UnregisteredInstanceError < PolarRuntimeError; end
37
36
  class DuplicateInstanceRegistrationError < PolarRuntimeError; end
37
+
38
+ # TODO: I think this should probably have some arguments to say what the call is
38
39
  class InvalidCallError < PolarRuntimeError; end
39
40
  class InvalidConstructorError < PolarRuntimeError; end
41
+ class InvalidIteratorError < PolarRuntimeError; end
40
42
  class InvalidQueryTypeError < PolarRuntimeError; end
41
43
  class NullByteInPolarFileError < PolarRuntimeError; end
42
44
  class UnexpectedPolarTypeError < PolarRuntimeError; end
@@ -66,6 +68,12 @@ module Oso
66
68
  end
67
69
  end
68
70
 
71
+ class UnimplementedOperationError < PolarRuntimeError # rubocop:disable Style/Documentation
72
+ def initialize(operation)
73
+ super("#{operation} are unimplemented in the oso Ruby library")
74
+ end
75
+ end
76
+
69
77
  # Generic operational exception.
70
78
  class OperationalError < Error; end
71
79
  class UnknownError < OperationalError; end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module Oso
4
6
  module Polar
5
7
  module FFI
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module Oso
4
6
  module Polar
5
7
  module FFI
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module Oso
4
6
  module Polar
5
7
  module FFI
@@ -11,6 +13,7 @@ module Oso
11
13
 
12
14
  attach_function :new, :polar_new, [], FFI::Polar
13
15
  attach_function :load, :polar_load, [FFI::Polar, :string, :string], :int32
16
+ attach_function :clear_rules, :polar_clear_rules, [FFI::Polar], :int32
14
17
  attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar, :uint32], FFI::Query
15
18
  attach_function :new_id, :polar_get_external_id, [FFI::Polar], :uint64
16
19
  attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32], FFI::Query
@@ -39,6 +42,13 @@ module Oso
39
42
  raise FFI::Error.get if loaded.zero?
40
43
  end
41
44
 
45
+ # @raise [FFI::Error] if the FFI call returns an error.
46
+ def clear_rules
47
+ cleared = Rust.clear_rules(self)
48
+ process_messages
49
+ raise FFI::Error.get if cleared.zero?
50
+ end
51
+
42
52
  # @return [FFI::Query] if there are remaining inline queries.
43
53
  # @return [nil] if there are no remaining inline queries.
44
54
  # @raise [FFI::Error] if the FFI call returns an error.
@@ -84,7 +94,7 @@ module Oso
84
94
  # @param name [String]
85
95
  # @param value [Hash<String, Object>]
86
96
  # @raise [FFI::Error] if the FFI call returns an error.
87
- def register_constant(name, value:)
97
+ def register_constant(value, name:)
88
98
  registered = Rust.register_constant(self, name, JSON.dump(value))
89
99
  raise FFI::Error.get if registered.zero?
90
100
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module Oso
4
6
  module Polar
5
7
  module FFI
@@ -10,8 +10,6 @@ module Oso
10
10
  attr_reader :ffi_polar
11
11
  # @return [Hash<String, Class>]
12
12
  attr_reader :classes
13
- # @return [Hash<String, Object>]
14
- attr_reader :constructors
15
13
  # @return [Hash<Integer, Object>]
16
14
  attr_reader :instances
17
15
 
@@ -20,14 +18,12 @@ module Oso
20
18
  def initialize(ffi_polar)
21
19
  @ffi_polar = ffi_polar
22
20
  @classes = {}
23
- @constructors = {}
24
21
  @instances = {}
25
22
  end
26
23
 
27
24
  def initialize_copy(other)
28
25
  @ffi_polar = other.ffi_polar
29
26
  @classes = other.classes.dup
30
- @constructors = other.constructors.dup
31
27
  @instances = other.instances.dup
32
28
  end
33
29
 
@@ -44,38 +40,18 @@ module Oso
44
40
 
45
41
  # Store a Ruby class in the {#classes} cache.
46
42
  #
47
- # @param cls [Class] the class to cache
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.
43
+ # @param cls [Class] the class to cache.
44
+ # @param name [String] the name to cache the class as.
50
45
  # @return [String] the name the class is cached as.
51
- # @raise [UnregisteredClassError] if the class has not been registered.
52
- def cache_class(cls, name:, constructor:) # rubocop:disable Metrics/MethodLength
53
- name = cls.name if name.nil?
46
+ # @raise [DuplicateClassAliasError] if attempting to register a class
47
+ # under a previously-registered name.
48
+ def cache_class(cls, name:)
54
49
  raise DuplicateClassAliasError, name: name, old: get_class(name), new: cls if classes.key? name
55
50
 
56
51
  classes[name] = cls
57
- if constructor.nil?
58
- constructors[name] = :new
59
- elsif constructor.respond_to? :call
60
- constructors[name] = constructor
61
- else
62
- raise InvalidConstructorError
63
- end
64
52
  name
65
53
  end
66
54
 
67
- # Fetch a constructor from the {#constructors} cache.
68
- #
69
- # @param name [String]
70
- # @return [Symbol] if constructor is the default of `:new`.
71
- # @return [Proc] if a custom constructor was registered.
72
- # @raise [UnregisteredConstructorError] if the constructor has not been registered.
73
- def get_constructor(name)
74
- raise MissingConstructorError, name unless constructors.key? name
75
-
76
- constructors[name]
77
- end
78
-
79
55
  # Check if an instance exists in the {#instances} cache.
80
56
  #
81
57
  # @param id [Integer]
@@ -100,12 +76,12 @@ module Oso
100
76
  instances[id]
101
77
  end
102
78
 
103
- # Cache a Ruby instance in the {#instances} cache, fetching a {#new_id}
104
- # if one isn't provided.
79
+ # Cache a Ruby instance in the {#instances} cache, fetching a new id if
80
+ # one isn't provided.
105
81
  #
106
82
  # @param instance [Object]
107
- # @param id [Integer]
108
- # @return [Integer]
83
+ # @param id [Integer] the instance ID. Generated via FFI if not provided.
84
+ # @return [Integer] the instance ID.
109
85
  def cache_instance(instance, id: nil)
110
86
  id = ffi_polar.new_id if id.nil?
111
87
  instances[id] = instance
@@ -114,30 +90,17 @@ module Oso
114
90
 
115
91
  # Construct and cache a Ruby instance.
116
92
  #
117
- # @param cls_name [String]
118
- # @param initargs [Hash<String, Hash>]
119
- # @param id [Integer]
93
+ # @param cls_name [String] name of the instance's class.
94
+ # @param args [Array<Object>] positional args to the constructor.
95
+ # @param kwargs [Hash<String, Object>] keyword args to the constructor.
96
+ # @param id [Integer] the instance ID.
120
97
  # @raise [PolarRuntimeError] if instance construction fails.
121
- def make_instance(cls_name, initargs:, id:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
122
- constructor = get_constructor(cls_name)
123
- instance = if constructor == :new
124
- if initargs.empty?
125
- get_class(cls_name).__send__(:new)
126
- elsif initargs.is_a? Array
127
- get_class(cls_name).__send__(:new, *initargs)
128
- elsif initargs.is_a? Hash
129
- get_class(cls_name).__send__(:new, **initargs)
130
- else
131
- raise PolarRuntimeError, "Bad initargs: #{initargs}"
132
- end
133
- elsif initargs.empty?
134
- constructor.call
135
- elsif initargs.is_a? Array
136
- constructor.call(*initargs)
137
- elsif initargs.is_a? Hash
138
- constructor.call(**initargs)
98
+ # @return [Integer] the instance ID.
99
+ def make_instance(cls_name, args:, kwargs:, id:)
100
+ instance = if kwargs.empty? # This check is for Ruby < 2.7.
101
+ get_class(cls_name).__send__(:new, *args)
139
102
  else
140
- raise PolarRuntimeError, "Bad initargs: #{initargs}"
103
+ get_class(cls_name).__send__(:new, *args, **kwargs)
141
104
  end
142
105
  cache_instance(instance, id: id)
143
106
  rescue StandardError => e
@@ -153,9 +116,9 @@ module Oso
153
116
  # @return [Boolean]
154
117
  def subspecializer?(instance_id, left_tag:, right_tag:)
155
118
  mro = get_instance(instance_id).class.ancestors
156
- mro.index(get_class(left_tag)) < mro.index(get_class(right_tag))
157
- rescue StandardError
158
- false
119
+ left_index = mro.index(get_class(left_tag))
120
+ right_index = mro.index(get_class(right_tag))
121
+ left_index && right_index && left_index < right_index
159
122
  end
160
123
 
161
124
  # Check if instance is an instance of class.
@@ -167,8 +130,6 @@ module Oso
167
130
  instance = to_ruby(instance)
168
131
  cls = get_class(class_tag)
169
132
  instance.is_a? cls
170
- rescue PolarRuntimeError
171
- false
172
133
  end
173
134
 
174
135
  # Check if two instances unify
@@ -180,8 +141,6 @@ module Oso
180
141
  left_instance = get_instance(left_instance_id)
181
142
  right_instance = get_instance(right_instance_id)
182
143
  left_instance == right_instance
183
- rescue PolarRuntimeError
184
- false
185
144
  end
186
145
 
187
146
  # Turn a Ruby value into a Polar term that's ready to be sent across the
@@ -189,22 +148,29 @@ module Oso
189
148
  #
190
149
  # @param value [Object]
191
150
  # @return [Hash<String, Object>]
192
- def to_polar_term(value) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
151
+ def to_polar(value) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
193
152
  value = case true # rubocop:disable Lint/LiteralAsCondition
194
153
  when value.instance_of?(TrueClass) || value.instance_of?(FalseClass)
195
154
  { 'Boolean' => value }
196
155
  when value.instance_of?(Integer)
197
156
  { 'Number' => { 'Integer' => value } }
198
157
  when value.instance_of?(Float)
158
+ if value == Float::INFINITY
159
+ value = 'Infinity'
160
+ elsif value == -Float::INFINITY
161
+ value = '-Infinity'
162
+ elsif value.nan?
163
+ value = 'NaN'
164
+ end
199
165
  { 'Number' => { 'Float' => value } }
200
166
  when value.instance_of?(String)
201
167
  { 'String' => value }
202
168
  when value.instance_of?(Array)
203
- { 'List' => value.map { |el| to_polar_term(el) } }
169
+ { 'List' => value.map { |el| to_polar(el) } }
204
170
  when value.instance_of?(Hash)
205
- { 'Dictionary' => { 'fields' => value.transform_values { |v| to_polar_term(v) } } }
171
+ { 'Dictionary' => { 'fields' => value.transform_values { |v| to_polar(v) } } }
206
172
  when value.instance_of?(Predicate)
207
- { 'Call' => { 'name' => value.name, 'args' => value.args.map { |el| to_polar_term(el) } } }
173
+ { 'Call' => { 'name' => value.name, 'args' => value.args.map { |el| to_polar(el) } } }
208
174
  when value.instance_of?(Variable)
209
175
  # This is supported so that we can query for unbound variables
210
176
  { 'Variable' => value }
@@ -222,13 +188,28 @@ module Oso
222
188
  # @option data [Hash<String, Object>] :value
223
189
  # @return [Object]
224
190
  # @raise [UnexpectedPolarTypeError] if type cannot be converted to Ruby.
225
- def to_ruby(data) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
191
+ def to_ruby(data) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
226
192
  tag, value = data['value'].first
227
193
  case tag
228
194
  when 'String', 'Boolean'
229
195
  value
230
196
  when 'Number'
231
- value.values.first
197
+ num = value.values.first
198
+ if value.key? 'Float'
199
+ case num
200
+ when 'Infinity'
201
+ return Float::INFINITY
202
+ when '-Infinity'
203
+ return -Float::INFINITY
204
+ when 'NaN'
205
+ return Float::NAN
206
+ else
207
+ unless value['Float'].is_a? Float # rubocop:disable Metrics/BlockNesting
208
+ raise PolarRuntimeError, "Expected a floating point number, got \"#{value['Float']}\""
209
+ end
210
+ end
211
+ end
212
+ num
232
213
  when 'List'
233
214
  value.map { |el| to_ruby(el) }
234
215
  when 'Dictionary'
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
- require 'pp'
5
- require 'set'
6
- require 'digest/md5'
7
-
8
3
  # Missing Ruby type.
9
4
  module PolarBoolean; end
10
5
  # Monkey-patch Ruby true type.
@@ -43,6 +38,9 @@ module Oso
43
38
  @ffi_polar = FFI::Polar.create
44
39
  @host = Host.new(ffi_polar)
45
40
 
41
+ # Register global constants.
42
+ register_constant nil, name: 'nil'
43
+
46
44
  # Register built-in classes.
47
45
  register_class PolarBoolean, name: 'Boolean'
48
46
  register_class Integer
@@ -52,9 +50,12 @@ module Oso
52
50
  register_class String
53
51
  end
54
52
 
55
- # Replace the current Polar instance but retain all registered classes and constructors.
56
- def clear
57
- @ffi_polar = FFI::Polar.create
53
+ # Clear all rules and rule sources from the current Polar instance
54
+ #
55
+ # @return [self] for chaining.
56
+ def clear_rules
57
+ ffi_polar.clear_rules
58
+ self
58
59
  end
59
60
 
60
61
  # Load a Polar policy file.
@@ -62,6 +63,7 @@ module Oso
62
63
  # @param name [String]
63
64
  # @raise [PolarFileExtensionError] if provided filename has invalid extension.
64
65
  # @raise [PolarFileNotFoundError] if provided filename does not exist.
66
+ # @return [self] for chaining.
65
67
  def load_file(name)
66
68
  raise PolarFileExtensionError, name unless File.extname(name) == '.polar'
67
69
 
@@ -78,6 +80,7 @@ module Oso
78
80
  # @raise [NullByteInPolarFileError] if str includes a non-terminating null byte.
79
81
  # @raise [InlineQueryFailedError] on the first failed inline query.
80
82
  # @raise [Error] if any of the FFI calls raise one.
83
+ # @return [self] for chaining.
81
84
  def load_str(str, filename: nil) # rubocop:disable Metrics/MethodLength
82
85
  raise NullByteInPolarFileError if str.chomp("\0").include?("\0")
83
86
 
@@ -92,6 +95,7 @@ module Oso
92
95
  raise InlineQueryFailedError, next_query.source
93
96
  end
94
97
  end
98
+ self
95
99
  end
96
100
 
97
101
  # Query for a Polar predicate or string.
@@ -110,7 +114,7 @@ module Oso
110
114
  when String
111
115
  ffi_query = ffi_polar.new_query_from_str(query)
112
116
  when Predicate
113
- ffi_query = ffi_polar.new_query_from_term(new_host.to_polar_term(query))
117
+ ffi_query = ffi_polar.new_query_from_term(new_host.to_polar(query))
114
118
  else
115
119
  raise InvalidQueryTypeError
116
120
  end
@@ -129,18 +133,26 @@ module Oso
129
133
 
130
134
  # Register a Ruby class with Polar.
131
135
  #
132
- # @param cls [Class]
133
- # @param name [String]
134
- # @param from_polar [Proc]
135
- # @raise [InvalidConstructorError] if provided an invalid 'from_polar' constructor.
136
- def register_class(cls, name: nil, from_polar: nil)
137
- from_polar = Proc.new if block_given?
138
- name = host.cache_class(cls, name: name, constructor: from_polar)
139
- register_constant(name, value: cls)
136
+ # @param cls [Class] the class to register.
137
+ # @param name [String] the name to register the class as. Defaults to the name of the class.
138
+ # @raise [DuplicateClassAliasError] if attempting to register a class
139
+ # under a previously-registered name.
140
+ # @raise [FFI::Error] if the FFI call returns an error.
141
+ # @return [self] for chaining.
142
+ def register_class(cls, name: nil)
143
+ name = host.cache_class(cls, name: name || cls.name)
144
+ register_constant(cls, name: name)
140
145
  end
141
146
 
142
- def register_constant(name, value:)
143
- ffi_polar.register_constant(name, value: host.to_polar_term(value))
147
+ # Register a Ruby object with Polar.
148
+ #
149
+ # @param value [Object] the object to register.
150
+ # @param name [String] the name to register the object as.
151
+ # @return [self] for chaining.
152
+ # @raise [FFI::Error] if the FFI call returns an error.
153
+ def register_constant(value, name:)
154
+ ffi_polar.register_constant(host.to_polar(value), name: name)
155
+ self
144
156
  end
145
157
 
146
158
  # Start a REPL session.
@@ -162,10 +174,6 @@ module Oso
162
174
 
163
175
  # @return [FFI::Polar]
164
176
  attr_reader :ffi_polar
165
- # @return [Hash<String, String>]
166
- attr_reader :loaded_names
167
- # @return [Hash<String, String>]
168
- attr_reader :loaded_contents
169
177
 
170
178
  # The R and L in REPL for systems where readline is available.
171
179
  def repl_readline(prompt)
@@ -220,7 +228,7 @@ module Oso
220
228
  puts true
221
229
  else
222
230
  result.each do |variable, value|
223
- puts "#{variable} => #{value.inspect}"
231
+ puts "#{variable} = #{value.inspect}"
224
232
  end
225
233
  end
226
234
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module Oso
4
6
  module Polar
5
7
  # A single Polar query.
@@ -34,31 +36,6 @@ module Oso
34
36
  ffi_query.question_result(result, call_id: call_id)
35
37
  end
36
38
 
37
- # Register a Ruby method call, wrapping the call result in a generator if
38
- # it isn't already one.
39
- #
40
- # @param method [#to_sym]
41
- # @param call_id [Integer]
42
- # @param instance [Hash<String, Object>]
43
- # @param args [Array<Hash>]
44
- # @raise [InvalidCallError] if the method doesn't exist on the instance or
45
- # the args passed to the method are invalid.
46
- def register_call(attribute, call_id:, instance:, args:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
47
- return if calls.key?(call_id)
48
-
49
- instance = host.to_ruby(instance)
50
- if args.nil?
51
- result = instance.__send__(attribute)
52
- else
53
- args = args.map { |a| host.to_ruby(a) }
54
- result = instance.__send__(attribute, *args)
55
- end
56
- result = [result].to_enum unless result.is_a? Enumerator # Call must be a generator.
57
- calls[call_id] = result.lazy
58
- rescue ArgumentError, NoMethodError
59
- raise InvalidCallError
60
- end
61
-
62
39
  # Send next result of Ruby method call across FFI boundary.
63
40
  #
64
41
  # @param result [String]
@@ -68,13 +45,13 @@ module Oso
68
45
  ffi_query.call_result(result, call_id: call_id)
69
46
  end
70
47
 
71
- # Retrieve the next result from a registered call and pass it to {#to_polar_term}.
48
+ # Retrieve the next result from a registered call and pass it to {#to_polar}.
72
49
  #
73
50
  # @param id [Integer]
74
51
  # @return [Hash]
75
52
  # @raise [StopIteration] if the call has been exhausted.
76
53
  def next_call_result(id)
77
- host.to_polar_term(calls[id].next)
54
+ host.to_polar(calls[id].next)
78
55
  end
79
56
 
80
57
  # Send application error across FFI boundary.
@@ -93,17 +70,51 @@ module Oso
93
70
  # @param call_id [Integer]
94
71
  # @param instance [Hash<String, Object>]
95
72
  # @raise [Error] if the FFI call raises one.
96
- def handle_call(attribute, call_id:, instance:, args:)
97
- register_call(attribute, call_id: call_id, instance: instance, args: args)
98
- result = JSON.dump(next_call_result(call_id))
73
+ def handle_call(attribute, call_id:, instance:, args:, kwargs:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
74
+ instance = host.to_ruby(instance)
75
+ args = args.map { |a| host.to_ruby(a) }
76
+ kwargs = Hash[kwargs.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
77
+ # The kwargs.empty? check is for Ruby < 2.7.
78
+ result = if kwargs.empty?
79
+ instance.__send__(attribute, *args)
80
+ else
81
+ instance.__send__(attribute, *args, **kwargs)
82
+ end
83
+ result = JSON.dump(host.to_polar(result))
99
84
  call_result(result, call_id: call_id)
100
- rescue InvalidCallError => e
85
+ rescue ArgumentError, NoMethodError => e
101
86
  application_error(e.message)
102
87
  call_result(nil, call_id: call_id)
88
+ end
89
+
90
+ def handle_next_external(call_id, iterable)
91
+ unless calls.key? call_id
92
+ value = host.to_ruby iterable
93
+ raise InvalidIteratorError unless value.is_a? Enumerable
94
+
95
+ calls[call_id] = value.lazy
96
+ end
97
+
98
+ result = JSON.dump(next_call_result(call_id))
99
+ call_result(result, call_id: call_id)
103
100
  rescue StopIteration
104
101
  call_result(nil, call_id: call_id)
105
102
  end
106
103
 
104
+ def handle_make_external(data) # rubocop:disable Metrics/AbcSize
105
+ id = data['instance_id']
106
+ raise DuplicateInstanceRegistrationError, id if host.instance? id
107
+
108
+ constructor = data['constructor']['value']
109
+ raise InvalidConstructorError unless constructor.key? 'Call'
110
+
111
+ cls_name = constructor['Call']['name']
112
+ args = constructor['Call']['args'].map { |arg| host.to_ruby(arg) }
113
+ kwargs = constructor['Call']['kwargs'] || {}
114
+ kwargs = Hash[kwargs.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
115
+ host.make_instance(cls_name, args: args, kwargs: kwargs, id: id)
116
+ end
117
+
107
118
  # Create a generator that can be polled to advance the query loop.
108
119
  #
109
120
  # @yieldparam [Hash<String, Object>]
@@ -119,27 +130,14 @@ module Oso
119
130
  when 'Result'
120
131
  yielder << event.data['bindings'].transform_values { |v| host.to_ruby(v) }
121
132
  when 'MakeExternal'
122
- id = event.data['instance_id']
123
- raise DuplicateInstanceRegistrationError, id if host.instance? id
124
-
125
- constructor = event.data['constructor']['value']
126
- if constructor.key? 'InstanceLiteral'
127
- cls_name = constructor['InstanceLiteral']['tag']
128
- fields = constructor['InstanceLiteral']['fields']['fields']
129
- initargs = Hash[fields.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
130
- elsif constructor.key? 'Call'
131
- cls_name = constructor['Call']['name']
132
- initargs = constructor['Call']['args'].map { |arg| host.to_ruby(arg) }
133
- else
134
- raise InvalidConstructorError
135
- end
136
- host.make_instance(cls_name, initargs: initargs, id: id)
133
+ handle_make_external(event.data)
137
134
  when 'ExternalCall'
138
135
  call_id = event.data['call_id']
139
136
  instance = event.data['instance']
140
137
  attribute = event.data['attribute']
141
- args = event.data['args']
142
- handle_call(attribute, call_id: call_id, instance: instance, args: args)
138
+ args = event.data['args'] || []
139
+ kwargs = event.data['kwargs'] || {}
140
+ handle_call(attribute, call_id: call_id, instance: instance, args: args, kwargs: kwargs)
143
141
  when 'ExternalIsSubSpecializer'
144
142
  instance_id = event.data['instance_id']
145
143
  left_tag = event.data['left_class_tag']
@@ -164,8 +162,14 @@ module Oso
164
162
  rescue EOFError
165
163
  next
166
164
  end
167
- command = JSON.dump(host.to_polar_term(input))
165
+ command = JSON.dump(host.to_polar(input))
168
166
  ffi_query.debug_command(command)
167
+ when 'ExternalOp'
168
+ raise UnimplementedOperationError, 'comparison operators'
169
+ when 'NextExternal'
170
+ call_id = event.data['call_id']
171
+ iterable = event.data['iterable']
172
+ handle_next_external(call_id, iterable)
169
173
  else
170
174
  raise "Unhandled event: #{JSON.dump(event.inspect)}"
171
175
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Oso
4
- VERSION = '0.5.1'
4
+ VERSION = '0.8.0'
5
5
  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.5.1
4
+ version: 0.8.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-09-01 00:00:00.000000000 Z
11
+ date: 2020-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi