jmespath 1.1.3 → 1.2

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8c1b07dda78e4e255a2f6b9d50705ad2d19029e
4
- data.tar.gz: 07200f08f3c6c63fe284cbec59921a146ae428d3
3
+ metadata.gz: fdb6bfadd1b501b4a2316f67248a96b51e350e00
4
+ data.tar.gz: edf12df4b8e49fc520f507098947c381d4312de1
5
5
  SHA512:
6
- metadata.gz: e605f030766166d80c779828eb1c75546f213309720b1ee3d88eb1740cca16a31f7011dfcd57e53961891ff8f6a5cdb611533ff64cdb95fb541c813bdd722873
7
- data.tar.gz: 73f06c5c63db7e878f9c3a6a286a026d42e098c79a41d721d82bafa0783e3a925636293caad04410946ef7963fbb016bdea51740973d3589fa1ff592a066d0b0
6
+ metadata.gz: 135adb95e2c213b911405e10412eb8484009605555433109a12f954aa96376818c74ef94af18868fd7fe309956d6a6457341c5621aeb76ff743f57192a4f8e2b
7
+ data.tar.gz: c5a3165e77c71faf9f62bba531b8b5091da2f9224693052c27899fee2ba15bd4f5a79e1fb2e5bc1e1407d5927362819ce226b6e6688f3e2a749a11c097a23dd8
data/lib/jmespath.rb CHANGED
@@ -13,6 +13,7 @@ module JMESPath
13
13
  autoload :Runtime, 'jmespath/runtime'
14
14
  autoload :Token, 'jmespath/token'
15
15
  autoload :TokenStream, 'jmespath/token_stream'
16
+ autoload :Util, 'jmespath/util'
16
17
  autoload :VERSION, 'jmespath/version'
17
18
 
18
19
  class << self
@@ -23,14 +24,14 @@ module JMESPath
23
24
  # @param [Hash] data
24
25
  # @return [Mixed,nil] Returns the matched values. Returns `nil` if the
25
26
  # expression does not resolve inside `data`.
26
- def search(expression, data)
27
+ def search(expression, data, runtime_options = {})
27
28
  data = case data
28
29
  when Hash, Struct then data # check for most common case first
29
30
  when Pathname then load_json(data)
30
31
  when IO, StringIO then JSON.load(data.read)
31
32
  else data
32
33
  end
33
- Runtime.new.search(expression, data)
34
+ Runtime.new(runtime_options).search(expression, data)
34
35
  end
35
36
 
36
37
  # @api private
@@ -1,3 +1,5 @@
1
+ gem('json', '>= 1.8.1') # fix for Ruby 1.9.3
2
+
1
3
  require 'json'
2
4
  require 'set'
3
5
 
@@ -24,6 +26,8 @@ module JMESPath
24
26
  T_UNKNOWN = :unknown
25
27
  T_PIPE = :pipe
26
28
  T_OR = :or
29
+ T_AND = :and
30
+ T_NOT = :not
27
31
  T_FILTER = :filter
28
32
  T_LITERAL = :literal
29
33
  T_EOF = :eof
@@ -42,6 +46,7 @@ module JMESPath
42
46
  STATE_GT = 10
43
47
  STATE_EQ = 11
44
48
  STATE_NOT = 12
49
+ STATE_AND = 13
45
50
 
