jmespath 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jmespath might be problematic. Click here for more details.

@@ -37,24 +37,19 @@ module JMESPath
37
37
  #
38
38
  # @option options [Parser,CachingParser] :parser
39
39
  #
40
- # @option options [Interpreter] :interpreter
41
- #
42
40
  def initialize(options = {})
43
41
  @parser = options[:parser] || default_parser(options)
44
- @interpreter = options[:interpreter] || TreeInterpreter.new
45
42
  end
46
43
 
47
44
  # @return [Parser, CachingParser]
48
45
  attr_reader :parser
49
46
 
50
- # @return [Interpreter]
51
- attr_reader :interpreter
52
-
53
47
  # @param [String<JMESPath>] expression
54
48
  # @param [Hash] data
55
49
  # @return [Mixed,nil]
56
50
  def search(expression, data)
57
- @interpreter.visit(@parser.parse(expression), data)
51
+ optimized_expression = @parser.parse(expression).optimize
52
+ optimized_expression.visit(data)
58
53
  end
59
54
 
60
55
  private
@@ -2,32 +2,31 @@ module JMESPath
2
2
  # @api private
3
3
  class Token < Struct.new(:type, :value, :position, :binding_power)
4
4
 
5
- # @api private
6
5
  NULL_TOKEN = Token.new(:eof, '', nil)
7
6
 
8
- # binding power
9
- # @api private
10
7
  BINDING_POWER = {
11
- :eof => 0,
12
- :quoted_identifier => 0,
13
- :identifier => 0,
14
- :rbracket => 0,
15
- :rparen => 0,
16
- :comma => 0,
17
- :rbrace => 0,
18
- :number => 0,
19
- :current => 0,
20
- :expref => 0,
21
- :pipe => 1,
22
- :comparator => 2,
23
- :or => 5,
24
- :flatten => 6,
25
- :star => 20,
26
- :dot => 40,
27
- :lbrace => 50,
28
- :filter => 50,
29
- :lbracket => 50,
30
- :lparen => 60,
8
+ Lexer::T_UNKNOWN => 0,
9
+ Lexer::T_EOF => 0,
10
+ Lexer::T_QUOTED_IDENTIFIER => 0,
11
+ Lexer::T_IDENTIFIER => 0,
12
+ Lexer::T_RBRACKET => 0,
13
+ Lexer::T_RPAREN => 0,
14
+ Lexer::T_COMMA => 0,
15
+ Lexer::T_RBRACE => 0,
16
+ Lexer::T_NUMBER => 0,
17
+ Lexer::T_CURRENT => 0,
18
+ Lexer::T_EXPREF => 0,
19
+ Lexer::T_COLON => 0,
20
+ Lexer::T_PIPE => 1,
21
+ Lexer::T_COMPARATOR => 2,
22
+ Lexer::T_OR => 5,
23
+ Lexer::T_FLATTEN => 6,
24
+ Lexer::T_STAR => 20,
25
+ Lexer::T_FILTER => 21,
26
+ Lexer::T_DOT => 40,
27
+ Lexer::T_LBRACE => 50,
28
+ Lexer::T_LBRACKET => 55,
29
+ Lexer::T_LPAREN => 60,
31
30
  }
32
31
 
33
32
  # @param [Symbol] type
@@ -1,3 +1,3 @@
1
1
  module JMESPath
2
- VERSION = '1.0.2'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,47 +1,62 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jmespath
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trevor Rowe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-04 00:00:00.000000000 Z
11
+ date: 2015-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: multi_json
14
+ name: json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: '1.8'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
26
+ version: '1.8'
27
27
  description: Implements JMESPath for Ruby
28
28
  email: trevorrowe@gmail.com
29
29
  executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - LICENSE.txt
34
+ - lib/jmespath.rb
33
35
  - lib/jmespath/caching_parser.rb
34
36
  - lib/jmespath/errors.rb
35
- - lib/jmespath/expr_node.rb
36
37
  - lib/jmespath/lexer.rb
38
+ - lib/jmespath/nodes.rb
39
+ - lib/jmespath/nodes/comparator.rb
40
+ - lib/jmespath/nodes/condition.rb
41
+ - lib/jmespath/nodes/current.rb
42
+ - lib/jmespath/nodes/expression.rb
43
+ - lib/jmespath/nodes/field.rb
44
+ - lib/jmespath/nodes/flatten.rb
45
+ - lib/jmespath/nodes/function.rb
46
+ - lib/jmespath/nodes/index.rb
47
+ - lib/jmespath/nodes/literal.rb
48
+ - lib/jmespath/nodes/multi_select_hash.rb
49
+ - lib/jmespath/nodes/multi_select_list.rb
50
+ - lib/jmespath/nodes/or.rb
51
+ - lib/jmespath/nodes/pipe.rb
52
+ - lib/jmespath/nodes/projection.rb
53
+ - lib/jmespath/nodes/slice.rb
54
+ - lib/jmespath/nodes/subexpression.rb
37
55
  - lib/jmespath/parser.rb
38
56
  - lib/jmespath/runtime.rb
39
57
  - lib/jmespath/token.rb
40
58
  - lib/jmespath/token_stream.rb
41
- - lib/jmespath/tree_interpreter.rb
42
59
  - lib/jmespath/version.rb
43
- - lib/jmespath.rb
44
- - LICENSE.txt
45
60
  homepage: http://github.com/trevorrowe/jmespath.rb
46
61
  licenses:
47
62
  - Apache 2.0
@@ -52,17 +67,17 @@ require_paths:
52
67
  - lib
53
68
  required_ruby_version: !ruby/object:Gem::Requirement
54
69
  requirements:
55
- - - '>='
70
+ - - ">="
56
71
  - !ruby/object:Gem::Version
57
72
  version: '0'
58
73
  required_rubygems_version: !ruby/object:Gem::Requirement
59
74
  requirements:
60
- - - '>='
75
+ - - ">="
61
76
  - !ruby/object:Gem::Version
62
77
  version: '0'
63
78
  requirements: []
64
79
  rubyforge_project:
65
- rubygems_version: 2.1.11
80
+ rubygems_version: 2.4.5
66
81
  signing_key:
67
82
  specification_version: 4
68
83
  summary: JMESPath - Ruby Edition
@@ -1,15 +0,0 @@
1
- module JMESPath
2
- # @api private
3
- class ExprNode
4
-
5
- def initialize(interpreter, node)
6
- @interpreter = interpreter
7
- @node = node
8
- end
9
-
10
- attr_reader :interpreter
11
-
12
- attr_reader :node
13
-
14
- end
15
- end
@@ -1,523 +0,0 @@
1
- module JMESPath
2
- # @api private
3
- class TreeInterpreter
4
-
5
- def visit(node, data)
6
- dispatch(node, data)
7
- end
8
-
9
- # @api private
10
- def method_missing(method_name, *args)
11
- if matches = method_name.to_s.match(/^function_(.*)/)
12
- raise Errors::UnknownFunctionError, "unknown function #{matches[1]}()"
13
- else
14
- super
15
- end
16
- end
17
-
18
- private
19
-
20
- def dispatch(node, value)
21
- case node[:type]
22
-
23
- when :field
24
- # hash_like?
25
- key = node[:key]
26
- case value
27
- when Hash then value.key?(key) ? value[key] : value[key.to_sym]
28
- when Struct then value.respond_to?(key) ? value[key] : nil
29
- else nil
30
- end
31
-
32
- when :subexpression
33
- dispatch(node[:children][1], dispatch(node[:children][0], value))
34
-
35
- when :index
36
- if Array === value
37
- value[node[:index]]
38
- else
39
- nil
40
- end
41
-
42
- when :projection
43
- # Interprets a projection node, passing the values of the left
44
- # child through the values of the right child and aggregating
45
- # the non-null results into the return value.
46
- left = dispatch(node[:children][0], value)
47
- if node[:from] == :object && hash_like?(left)
48
- projection(left.values, node)
49
- elsif node[:from] == :object && left == []
50
- projection(left, node)
51
- elsif node[:from] == :array && Array === left
52
- projection(left, node)
53
- else
54
- nil
55
- end
56
-
57
- when :flatten
58
- value = dispatch(node[:children][0], value)
59
- if Array === value
60
- value.inject([]) do |values, v|
61
- values + (Array === v ? v : [v])
62
- end
63
- else
64
- nil
65
- end
66
-
67
- when :literal
68
- node[:value]
69
-
70
- when :current
71
- value
72
-
73
- when :or
74
- result = dispatch(node[:children][0], value)
75
- if result.nil? or result.empty?
76
- dispatch(node[:children][1], value)
77
- else
78
- result
79
- end
80
-
81
- when :pipe
82
- dispatch(node[:children][1], dispatch(node[:children][0], value))
83
-
84
- when :multi_select_list
85
- if value.nil?
86
- value
87
- else
88
- node[:children].map { |n| dispatch(n, value) }
89
- end
90
-
91
- when :multi_select_hash
92
- if value.nil?
93
- nil
94
- else
95
- node[:children].each.with_object({}) do |child, hash|
96
- hash[child[:key]] = dispatch(child[:children][0], value)
97
- end
98
- end
99
-
100
-
101
- when :comparator
102
- left = dispatch(node[:children][0], value)
103
- right = dispatch(node[:children][1], value)
104
- case node[:relation]
105
- when '==' then compare_values(left, right)
106
- when '!=' then !compare_values(left, right)
107
- when '>' then is_int(left) && is_int(right) && left > right
108
- when '>=' then is_int(left) && is_int(right) && left >= right
109
- when '<' then is_int(left) && is_int(right) && left < right
110
- when '<=' then is_int(left) && is_int(right) && left <= right
111
- end
112
-
113
- when :condition
114
- true == dispatch(node[:children][0], value) ?
115
- dispatch(node[:children][1], value) :
116
- nil
117
-
118
- when :function
119
- args = node[:children].map { |child| dispatch(child, value) }
120
- send("function_#{node[:fn]}", *args)
121
-
122
- when :slice
123
- function_slice(value, *node[:args])
124
-
125
- when :expression
126
- ExprNode.new(self, node[:children][0])
127
-
128
- else
129
- raise NotImplementedError
130
- end
131
- end
132
-
133
- def hash_like?(value)
134
- Hash === value || Struct === value
135
- end
136
-
137
- def projection(values, node)
138
- values.inject([]) do |list, v|
139
- list << dispatch(node[:children][1], v)
140
- end.compact
141
- end
142
-
143
- def function_abs(*args)
144
- if args.count == 1
145
- value = args.first
146
- else
147
- raise Errors::InvalidArityError, "function abs() expects one argument"
148
- end
149
- if Numeric === value
150
- value.abs
151
- else
152
- raise Errors::InvalidTypeError, "function abs() expects a number"
153
- end
154
- end
155
-
156
- def function_avg(*args)
157
- if args.count == 1
158
- values = args.first
159
- else
160
- raise Errors::InvalidArityError, "function avg() expects one argument"
161
- end
162
- if Array === values
163
- values.inject(0) do |total,n|
164
- if Numeric === n
165
- total + n
166
- else
167
- raise Errors::InvalidTypeError, "function avg() expects numeric values"
168
- end
169
- end / values.size.to_f
170
- else
171
- raise Errors::InvalidTypeError, "function avg() expects a number"
172
- end
173
- end
174
-
175
- def function_ceil(*args)
176
- if args.count == 1
177
- value = args.first
178
- else
179
- raise Errors::InvalidArityError, "function ceil() expects one argument"
180
- end
181
- if Numeric === value
182
- value.ceil
183
- else
184
- raise Errors::InvalidTypeError, "function ceil() expects a numeric value"
185
- end
186
- end
187
-
188
- def function_contains(*args)
189
- if args.count == 2
190
- if String === args[0] || Array === args[0]
191
- args[0].include?(args[1])
192
- else
193
- raise Errors::InvalidTypeError, "contains expects 2nd arg to be a list"
194
- end
195
- else
196
- raise Errors::InvalidArityError, "function contains() expects 2 arguments"
197
- end
198
- end
199
-
200
- def function_floor(*args)
201
- if args.count == 1
202
- value = args.first
203
- else
204
- raise Errors::InvalidArityError, "function floor() expects one argument"
205
- end
206
- if Numeric === value
207
- value.floor
208
- else
209
- raise Errors::InvalidTypeError, "function floor() expects a numeric value"
210
- end
211
- end
212
-
213
- def function_length(*args)
214
- if args.count == 1
215
- value = args.first
216
- else
217
- raise Errors::InvalidArityError, "function length() expects one argument"
218
- end
219
- case value
220
- when Hash, Array, String then value.size
221
- else raise Errors::InvalidTypeError, "function length() expects string, array or object"
222
- end
223
- end
224
-
225
- def function_max(*args)
226
- if args.count == 1
227
- values = args.first
228
- else
229
- raise Errors::InvalidArityError, "function max() expects one argument"
230
- end
231
- if Array === values
232
- values.inject(values.first) do |max, v|
233
- if Numeric === v
234
- v > max ? v : max
235
- else
236
- raise Errors::InvalidTypeError, "function max() expects numeric values"
237
- end
238
- end
239
- else
240
- raise Errors::InvalidTypeError, "function max() expects an array"
241
- end
242
- end
243
-
244
- def function_min(*args)
245
- if args.count == 1
246
- values = args.first
247
- else
248
- raise Errors::InvalidArityError, "function min() expects one argument"
249
- end
250
- if Array === values
251
- values.inject(values.first) do |min, v|
252
- if Numeric === v
253
- v < min ? v : min
254
- else
255
- raise Errors::InvalidTypeError, "function min() expects numeric values"
256
- end
257
- end
258
- else
259
- raise Errors::InvalidTypeError, "function min() expects an array"
260
- end
261
- end
262
-
263
- def function_type(*args)
264
- if args.count == 1
265
- get_type(args.first)
266
- else
267
- raise Errors::InvalidArityError, "function type() expects one argument"
268
- end
269
- end
270
-
271
- def function_keys(*args)
272
- if args.count == 1
273
- value = args.first
274
- if hash_like?(value)
275
- case value
276
- when Hash then value.keys.map(&:to_s)
277
- when Struct then value.members.map(&:to_s)
278
- else raise NotImplementedError
279
- end
280
- else
281
- raise Errors::InvalidTypeError, "function keys() expects a hash"
282
- end
283
- else
284
- raise Errors::InvalidArityError, "function keys() expects one argument"
285
- end
286
- end
287
-
288
- def function_values(*args)
289
- if args.count == 1
290
- value = args.first
291
- if hash_like?(value)
292
- value.values
293
- elsif Array === value
294
- value
295
- else
296
- raise Errors::InvalidTypeError, "function values() expects an array or a hash"
297
- end
298
- else
299
- raise Errors::InvalidArityError, "function values() expects one argument"
300
- end
301
- end
302
-
303
- def function_join(*args)
304
- if args.count == 2
305
- glue = args[0]
306
- values = args[1]
307
- if !(String === glue)
308
- raise Errors::InvalidTypeError, "function join() expects the first argument to be a string"
309
- elsif Array === values && values.all? { |v| String === v }
310
- values.join(glue)
311
- else
312
- raise Errors::InvalidTypeError, "function join() expects values to be an array of strings"
313
- end
314
- else
315
- raise Errors::InvalidArityError, "function join() expects an array of strings"
316
- end
317
- end
318
-
319
- def function_to_string(*args)
320
- if args.count == 1
321
- value = args.first
322
- String === value ? value : MultiJson.dump(value)
323
- else
324
- raise Errors::InvalidArityError, "function to_string() expects one argument"
325
- end
326
- end
327
-
328
- def function_to_number(*args)
329
- if args.count == 1
330
- begin
331
- value = Float(args.first)
332
- Integer(value) === value ? value.to_i : value
333
- rescue
334
- nil
335
- end
336
- else
337
- raise Errors::InvalidArityError, "function to_number() expects one argument"
338
- end
339
- end
340
-
341
- def function_sum(*args)
342
- if args.count == 1 && Array === args.first
343
- args.first.inject(0) do |sum,n|
344
- if Numeric === n
345
- sum + n
346
- else
347
- raise Errors::InvalidTypeError, "function sum() expects values to be numeric"
348
- end
349
- end
350
- else
351
- raise Errors::InvalidArityError, "function sum() expects one argument"
352
- end
353
- end
354
-
355
- def function_not_null(*args)
356
- if args.count > 0
357
- args.find { |value| !value.nil? }
358
- else
359
- raise Errors::InvalidArityError, "function not_null() expects one or more arguments"
360
- end
361
- end
362
-
363
- def function_sort(*args)
364
- if args.count == 1
365
- value = args.first
366
- if Array === value
367
- value.sort do |a, b|
368
- a_type = get_type(a)
369
- b_type = get_type(b)
370
- if ['string', 'number'].include?(a_type) && a_type == b_type
371
- a <=> b
372
- else
373
- raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
374
- end
375
- end
376
- else
377
- raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
378
- end
379
- else
380
- raise Errors::InvalidArityError, "function sort() expects one argument"
381
- end
382
- end
383
-
384
- def function_sort_by(*args)
385
- if args.count == 2
386
- if get_type(args[0]) == 'array' && get_type(args[1]) == 'expression'
387
- values = args[0]
388
- expression = args[1]
389
- values.sort do |a,b|
390
- a_value = expression.interpreter.visit(expression.node, a)
391
- b_value = expression.interpreter.visit(expression.node, b)
392
- a_type = get_type(a_value)
393
- b_type = get_type(b_value)
394
- if ['string', 'number'].include?(a_type) && a_type == b_type
395
- a_value <=> b_value
396
- else
397
- raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
398
- end
399
- end
400
- else
401
- raise Errors::InvalidTypeError, "function sort_by() expects an array and an expression"
402
- end
403
- else
404
- raise Errors::InvalidArityError, "function sort_by() expects two arguments"
405
- end
406
- end
407
-
408
- def function_max_by(*args)
409
- number_compare(:max, *args)
410
- end
411
-
412
- def function_min_by(*args)
413
- number_compare(:min, *args)
414
- end
415
-
416
- def number_compare(mode, *args)
417
- if args.count == 2
418
- if get_type(args[0]) == 'array' && get_type(args[1]) == 'expression'
419
- values = args[0]
420
- expression = args[1]
421
- args[0].send("#{mode}_by") do |entry|
422
- value = expression.interpreter.visit(expression.node, entry)
423
- if get_type(value) == 'number'
424
- value
425
- else
426
- raise Errors::InvalidTypeError, "function #{mode}_by() expects values to be an numbers"
427
- end
428
- end
429
- else
430
- raise Errors::InvalidTypeError, "function #{mode}_by() expects an array and an expression"
431
- end
432
- else
433
- raise Errors::InvalidArityError, "function #{mode}_by() expects two arguments"
434
- end
435
- end
436
-
437
- def function_slice(values, *args)
438
- if String === values || Array === values
439
- _slice(values, *args)
440
- else
441
- nil
442
- end
443
- end
444
-
445
- def _slice(values, start, stop, step)
446
- start, stop, step = _adjust_slice(values.size, start, stop, step)
447
- result = []
448
- if step > 0
449
- i = start
450
- while i < stop
451
- result << values[i]
452
- i += step
453
- end
454
- else
455
- i = start
456
- while i > stop
457
- result << values[i]
458
- i += step
459
- end
460
- end
461
- String === values ? result.join : result
462
- end
463
-
464
- def _adjust_slice(length, start, stop, step)
465
- if step.nil?
466
- step = 1
467
- elsif step == 0
468
- raise Errors::RuntimeError, 'slice step cannot be 0'
469
- end
470
-
471
- if start.nil?
472
- start = step < 0 ? length - 1 : 0
473
- else
474
- start = _adjust_endpoint(length, start, step)
475
- end
476
-
477
- if stop.nil?
478
- stop = step < 0 ? -1 : length
479
- else
480
- stop = _adjust_endpoint(length, stop, step)
481
- end
482
-
483
- [start, stop, step]
484
- end
485
-
486
- def _adjust_endpoint(length, endpoint, step)
487
- if endpoint < 0
488
- endpoint += length
489
- endpoint = 0 if endpoint < 0
490
- endpoint
491
- elsif endpoint >= length
492
- step < 0 ? length - 1 : length
493
- else
494
- endpoint
495
- end
496
- end
497
-
498
- def compare_values(a, b)
499
- if a == b
500
- true
501
- else
502
- false
503
- end
504
- end
505
-
506
- def is_int(value)
507
- Integer === value
508
- end
509
-
510
- def get_type(value)
511
- case
512
- when ExprNode === value then 'expression'
513
- when String === value then 'string'
514
- when hash_like?(value) then 'object'
515
- when Array === value then 'array'
516
- when [true, false].include?(value) then 'boolean'
517
- when value.nil? then 'null'
518
- when Numeric === value then 'number'
519
- end
520
- end
521
-
522
- end
523
- end