jsonata 2.2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,600 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) Copyright IBM Corp. 2016, 2018 All Rights Reserved
4
+ # Project name: JSONata
5
+ # This project is licensed under the MIT License, see LICENSE
6
+
7
+ # Port of jsonata-js src/utils.js
8
+ #
9
+ # In addition to the functions in utils.js, this file contains the helpers that
10
+ # emulate JavaScript semantics that the rest of the port depends on:
11
+ #
12
+ # * `nil` represents JavaScript `undefined`, and `Jsonata::NULL` represents JSON `null`.
13
+ # * `Jsonata::JArray` is an Array that can carry the extra properties that
14
+ # jsonata-js attaches to arrays (sequence, keepSingleton, tupleStream, cons, outerWrapper).
15
+ # * JSON objects are Hashes with String keys.
16
+
17
+ module Jsonata
18
+ # Represents the JSON value `null` (as distinct from `nil`, which represents `undefined`)
19
+ NULL = Object.new
20
+ class << NULL
21
+ def inspect = 'Jsonata::NULL'
22
+ def to_s = 'null'
23
+ def to_json(*) = 'null'
24
+ end
25
+ NULL.freeze
26
+
27
+ # An Array that carries the additional properties that jsonata-js sets on arrays
28
+ class JArray < Array
29
+ attr_accessor :sequence, :keep_singleton, :tuple_stream, :cons, :outer_wrapper, :max_length
30
+
31
+ def push(*items)
32
+ if max_length && length + items.length > max_length
33
+ raise Jsonata::Error.new('D2015', :value => max_length)
34
+ end
35
+
36
+ super
37
+ end
38
+ alias << push
39
+ end
40
+
41
+ module Utils
42
+ module_function
43
+
44
+ # Check if value is a finite number
45
+ # @param n - number to evaluate
46
+ # @returns true if n is a finite number
47
+ def numeric?(n)
48
+ is_num = false
49
+ if n.kind_of?(Numeric)
50
+ is_num = !(n.kind_of?(Float) && n.nan?)
51
+ if is_num && n.kind_of?(Float) && n.infinite?
52
+ raise Jsonata::Error.new('D1001', :value => n)
53
+ end
54
+ end
55
+ is_num
56
+ end
57
+
58
+ # Returns true if the arg is an array of strings
59
+ def array_of_strings?(arg)
60
+ result = false
61
+ if arg.kind_of?(Array)
62
+ result = arg.all?(String)
63
+ end
64
+ result
65
+ end
66
+
67
+ # Returns true if the arg is an array of numbers
68
+ def array_of_numbers?(arg)
69
+ result = false
70
+ if arg.kind_of?(Array)
71
+ result = arg.all? { |item| numeric?(item) }
72
+ end
73
+ result
74
+ end
75
+
76
+ # Tests if a value is a sequence
77
+ def sequence?(value)
78
+ value.kind_of?(JArray) && value.sequence == true
79
+ end
80
+
81
+ # @returns true if it is a function (lambda or built-in)
82
+ def function?(arg)
83
+ arg.kind_of?(Lambda) || arg.kind_of?(NativeFunction) || callable?(arg)
84
+ end
85
+
86
+ # Equivalent of JavaScript `typeof arg === 'function'`
87
+ def callable?(arg)
88
+ arg.kind_of?(Proc) || arg.kind_of?(Method) || arg.kind_of?(Closure)
89
+ end
90
+
91
+ # Returns the arity (number of arguments) of the function
92
+ def get_function_arity(func)
93
+ case func
94
+ when Closure then func.arity
95
+ when NativeFunction then parameter_arity(func.implementation)
96
+ when Lambda then func.arguments.length
97
+ else parameter_arity(func)
98
+ end
99
+ end
100
+
101
+ # Equivalent of JavaScript Function.length - the number of declared parameters
102
+ # before the first one with a default value
103
+ def parameter_arity(callable)
104
+ callable.parameters.take_while { |type, _| type == :req }.length
105
+ end
106
+
107
+ # Names of the declared parameters of a Ruby callable (used for partial application of native functions)
108
+ NON_POSITIONAL_PARAMETER_TYPES = %i[block keyrest key keyreq].freeze
109
+
110
+ def parameter_names(callable)
111
+ callable.parameters.filter_map { |type, name| name.to_s unless NON_POSITIONAL_PARAMETER_TYPES.include?(type) }
112
+ end
113
+
114
+ # Invokes a Ruby callable with JavaScript calling semantics - missing arguments are
115
+ # passed as undefined (nil), and extraneous arguments are dropped.
116
+ # If `receiver` is given and the callable is an UnboundMethod, it is bound to the receiver
117
+ # (the equivalent of Function.prototype.apply(this, args))
118
+ def invoke(callable, args, receiver = nil)
119
+ required, max = invoke_arity(callable)
120
+ unless max.nil?
121
+ args = args.take(max) if args.length > max
122
+ args += Array.new(required - args.length) if args.length < required
123
+ end
124
+ if callable.kind_of?(UnboundMethod)
125
+ callable.bind_call(receiver, *args)
126
+ else
127
+ callable.call(*args)
128
+ end
129
+ end
130
+
131
+ INVOKE_ARITY_CACHE = ObjectSpace::WeakMap.new
132
+
133
+ # The number of required parameters, and the maximum number of arguments (nil if unlimited) of a callable
134
+ def invoke_arity(callable)
135
+ cached = INVOKE_ARITY_CACHE[callable]
136
+ return cached if cached
137
+
138
+ params = callable.parameters
139
+ arity = if params.any? { |type, _| type == :rest }
140
+ [0, nil]
141
+ else
142
+ required = params.count { |type, _| type == :req }
143
+ [required, required + params.count { |type, _| type == :opt }]
144
+ end
145
+ INVOKE_ARITY_CACHE[callable] = arity.freeze
146
+ end
147
+
148
+ # Tests whether arg is a lambda function
149
+ def lambda?(arg)
150
+ arg.kind_of?(Lambda)
151
+ end
152
+
153
+ # Compares two values for equality
154
+ # @returns true if they are deep equal
155
+ def deep_equal?(lhs, rhs)
156
+ return true if lhs.equal?(rhs)
157
+
158
+ if lhs.kind_of?(Array) && rhs.kind_of?(Array)
159
+ # both arrays (or sequences)
160
+ # must be the same length
161
+ return false if lhs.length != rhs.length
162
+
163
+ # must contain same values in same order
164
+ lhs.each_index do |ii|
165
+ return false unless deep_equal?(lhs[ii], rhs[ii])
166
+ end
167
+ return true
168
+ end
169
+ if lhs.kind_of?(Hash) && rhs.kind_of?(Hash)
170
+ # both objects
171
+ # must have the same set of keys (in any order)
172
+ return false if lhs.length != rhs.length
173
+
174
+ lkeys = lhs.keys.sort
175
+ rkeys = rhs.keys.sort
176
+ return false if lkeys != rkeys
177
+
178
+ # must have the same values
179
+ lkeys.each do |key|
180
+ return false unless deep_equal?(lhs[key], rhs[key])
181
+ end
182
+ return true
183
+ end
184
+ return false if lhs.kind_of?(Array) || lhs.kind_of?(Hash) || rhs.kind_of?(Array) || rhs.kind_of?(Hash)
185
+
186
+ strict_equal?(lhs, rhs)
187
+ end
188
+
189
+ # Equivalent of the JavaScript `===` operator
190
+ def strict_equal?(lhs, rhs)
191
+ if lhs.kind_of?(Numeric) && rhs.kind_of?(Numeric)
192
+ lhs == rhs
193
+ elsif lhs.kind_of?(String) && rhs.kind_of?(String)
194
+ lhs == rhs
195
+ else
196
+ lhs.equal?(rhs)
197
+ end
198
+ end
199
+
200
+ # converts a string to an array of characters
201
+ def string_to_array(str)
202
+ str.chars
203
+ end
204
+
205
+ # @returns the keys (property names) of the object
206
+ def keys(arg)
207
+ case arg
208
+ when Hash then arg.keys
209
+ when Array then (0...arg.length).map(&:to_s)
210
+ else []
211
+ end
212
+ end
213
+
214
+ ##################################################################
215
+ # JavaScript emulation helpers (not part of utils.js)
216
+ ##################################################################
217
+
218
+ # Equivalent of JavaScript `typeof value`
219
+ def typeof(value)
220
+ case value
221
+ when nil then 'undefined'
222
+ when String then 'string'
223
+ when Numeric then 'number'
224
+ when true, false then 'boolean'
225
+ else
226
+ callable?(value) ? 'function' : 'object'
227
+ end
228
+ end
229
+
230
+ # Equivalent of JavaScript truthiness
231
+ def truthy?(value)
232
+ case value
233
+ when nil, false, NULL, '' then false
234
+ when Numeric then !(value.zero? || (value.kind_of?(Float) && value.nan?))
235
+ else true
236
+ end
237
+ end
238
+
239
+ # Converts a numeric result of a floating point operation to an Integer, if it is integral
240
+ def normalize_number(num)
241
+ if num.kind_of?(Float) && num.finite? && num == num.truncate && num.abs < 9_007_199_254_740_992
242
+ num.to_i
243
+ else
244
+ num
245
+ end
246
+ end
247
+
248
+ # Equivalent of JavaScript Number.isInteger
249
+ def integer?(value)
250
+ value.kind_of?(Integer) || (value.kind_of?(Float) && value.finite? && value == value.truncate)
251
+ end
252
+
253
+ # Equivalent of the JavaScript Number::toString() algorithm
254
+ def number_to_s(num)
255
+ if num.kind_of?(Integer)
256
+ return num.to_s if num.abs < 10**21
257
+
258
+ num = num.to_f
259
+ end
260
+ return 'NaN' if num.nan?
261
+ return (num.positive? ? 'Infinity' : '-Infinity') if num.infinite?
262
+ return '0' if num.zero?
263
+
264
+ digits, point = shortest_digits(num.abs)
265
+ k = digits.length
266
+ str = if point.between?(k, 21)
267
+ digits + ('0' * (point - k))
268
+ elsif point.positive? && point <= 21
269
+ "#{digits[0, point]}.#{digits[point..]}"
270
+ elsif point > -6 && point <= 0
271
+ "0.#{'0' * -point}#{digits}"
272
+ else
273
+ e = point - 1
274
+ exp = e.negative? ? "-#{-e}" : "+#{e}"
275
+ k == 1 ? "#{digits}e#{exp}" : "#{digits[0]}.#{digits[1..]}e#{exp}"
276
+ end
277
+ num.negative? ? "-#{str}" : str
278
+ end
279
+
280
+ # Returns the shortest round-trip decimal digits of a positive finite float,
281
+ # and the position of the decimal point relative to the start of the digits
282
+ def shortest_digits(num)
283
+ mantissa, exp = num.to_s.split('e')
284
+ int_part, frac_part = mantissa.split('.')
285
+ digits = int_part + (frac_part || '')
286
+ point = int_part.length + exp.to_i
287
+ stripped = digits.sub(/\A0+/, '')
288
+ point -= digits.length - stripped.length
289
+ digits = stripped.sub(/0+\z/, '')
290
+ [digits, point]
291
+ end
292
+
293
+ # Equivalent of JavaScript Number.prototype.toFixed
294
+ def to_fixed(num, digits)
295
+ return number_to_s(num) if num.kind_of?(Float) && !num.finite?
296
+ return number_to_s(num) if num.abs >= 1e21
297
+
298
+ negative = num.negative?
299
+ scaled = (num.abs.to_r * (10**digits)).round(:half => :up)
300
+ str = scaled.to_s
301
+ if digits.positive?
302
+ str = str.rjust(digits + 1, '0')
303
+ str = "#{str[0...-digits]}.#{str[-digits..]}"
304
+ end
305
+ negative && scaled != 0 ? "-#{str}" : str
306
+ end
307
+
308
+ # Equivalent of JavaScript Number.prototype.toPrecision, followed by conversion back to a number
309
+ def to_precision(num, precision)
310
+ return num if num.zero? || !num.finite?
311
+
312
+ exact = num.abs.to_r
313
+ e = Math.log10(num.abs).floor
314
+ # correct for floating point inaccuracies in log10
315
+ e += 1 while exact >= Rational(10)**(e + 1)
316
+ e -= 1 while exact < Rational(10)**e
317
+ scale = Rational(10)**(precision - 1 - e)
318
+ n = (exact * scale).round(:half => :up)
319
+ result = (n / scale).to_f
320
+ num.negative? ? -result : result
321
+ end
322
+
323
+ # Equivalent of JavaScript parseInt(str, 10) - returns nil (NaN) if no digits
324
+ def parse_int(str)
325
+ m = /\A\s*([-+]?\d+)/.match(str.to_s)
326
+ m ? m[1].to_i : nil
327
+ end
328
+
329
+ # Equivalent of Array.prototype.slice / String.prototype.slice
330
+ def js_slice(obj, start, end_pos = nil)
331
+ len = obj.length
332
+ relative = lambda do |val|
333
+ val = val.to_f
334
+ return 0 if val.nan?
335
+
336
+ val = val.truncate if val.finite?
337
+ val = len + val if val.negative?
338
+ val.clamp(0, len).to_i
339
+ end
340
+ from = relative.call(start)
341
+ to = end_pos.nil? ? len : relative.call(end_pos)
342
+ return obj.kind_of?(String) ? +'' : [] if to <= from
343
+
344
+ obj[from...to]
345
+ end
346
+
347
+ # Equivalent of String.prototype.split(separator, limit) for a string separator
348
+ def js_split(str, separator, limit = nil)
349
+ result = if separator == ''
350
+ str.chars
351
+ else
352
+ parts = []
353
+ position = 0
354
+ index = str.index(separator, position)
355
+ until index.nil?
356
+ parts.push(str[position...index])
357
+ position = index + separator.length
358
+ index = str.index(separator, position)
359
+ end
360
+ parts.push(str[position..])
361
+ parts
362
+ end
363
+ limit.nil? ? result : result.take(limit.to_i)
364
+ end
365
+
366
+ # Equivalent of the JavaScript Number(str) conversion
367
+ def js_number(str)
368
+ s = str.strip
369
+ return 0 if s.empty?
370
+
371
+ case s
372
+ when /\A0[xX][0-9a-fA-F]+\z/, /\A0[oO][0-7]+\z/, /\A0[bB][01]+\z/
373
+ Integer(s)
374
+ when /\A[-+]?(\d+\.?\d*([eE][-+]?\d+)?|\.\d+([eE][-+]?\d+)?)\z/
375
+ normalize_number(Float(s))
376
+ when /\A[-+]?Infinity\z/
377
+ s.start_with?('-') ? -Float::INFINITY : Float::INFINITY
378
+ else
379
+ Float::NAN
380
+ end
381
+ end
382
+
383
+ class URIError < StandardError; end
384
+
385
+ URI_UNRESERVED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()"
386
+ URI_RESERVED = ';/?:@&=+$,#'
387
+
388
+ # Equivalent of encodeURI / encodeURIComponent
389
+ # @param unescaped - the set of characters that are not escaped
390
+ def encode_uri(str, unescaped)
391
+ raise URIError unless str.valid_encoding?
392
+
393
+ str.each_char.map do |ch|
394
+ if unescaped.include?(ch)
395
+ ch
396
+ else
397
+ ch.bytes.map { |b| '%%%02X' % b }.join
398
+ end
399
+ end.join
400
+ end
401
+
402
+ # Equivalent of decodeURI / decodeURIComponent
403
+ # @param reserved - the set of characters whose escape sequences are not decoded
404
+ def decode_uri(str, reserved)
405
+ result = +''
406
+ k = 0
407
+ length = str.length
408
+ while k < length
409
+ ch = str[k]
410
+ if ch != '%'
411
+ result << ch
412
+ k += 1
413
+ next
414
+ end
415
+ start = k
416
+ raise URIError if k + 2 >= length || !str[k + 1, 2].match?(/\A\h\h\z/)
417
+
418
+ b = str[k + 1, 2].to_i(16)
419
+ k += 2
420
+ if b < 0x80
421
+ c = b.chr
422
+ result << (reserved.include?(c) ? str[start..k] : c)
423
+ else
424
+ n = if (b & 0xE0) == 0xC0 then 2
425
+ elsif (b & 0xF0) == 0xE0 then 3
426
+ elsif (b & 0xF8) == 0xF0 then 4
427
+ else raise URIError
428
+ end
429
+ bytes = [b]
430
+ (n - 1).times do
431
+ k += 1
432
+ raise URIError if k + 2 >= length || str[k] != '%' || !str[k + 1, 2].match?(/\A\h\h\z/)
433
+
434
+ byte = str[k + 1, 2].to_i(16)
435
+ raise URIError if (byte & 0xC0) != 0x80
436
+
437
+ bytes.push(byte)
438
+ k += 2
439
+ end
440
+ decoded = bytes.pack('C*').force_encoding(Encoding::UTF_8)
441
+ raise URIError unless decoded.valid_encoding?
442
+
443
+ result << decoded
444
+ end
445
+ k += 1
446
+ end
447
+ result
448
+ end
449
+
450
+ # Equivalent of String.fromCharCode for a single code point.
451
+ # Lone surrogates are encoded as (invalid) UTF-8 bytes, so that they can be detected later
452
+ def from_char_code(codepoint)
453
+ [codepoint].pack('U').force_encoding(Encoding::UTF_8)
454
+ end
455
+
456
+ # Translates a JavaScript regular expression into a Ruby Regexp
457
+ # @param source - the JavaScript regex pattern
458
+ # @param flags - the JavaScript regex flags (only 'i' and 'm' are significant)
459
+ def js_regexp(source, flags = '')
460
+ multiline = flags.include?('m')
461
+ out = +''
462
+ in_class = false
463
+ i = 0
464
+ while i < source.length
465
+ ch = source[i]
466
+ if ch == '\\'
467
+ out << ch << (source[i + 1] || '')
468
+ i += 2
469
+ next
470
+ end
471
+ if in_class
472
+ in_class = false if ch == ']'
473
+ out << ch
474
+ elsif ch == '['
475
+ if source[i + 1] == ']'
476
+ # empty class - matches nothing
477
+ out << '(?!)'
478
+ i += 2
479
+ next
480
+ elsif source[i + 1, 2] == '^]'
481
+ # negated empty class - matches anything
482
+ out << '[\s\S]'
483
+ i += 3
484
+ next
485
+ end
486
+ in_class = true
487
+ out << ch
488
+ # a ']' immediately after '[' or '[^' is a literal in Ruby but closes the class in JS
489
+ elsif ch == '^' && !multiline
490
+ out << '\A'
491
+ elsif ch == '$' && !multiline
492
+ out << '\z'
493
+ else
494
+ out << ch
495
+ end
496
+ i += 1
497
+ end
498
+ options = flags.include?('i') ? Regexp::IGNORECASE : 0
499
+ Regexp.new("(?a)#{out}", options)
500
+ end
501
+
502
+ # Converts a Ruby value supplied by the caller into the representation used internally:
503
+ # Symbol keys are converted to Strings and nil is converted to NULL
504
+ def from_ruby(value)
505
+ case value
506
+ when nil then NULL
507
+ when Hash then value.each_with_object({}) { |(k, v), h| h[k.kind_of?(Symbol) ? k.to_s : k] = from_ruby(v) }
508
+ when JArray then value
509
+ when Array then value.map { |v| from_ruby(v) }
510
+ when Symbol then value.to_s
511
+ else value
512
+ end
513
+ end
514
+
515
+ # Converts an internal value into a plain Ruby value for the caller:
516
+ # NULL is converted to nil, and JArrays to plain Arrays
517
+ def to_ruby(value)
518
+ case value
519
+ when NULL then nil
520
+ when Hash then value.transform_values { |v| to_ruby(v) }
521
+ when Array then value.map { |v| to_ruby(v) }
522
+ else value
523
+ end
524
+ end
525
+
526
+ # JSON.stringify with JavaScript semantics
527
+ # @param replacer - optional proc(value) returning the replaced value
528
+ # @param space - number of spaces to indent
529
+ def json_stringify(value, replacer = nil, space = 0)
530
+ value = replacer.call(value) if replacer
531
+ stringify_value(value, replacer, space, '')
532
+ end
533
+
534
+ def stringify_value(value, replacer, space, indent)
535
+ case value
536
+ when nil then nil
537
+ when NULL, true, false then value == NULL ? 'null' : value.to_s
538
+ when String then quote_json_string(value)
539
+ when Numeric
540
+ value.kind_of?(Float) && !value.finite? ? 'null' : number_to_s(value)
541
+ when Array
542
+ return '[]' if value.empty?
543
+
544
+ inner = indent + (' ' * space)
545
+ items = value.map do |item|
546
+ item = replacer.call(item) if replacer
547
+ stringify_value(item, replacer, space, inner) || 'null'
548
+ end
549
+ if space.positive?
550
+ "[\n#{inner}#{items.join(",\n#{inner}")}\n#{indent}]"
551
+ else
552
+ "[#{items.join(',')}]"
553
+ end
554
+ when Hash
555
+ inner = indent + (' ' * space)
556
+ sep = space.positive? ? ': ' : ':'
557
+ items = value.filter_map do |k, v|
558
+ v = replacer.call(v) if replacer
559
+ str = stringify_value(v, replacer, space, inner)
560
+ "#{quote_json_string(k.to_s)}#{sep}#{str}" unless str.nil?
561
+ end
562
+ return '{}' if items.empty?
563
+
564
+ if space.positive?
565
+ "{\n#{inner}#{items.join(",\n#{inner}")}\n#{indent}}"
566
+ else
567
+ "{#{items.join(',')}}"
568
+ end
569
+ else
570
+ callable?(value) ? nil : '{}'
571
+ end
572
+ end
573
+
574
+ JSON_ESCAPES = {'"' => '\\"', '\\' => '\\\\', "\b" => '\\b', "\f" => '\\f', "\n" => '\\n', "\r" => '\\r', "\t" => '\\t'}.freeze
575
+
576
+ def quote_json_string(str)
577
+ unless str.valid_encoding?
578
+ # lone surrogates (see #from_char_code) are escaped, as they are by JSON.stringify
579
+ marked = str.scrub { |bytes| "\uFFFF#{bytes.unpack1('H*')}\uFFFF" }
580
+ return quote_json_string(marked).gsub(/(?:\uFFFF\h+\uFFFF)+/) do |run|
581
+ bytes = run.scan(/\h\h/).map(&:hex)
582
+ bytes.each_slice(3).map do |b0, b1, b2|
583
+ codepoint = ((b0 & 0x0F) << 12) | (((b1 || 0) & 0x3F) << 6) | ((b2 || 0) & 0x3F)
584
+ '\\u%04x' % codepoint
585
+ end.join
586
+ end
587
+ end
588
+
589
+ escaped = str.gsub(/["\\\u0000-\u001f]/) do |ch|
590
+ JSON_ESCAPES[ch] || ('\\u%04x' % ch.ord)
591
+ end
592
+ "\"#{escaped}\""
593
+ end
594
+
595
+ # Equivalent of Math.floor - the result is an Integer only if it can be represented exactly
596
+ def js_floor(num)
597
+ num.kind_of?(Integer) ? num : normalize_number(num.floor.to_f)
598
+ end
599
+ end
600
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsonata
4
+ VERSION = '2.2.2.1'
5
+ end
data/lib/jsonata.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'jsonata/version'
4
+ require_relative 'jsonata/error'
5
+ require_relative 'jsonata/utils'
6
+ require_relative 'jsonata/signature'
7
+ require_relative 'jsonata/parser'
8
+ require_relative 'jsonata/functions'
9
+ require_relative 'jsonata/datetime'
10
+ require_relative 'jsonata/evaluator'
11
+
12
+ # JSONata - JSON query and transformation language
13
+ #
14
+ # This is a Ruby port of jsonata-js (https://github.com/jsonata-js/jsonata).
15
+ #
16
+ # @example Basic usage
17
+ # require 'jsonata'
18
+ #
19
+ # data = { example: [{ value: 4 }, { value: 7 }, { value: 13 }] }
20
+ # expression = Jsonata.expression('$sum(example.value)')
21
+ # result = expression.evaluate(data) # => 24
22
+ #
23
+ # @see http://jsonata.org/ JSONata official website
24
+ # @see http://docs.jsonata.org/ JSONata documentation
25
+ module Jsonata
26
+ class << self
27
+ # Compile a JSONata expression (jsonata-js: `jsonata(expr, options)`)
28
+ #
29
+ # @param expr [String] the JSONata expression
30
+ # @param options [Hash] see Jsonata::Expression#initialize
31
+ # @return [Expression] a compiled JSONata expression
32
+ # @raise [Jsonata::Error] if the expression is invalid
33
+ def expression(expr, options = nil)
34
+ Expression.new(expr, options)
35
+ end
36
+
37
+ # Evaluate a JSONata expression against data (convenience method)
38
+ #
39
+ # @param expr [String] the JSONata expression
40
+ # @param data [Hash, Array, Object] the data to query
41
+ # @param bindings [Hash] variable bindings
42
+ # @return [Object] the result of the evaluation
43
+ def evaluate(expr, data = nil, bindings = nil)
44
+ expression(expr).evaluate(data, bindings)
45
+ end
46
+ end
47
+ end