46
51
  TRANSLATION_TABLE = {
47
52
  '<' => STATE_LT,
@@ -50,6 +55,7 @@ module JMESPath
50
55
  '!' => STATE_NOT,
51
56
  '[' => STATE_LBRACKET,
52
57
  '|' => STATE_PIPE,
58
+ '&' => STATE_AND,
53
59
  '`' => STATE_JSON_LITERAL,
54
60
  '"' => STATE_QUOTED_STRING,
55
61
  "'" => STATE_STRING_LITERAL,
@@ -74,7 +80,6 @@ module JMESPath
74
80
  ',' => STATE_SINGLE_CHAR,
75
81
  ':' => STATE_SINGLE_CHAR,
76
82
  '@' => STATE_SINGLE_CHAR,
77
- '&' => STATE_SINGLE_CHAR,
78
83
  '(' => STATE_SINGLE_CHAR,
79
84
  ')' => STATE_SINGLE_CHAR,
80
85
  '{' => STATE_SINGLE_CHAR,
@@ -149,7 +154,6 @@ module JMESPath
149
154
  ',' => T_COMMA,
150
155
  ':' => T_COLON,
151
156
  '@' => T_CURRENT,
152
- '&' => T_EXPREF,
153
157
  '(' => T_LPAREN,
154
158
  ')' => T_RPAREN,
155
159
  '{' => T_LBRACE,
@@ -210,7 +214,9 @@ module JMESPath
210
214
  end
211
215
  when STATE_STRING_LITERAL
212
216
  # consume raw string literals
213
- tokens << inside(chars, "'", T_LITERAL)
217
+ t = inside(chars, "'", T_LITERAL)
218
+ t.value = t.value.gsub("\\'", "'")
219
+ tokens << t
214
220
  when STATE_PIPE
215
221
  # consume pipe and OR
216
222
  tokens << match_or(chars, '|', '|', T_OR, T_PIPE)
@@ -245,9 +251,11 @@ module JMESPath
245
251
  when STATE_EQ
246
252
  # consume equals
247
253
  tokens << match_or(chars, '=', '=', T_COMPARATOR, T_UNKNOWN)
254
+ when STATE_AND
255
+ tokens << match_or(chars, '&', '&', T_AND, T_EXPREF)
248
256
  when STATE_NOT
249
257
  # consume not equals
250
- tokens << match_or(chars, '!', '=', T_COMPARATOR, T_UNKNOWN)
258
+ tokens << match_or(chars, '!', '=', T_COMPARATOR, T_NOT);
251
259
  else
252
260
  # either '<' or '>'
253
261
  # consume less than and greater than
@@ -18,6 +18,7 @@ module JMESPath
18
18
  end
19
19
  end
20
20
 
21
+ autoload :And, 'jmespath/nodes/and'
21
22
  autoload :Comparator, 'jmespath/nodes/comparator'
22
23
  autoload :Comparators, 'jmespath/nodes/comparator'
23
24
  autoload :Condition, 'jmespath/nodes/condition'
@@ -30,6 +31,7 @@ module JMESPath
30
31
  autoload :Literal, 'jmespath/nodes/literal'
31
32
  autoload :MultiSelectHash, 'jmespath/nodes/multi_select_hash'
32
33
  autoload :MultiSelectList, 'jmespath/nodes/multi_select_list'
34
+ autoload :Not, 'jmespath/nodes/not'
33
35
  autoload :Or, 'jmespath/nodes/or'
34
36
  autoload :Pipe, 'jmespath/nodes/pipe'
35
37
  autoload :Projection, 'jmespath/nodes/projection'
@@ -0,0 +1,25 @@
1
+ module JMESPath
2
+ module Nodes
3
+ class And < Node
4
+
5
+ def initialize(left, right)
6
+ @left = left
7
+ @right = right
8
+ end
9
+
10
+ def visit(value)
11
+ result = @left.visit(value)
12
+ if JMESPath::Util.falsey?(result)
13
+ result
14
+ else
15
+ @right.visit(value)
16
+ end
17
+ end
18
+
19
+ def optimize
20
+ self.class.new(@left.optimize, @right.optimize)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -8,10 +8,10 @@ module JMESPath
8
8
  end
9
9
 
10
10
  def visit(value)
11
- if @test.visit(value)
12
- @child.visit(value)
13
- else
11
+ if JMESPath::Util.falsey?(@test.visit(value))
14
12
  nil
13
+ else
14
+ @child.visit(value)
15
15
  end
16
16
  end
17
17
 
@@ -46,8 +46,8 @@ module JMESPath
46
46
  end
47
47
  end
48
48
 
49
- def visit(value)
50
- @keys.reduce(value) do |value, key|
49
+ def visit(obj)
50
+ @keys.reduce(obj) do |value, key|
51
51
  if value.is_a?(Array) && key.is_a?(Integer)
52
52
  value[key]
53
53
  elsif value.is_a?(Hash)
@@ -68,7 +68,10 @@ module JMESPath
68
68
 
69
69
  private
70
70
 
71
- attr_reader :keys
71
+ def keys
72
+ @keys
73
+ end
74
+
72
75
  end
73
76
  end
74
77
  end
@@ -5,13 +5,15 @@ module JMESPath
5
5
 
6
6
  FUNCTIONS = {}
7
7
 
8
- def initialize(children)
8
+ def initialize(children, options = {})
9
9
  @children = children
10
+ @options = options
11
+ @disable_visit_errors = @options[:disable_visit_errors]
10
12
  end
11
13
 
12
- def self.create(name, children)
14
+ def self.create(name, children, options = {})
13
15
  if (type = FUNCTIONS[name])
14
- type.new(children)
16
+ type.new(children, options)
15
17
  else
16
18
  raise Errors::UnknownFunctionError, "unknown function #{name}()"
17
19
  end
@@ -22,7 +24,7 @@ module JMESPath
22
24
  end
23
25
 
24
26
  def optimize
25
- self.class.new(@children.map(&:optimize))
27
+ self.class.new(@children.map(&:optimize), @options)
26
28
  end
27
29
 
28
30
  class FunctionName
@@ -35,6 +37,12 @@ module JMESPath
35
37
 
36
38
  private
37
39
 
40
+ def maybe_raise(error_type, message)
41
+ unless @disable_visit_errors
42
+ raise error_type, message
43
+ end
44
+ end
45
+
38
46
  def call(args)
39
47
  nil
40
48
  end
@@ -79,12 +87,12 @@ module JMESPath
79
87
  if args.count == 1
80
88
  value = args.first
81
89
  else
82
- raise Errors::InvalidArityError, "function abs() expects one argument"
90
+ return maybe_raise Errors::InvalidArityError, "function abs() expects one argument"
83
91
  end
84
92
  if Numeric === value
85
93
  value.abs
86
94
  else
87
- raise Errors::InvalidTypeError, "function abs() expects a number"
95
+ return maybe_raise Errors::InvalidTypeError, "function abs() expects a number"
88
96
  end
89
97
  end
90
98
  end
@@ -96,18 +104,18 @@ module JMESPath
96
104
  if args.count == 1
97
105
  values = args.first
98
106
  else
99
- raise Errors::InvalidArityError, "function avg() expects one argument"
107
+ return maybe_raise Errors::InvalidArityError, "function avg() expects one argument"
100
108
  end
101
109
  if Array === values
102
110
  values.inject(0) do |total,n|
103
111
  if Numeric === n
104
112
  total + n
105
113
  else
106
- raise Errors::InvalidTypeError, "function avg() expects numeric values"
114
+ return maybe_raise Errors::InvalidTypeError, "function avg() expects numeric values"
107
115
  end
108
116
  end / values.size.to_f
109
117
  else
110
- raise Errors::InvalidTypeError, "function avg() expects a number"
118
+ return maybe_raise Errors::InvalidTypeError, "function avg() expects a number"
111
119
  end
112
120
  end
113
121
  end
@@ -119,12 +127,12 @@ module JMESPath
119
127
  if args.count == 1
120
128
  value = args.first
121
129
  else
122
- raise Errors::InvalidArityError, "function ceil() expects one argument"
130
+ return maybe_raise Errors::InvalidArityError, "function ceil() expects one argument"
123
131
  end
124
132
  if Numeric === value
125
133
  value.ceil
126
134
  else
127
- raise Errors::InvalidTypeError, "function ceil() expects a numeric value"
135
+ return maybe_raise Errors::InvalidTypeError, "function ceil() expects a numeric value"
128
136
  end
129
137
  end
130
138
  end
@@ -139,10 +147,10 @@ module JMESPath
139
147
  if String === haystack || Array === haystack
140
148
  haystack.include?(needle)
141
149
  else
142
- raise Errors::InvalidTypeError, "contains expects 2nd arg to be a list"
150
+ return maybe_raise Errors::InvalidTypeError, "contains expects 2nd arg to be a list"
143
151
  end
144
152
  else
145
- raise Errors::InvalidArityError, "function contains() expects 2 arguments"
153
+ return maybe_raise Errors::InvalidArityError, "function contains() expects 2 arguments"
146
154
  end
147
155
  end
148
156
  end
@@ -154,12 +162,12 @@ module JMESPath
154
162
  if args.count == 1
155
163
  value = args.first
156
164
  else
157
- raise Errors::InvalidArityError, "function floor() expects one argument"
165
+ return maybe_raise Errors::InvalidArityError, "function floor() expects one argument"
158
166
  end
159
167
  if Numeric === value
160
168
  value.floor
161
169
  else
162
- raise Errors::InvalidTypeError, "function floor() expects a numeric value"
170
+ return maybe_raise Errors::InvalidTypeError, "function floor() expects a numeric value"
163
171
  end
164
172
  end
165
173
  end
@@ -171,15 +179,38 @@ module JMESPath
171
179
  if args.count == 1
172
180
  value = args.first
173
181
  else
174
- raise Errors::InvalidArityError, "function length() expects one argument"
182
+ return maybe_raise Errors::InvalidArityError, "function length() expects one argument"
175
183
  end
176
184
  case value
177
185
  when Hash, Array, String then value.size
178
- else raise Errors::InvalidTypeError, "function length() expects string, array or object"
186
+ else return maybe_raise Errors::InvalidTypeError, "function length() expects string, array or object"
179
187
  end
180
188
  end
181
189
  end
182
190
 
191
+ class Map < Function
192
+
193
+ FUNCTIONS['map'] = self
194
+
195
+ def call(args)
196
+ if args.count != 2
197
+ return maybe_raise Errors::InvalidArityError, "function map() expects two arguments"
198
+ end
199
+ if Nodes::Expression === args[0]
200
+ expr = args[0]
201
+ else
202
+ return maybe_raise Errors::InvalidTypeError, "function map() expects the first argument to be an expression"
203
+ end
204
+ if Array === args[1]
205
+ list = args[1]
206
+ else
207
+ return maybe_raise Errors::InvalidTypeError, "function map() expects the second argument to be an list"
208
+ end
209
+ list.map { |value| expr.eval(value) }
210
+ end
211
+
212
+ end
213
+
183
214
  class MaxFunction < Function
184
215
  include TypeChecker
185
216
 
@@ -189,7 +220,7 @@ module JMESPath
189
220
  if args.count == 1
190
221
  values = args.first
191
222
  else
192
- raise Errors::InvalidArityError, "function max() expects one argument"
223
+ return maybe_raise Errors::InvalidArityError, "function max() expects one argument"
193
224
  end
194
225
  if Array === values
195
226
  return nil if values.empty?
@@ -197,7 +228,7 @@ module JMESPath
197
228
  first_type = get_type(first)
198
229
  unless first_type == NUMBER_TYPE || first_type == STRING_TYPE
199
230
  msg = "function max() expects numeric or string values"
200
- raise Errors::InvalidTypeError, msg
231
+ return maybe_raise Errors::InvalidTypeError, msg
201
232
  end
202
233
  values.inject([first, first_type]) do |(max, max_type), v|
203
234
  v_type = get_type(v)
@@ -206,11 +237,11 @@ module JMESPath
206
237
  else
207
238
  msg = "function max() encountered a type mismatch in sequence: "
208
239
  msg << "#{max_type}, #{v_type}"
209
- raise Errors::InvalidTypeError, msg
240
+ return maybe_raise Errors::InvalidTypeError, msg
210
241
  end
211
242
  end.first
212
243
  else
213
- raise Errors::InvalidTypeError, "function max() expects an array"
244
+ return maybe_raise Errors::InvalidTypeError, "function max() expects an array"
214
245
  end
215
246
  end
216
247
  end
@@ -224,7 +255,7 @@ module JMESPath
224
255
  if args.count == 1
225
256
  values = args.first
226
257
  else
227
- raise Errors::InvalidArityError, "function min() expects one argument"
258
+ return maybe_raise Errors::InvalidArityError, "function min() expects one argument"
228
259
  end
229
260
  if Array === values
230
261
  return nil if values.empty?
@@ -232,7 +263,7 @@ module JMESPath
232
263
  first_type = get_type(first)
233
264
  unless first_type == NUMBER_TYPE || first_type == STRING_TYPE
234
265
  msg = "function min() expects numeric or string values"
235
- raise Errors::InvalidTypeError, msg
266
+ return maybe_raise Errors::InvalidTypeError, msg
236
267
  end
237
268
  values.inject([first, first_type]) do |(min, min_type), v|
238
269
  v_type = get_type(v)
@@ -241,11 +272,11 @@ module JMESPath
241
272
  else
242
273
  msg = "function min() encountered a type mismatch in sequence: "
243
274
  msg << "#{min_type}, #{v_type}"
244
- raise Errors::InvalidTypeError, msg
275
+ return maybe_raise Errors::InvalidTypeError, msg
245
276
  end
246
277
  end.first
247
278
  else
248
- raise Errors::InvalidTypeError, "function min() expects an array"
279
+ return maybe_raise Errors::InvalidTypeError, "function min() expects an array"
249
280
  end
250
281
  end
251
282
  end
@@ -259,7 +290,7 @@ module JMESPath
259
290
  if args.count == 1
260
291
  TYPE_NAMES[get_type(args.first)]
261
292
  else
262
- raise Errors::InvalidArityError, "function type() expects one argument"
293
+ return maybe_raise Errors::InvalidArityError, "function type() expects one argument"
263
294
  end
264
295
  end
265
296
  end
@@ -277,10 +308,10 @@ module JMESPath
277
308
  else raise NotImplementedError
278
309
  end
279
310
  else
280
- raise Errors::InvalidTypeError, "function keys() expects a hash"
311
+ return maybe_raise Errors::InvalidTypeError, "function keys() expects a hash"
281
312
  end
282
313
  else
283
- raise Errors::InvalidArityError, "function keys() expects one argument"
314
+ return maybe_raise Errors::InvalidArityError, "function keys() expects one argument"
284
315
  end
285
316
  end
286
317
  end
@@ -296,10 +327,10 @@ module JMESPath
296
327
  elsif Array === value
297
328
  value
298
329
  else
299
- raise Errors::InvalidTypeError, "function values() expects an array or a hash"
330
+ return maybe_raise Errors::InvalidTypeError, "function values() expects an array or a hash"
300
331
  end
301
332
  else
302
- raise Errors::InvalidArityError, "function values() expects one argument"
333
+ return maybe_raise Errors::InvalidArityError, "function values() expects one argument"
303
334
  end
304
335
  end
305
336
  end
@@ -312,14 +343,14 @@ module JMESPath
312
343
  glue = args[0]
313
344
  values = args[1]
314
345
  if !(String === glue)
315
- raise Errors::InvalidTypeError, "function join() expects the first argument to be a string"
346
+ return maybe_raise Errors::InvalidTypeError, "function join() expects the first argument to be a string"
316
347
  elsif Array === values && values.all? { |v| String === v }
317
348
  values.join(glue)
318
349
  else
319
- raise Errors::InvalidTypeError, "function join() expects values to be an array of strings"
350
+ return maybe_raise Errors::InvalidTypeError, "function join() expects values to be an array of strings"
320
351
  end
321
352
  else
322
- raise Errors::InvalidArityError, "function join() expects an array of strings"
353
+ return maybe_raise Errors::InvalidArityError, "function join() expects an array of strings"
323
354
  end
324
355
  end
325
356
  end
@@ -332,7 +363,7 @@ module JMESPath
332
363
  value = args.first
333
364
  String === value ? value : JSON.dump(value)
334
365
  else
335
- raise Errors::InvalidArityError, "function to_string() expects one argument"
366
+ return maybe_raise Errors::InvalidArityError, "function to_string() expects one argument"
336
367
  end
337
368
  end
338
369
  end
@@ -349,7 +380,7 @@ module JMESPath
349
380
  nil
350
381
  end
351
382
  else
352
- raise Errors::InvalidArityError, "function to_number() expects one argument"
383
+ return maybe_raise Errors::InvalidArityError, "function to_number() expects one argument"
353
384
  end
354
385
  end
355
386
  end
@@ -363,11 +394,11 @@ module JMESPath
363
394
  if Numeric === n
364
395
  sum + n
365
396
  else
366
- raise Errors::InvalidTypeError, "function sum() expects values to be numeric"
397
+ return maybe_raise Errors::InvalidTypeError, "function sum() expects values to be numeric"
367
398
  end
368
399
  end
369
400
  else
370
- raise Errors::InvalidArityError, "function sum() expects one argument"
401
+ return maybe_raise Errors::InvalidArityError, "function sum() expects one argument"
371
402
  end
372
403
  end
373
404
  end
@@ -379,7 +410,7 @@ module JMESPath
379
410
  if args.count > 0
380
411
  args.find { |value| !value.nil? }
381
412
  else
382
- raise Errors::InvalidArityError, "function not_null() expects one or more arguments"
413
+ return maybe_raise Errors::InvalidArityError, "function not_null() expects one or more arguments"
383
414
  end
384
415
  end
385
416
  end
@@ -399,14 +430,14 @@ module JMESPath
399
430
  if (a_type == STRING_TYPE || a_type == NUMBER_TYPE) && a_type == b_type
400
431
  a <=> b
401
432
  else
402
- raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
433
+ return maybe_raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
403
434
  end
404
435
  end
405
436
  else
406
- raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
437
+ return maybe_raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
407
438
  end
408
439
  else
409
- raise Errors::InvalidArityError, "function sort() expects one argument"
440
+ return maybe_raise Errors::InvalidArityError, "function sort() expects one argument"
410
441
  end
411
442
  end
412
443
  end
@@ -429,14 +460,14 @@ module JMESPath
429
460
  if (a_type == STRING_TYPE || a_type == NUMBER_TYPE) && a_type == b_type
430
461
  a_value <=> b_value
431
462
  else
432
- raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
463
+ return maybe_raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
433
464
  end
434
465
  end
435
466
  else
436
- raise Errors::InvalidTypeError, "function sort_by() expects an array and an expression"
467
+ return maybe_raise Errors::InvalidTypeError, "function sort_by() expects an array and an expression"
437
468
  end
438
469
  else
439
- raise Errors::InvalidArityError, "function sort_by() expects two arguments"
470
+ return maybe_raise Errors::InvalidArityError, "function sort_by() expects two arguments"
440
471
  end
441
472
  end
442
473
  end
@@ -452,7 +483,7 @@ module JMESPath
452
483
  type = get_type(expression.eval(values.first))
453
484
  if type != NUMBER_TYPE && type != STRING_TYPE
454
485
  msg = "function #{mode}() expects values to be strings or numbers"
455
- raise Errors::InvalidTypeError, msg
486
+ return maybe_raise Errors::InvalidTypeError, msg
456
487
  end
457
488
  values.send(mode) do |entry|
458
489
  value = expression.eval(entry)
@@ -460,17 +491,17 @@ module JMESPath
460
491
  if value_type != type
461
492
  msg = "function #{mode}() encountered a type mismatch in "
462
493
  msg << "sequence: #{type}, #{value_type}"
463
- raise Errors::InvalidTypeError, msg
494
+ return maybe_raise Errors::InvalidTypeError, msg
464
495
  end
465
496
  value
466
497
  end
467
498
  else
468
499
  msg = "function #{mode}() expects an array and an expression"
469
- raise Errors::InvalidTypeError, msg
500
+ return maybe_raise Errors::InvalidTypeError, msg
470
501
  end
471
502
  else
472
503
  msg = "function #{mode}() expects two arguments"
473
- raise Errors::InvalidArityError, msg
504
+ return maybe_raise Errors::InvalidArityError, msg
474
505
  end
475
506
  end
476
507
  end
@@ -507,16 +538,16 @@ module JMESPath
507
538
  suffix_type = get_type(suffix)
508
539
  if search_type != STRING_TYPE
509
540
  msg = "function ends_with() expects first argument to be a string"
510
- raise Errors::InvalidTypeError, msg
541
+ return maybe_raise Errors::InvalidTypeError, msg
511
542
  end
512
543
  if suffix_type != STRING_TYPE
513
544
  msg = "function ends_with() expects second argument to be a string"
514
- raise Errors::InvalidTypeError, msg
545
+ return maybe_raise Errors::InvalidTypeError, msg
515
546
  end
516
547
  search.end_with?(suffix)
517
548
  else
518
549
  msg = "function ends_with() expects two arguments"
519
- raise Errors::InvalidArityError, msg
550
+ return maybe_raise Errors::InvalidArityError, msg
520
551
  end
521
552
  end
522
553
  end
@@ -533,16 +564,16 @@ module JMESPath
533
564
  prefix_type = get_type(prefix)
534
565
  if search_type != STRING_TYPE
535
566
  msg = "function starts_with() expects first argument to be a string"
536
- raise Errors::InvalidTypeError, msg
567
+ return maybe_raise Errors::InvalidTypeError, msg
537
568
  end
538
569
  if prefix_type != STRING_TYPE
539
570
  msg = "function starts_with() expects second argument to be a string"
540
- raise Errors::InvalidTypeError, msg
571
+ return maybe_raise Errors::InvalidTypeError, msg
541
572
  end
542
573
  search.start_with?(prefix)
543
574
  else
544
575
  msg = "function starts_with() expects two arguments"
545
- raise Errors::InvalidArityError, msg
576
+ return maybe_raise Errors::InvalidArityError, msg
546
577
  end
547
578
  end
548
579
  end
@@ -553,7 +584,7 @@ module JMESPath
553
584
  def call(args)
554
585
  if args.count == 0
555
586
  msg = "function merge() expects 1 or more arguments"
556
- raise Errors::InvalidArityError, msg
587
+ return maybe_raise Errors::InvalidArityError, msg
557
588
  end
558
589
  args.inject({}) do |h, v|
559
590
  h.merge(v)
@@ -567,14 +598,14 @@ module JMESPath
567
598
  def call(args)
568
599
  if args.count == 0
569
600
  msg = "function reverse() expects 1 or more arguments"
570
- raise Errors::InvalidArityError, msg
601
+ return maybe_raise Errors::InvalidArityError, msg
571
602
  end
572
603
  value = args.first
573
604
  if Array === value || String === value
574
605
  value.reverse
575
606
  else
576
607
  msg = "function reverse() expects an array or string"
577
- raise Errors::InvalidTypeError, msg
608
+ return maybe_raise Errors::InvalidTypeError, msg
578
609
  end
579
610
  end
580
611
  end
@@ -0,0 +1,19 @@
1
+ module JMESPath
2
+ module Nodes
3
+ class Not < Node
4
+
5
+ def initialize(expression)
6
+ @expression = expression
7
+ end
8
+
9
+ def visit(value)
10
+ JMESPath::Util.falsey?(@expression.visit(value))
11
+ end
12
+
13
+ def optimize
14
+ self.class.new(@expression.optimize)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -9,7 +9,7 @@ module JMESPath
9
9
 
10
10
  def visit(value)
11
11
  result = @left.visit(value)
12
- if result == false || result.nil? || result.empty?
12
+ if JMESPath::Util.falsey?(result)
13
13
  @right.visit(value)
14
14
  else
15
15
  result
@@ -6,6 +6,7 @@ module JMESPath
6
6
  @start = start
7
7
  @stop = stop
8
8
  @step = step
9
+ raise Errors::InvalidValueError.new('slice step cannot be 0') if @step == 0
9
10
  end
10
11
 
11
12
  def visit(value)
@@ -44,8 +45,6 @@ module JMESPath
44
45
  def adjust_slice(length, start, stop, step)
45
46
  if step.nil?
46
47
  step = 1
47
- elsif step == 0
48
- raise Errors::InvalidValueError, 'slice step cannot be 0'
49
48
  end
50
49
 
51
50
  if start.nil?
@@ -29,6 +29,7 @@ module JMESPath
29
29
  # @option options [Lexer] :lexer
30
30
  def initialize(options = {})
31
31
  @lexer = options[:lexer] || Lexer.new
32
+ @disable_visit_errors = options[:disable_visit_errors]
32
33
  end
33
34
 
34
35
  # @param [String<JMESPath>] expression
@@ -71,7 +72,22 @@ module JMESPath
71
72
 
72
73
  def nud_expref(stream)
73
74
  stream.next
74
- Nodes::Expression.new(expr(stream, 2))
75
+ Nodes::Expression.new(expr(stream, Token::BINDING_POWER[:expref]))
76
+ end
77
+
78
+ def nud_not(stream)
79
+ stream.next
80
+ Nodes::Not.new(expr(stream, Token::BINDING_POWER[:not]))
81
+ end
82
+
83
+ def nud_lparen(stream)
84
+ stream.next
85
+ result = expr(stream, 0)
86
+ if stream.token.type != Lexer::T_RPAREN
87
+ raise Errors::SyntaxError, 'Unclosed `(`'
88
+ end
89
+ stream.next
90
+ result
75
91
  end
76
92
 
77
93
  def nud_filter(stream)
@@ -142,7 +158,7 @@ module JMESPath
142
158
  def led_comparator(stream, left)
143
159
  token = stream.token
144
160
  stream.next
145
- right = expr(stream)
161
+ right = expr(stream, Token::BINDING_POWER[:comparator])
146
162
  Nodes::Comparator.create(token.value, left, right)
147
163
  end
148
164
 
@@ -189,7 +205,11 @@ module JMESPath
189
205
 
190
206
  def led_lparen(stream, left)
191
207
  args = []
192
- name = left.name
208
+ if Nodes::Function::FunctionName === left
209
+ name = left.name
210
+ else
211
+ raise Errors::SyntaxError, 'invalid function invocation'
212
+ end
193
213
  stream.next
194
214
  while stream.token.type != :rparen
195
215
  args << expr(stream, 0)
@@ -198,7 +218,7 @@ module JMESPath
198
218
  end
199
219
  end
200
220
  stream.next
201
- Nodes::Function.create(name, args)
221
+ Nodes::Function.create(name, args, :disable_visit_errors => @disable_visit_errors)
202
222
  end
203
223
 
204
224
  def led_or(stream, left)
@@ -207,6 +227,12 @@ module JMESPath
207
227
  Nodes::Or.new(left, right)
208
228
  end
209
229
 
230
+ def led_and(stream, left)
231
+ stream.next
232
+ right = expr(stream, Token::BINDING_POWER[:or])
233
+ Nodes::And.new(left, right)
234
+ end
235
+
210
236
  def led_pipe(stream, left)
211
237
  stream.next
212
238
  right = expr(stream, Token::BINDING_POWER[:pipe])
@@ -3,7 +3,7 @@ module JMESPath
3
3
  class Runtime
4
4
 
5
5
  # @api private
6
- DEFAULT_PARSER = CachingParser.new
6
+ DEFAULT_PARSER = CachingParser
7
7
 
8
8
  # Constructs a new runtime object for evaluating JMESPath expressions.
9
9
  #
@@ -35,6 +35,11 @@ module JMESPath
35
35
  # caching parser will be used. When `true`, a shared instance of
36
36
  # {CachingParser} is used. Defaults to `true`.
37
37
  #
38
+ # @option options [Boolean] :disable_visit_errors (false) When `true`,
39
+ # no errors will be raised during runtime processing. Parse errors
40
+ # will still be raised, but unexpected data sent to visit will
41
+ # result in nil being returned.
42
+ #
38
43
  # @option options [Parser,CachingParser] :parser
39
44
  #
40
45
  def initialize(options = {})
@@ -58,7 +63,7 @@ module JMESPath
58
63
  if options[:cache_expressions] == false
59
64
  Parser.new(options)
60
65
  else
61
- DEFAULT_PARSER
66
+ DEFAULT_PARSER.new(options)
62
67
  end
63
68
  end
64
69
 
@@ -18,12 +18,14 @@ module JMESPath
18
18
  Lexer::T_EXPREF => 0,
19
19
  Lexer::T_COLON => 0,
20
20
  Lexer::T_PIPE => 1,
21
- Lexer::T_COMPARATOR => 2,
22
- Lexer::T_OR => 5,
23
- Lexer::T_FLATTEN => 6,
21
+ Lexer::T_OR => 2,
22
+ Lexer::T_AND => 3,
23
+ Lexer::T_COMPARATOR => 5,
24
+ Lexer::T_FLATTEN => 9,
24
25
  Lexer::T_STAR => 20,
25
26
  Lexer::T_FILTER => 21,
26
27
  Lexer::T_DOT => 40,
28
+ Lexer::T_NOT => 45,
27
29
  Lexer::T_LBRACE => 50,
28
30
  Lexer::T_LBRACKET => 55,
29
31
  Lexer::T_LPAREN => 60,
@@ -0,0 +1,22 @@
1
+ module JMESPath
2
+ # @api private
3
+ module Util
4
+ class << self
5
+
6
+ # Determines if a value is false as defined by JMESPath:
7
+ #
8
+ # https://github.com/jmespath/jmespath.site/blob/master/docs/proposals/improved-filters.rst#and-expressions-1
9
+ #
10
+ def falsey?(value)
11
+ value.nil? ||
12
+ value === false ||
13
+ value == '' ||
14
+ value == {} ||
15
+ value == [] ||
16
+ (value.respond_to?(:entries) && value.entries.compact.empty?)
17
+ # final case necessary to support Enumerable and Struct
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module JMESPath
2
- VERSION = '1.1.3'
2
+ VERSION = '1.2'
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jmespath
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: '1.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trevor Rowe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-17 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2016-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.8.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.8.1
13
27
  description: Implements JMESPath for Ruby
14
28
  email: trevorrowe@gmail.com
15
29
  executables: []
@@ -22,6 +36,7 @@ files:
22
36
  - lib/jmespath/errors.rb
23
37
  - lib/jmespath/lexer.rb
24
38
  - lib/jmespath/nodes.rb
39
+ - lib/jmespath/nodes/and.rb
25
40
  - lib/jmespath/nodes/comparator.rb
26
41
  - lib/jmespath/nodes/condition.rb
27
42
  - lib/jmespath/nodes/current.rb
@@ -33,6 +48,7 @@ files:
33
48
  - lib/jmespath/nodes/literal.rb
34
49
  - lib/jmespath/nodes/multi_select_hash.rb
35
50
  - lib/jmespath/nodes/multi_select_list.rb
51
+ - lib/jmespath/nodes/not.rb
36
52
  - lib/jmespath/nodes/or.rb
37
53
  - lib/jmespath/nodes/pipe.rb
38
54
  - lib/jmespath/nodes/projection.rb
@@ -42,6 +58,7 @@ files:
42
58
  - lib/jmespath/runtime.rb
43
59
  - lib/jmespath/token.rb
44
60
  - lib/jmespath/token_stream.rb
61
+ - lib/jmespath/util.rb
45
62
  - lib/jmespath/version.rb
46
63
  homepage: http://github.com/trevorrowe/jmespath.rb
47
64
  licenses: