jsonpath 0.5.8 → 1.1.0

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.
@@ -1,17 +1,20 @@
1
- class TestJsonpath < MiniTest::Unit::TestCase
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'phocus'
5
+ require 'jsonpath'
6
+ require 'json'
2
7
 
8
+ class TestJsonpath < MiniTest::Unit::TestCase
3
9
  def setup
4
10
  @object = example_object
5
11
  @object2 = example_object
6
12
  end
7
13
 
8
14
  def test_bracket_matching
9
- assert_raises(ArgumentError) {
10
- JsonPath.new('$.store.book[0')
11
- }
12
- assert_raises(ArgumentError) {
13
- JsonPath.new('$.store.book[0]]')
14
- }
15
+ assert_raises(ArgumentError) { JsonPath.new('$.store.book[0') }
16
+ assert_raises(ArgumentError) { JsonPath.new('$.store.book[0]]') }
17
+ assert_equal [9], JsonPath.new('$.store.book[0].price').on(@object)
15
18
  end
16
19
 
17
20
  def test_lookup_direct_path
@@ -45,13 +48,18 @@ class TestJsonpath < MiniTest::Unit::TestCase
45
48
  end
46
49
 
47
50
  def test_recognize_array_splices
48
- assert_equal [@object['store']['book'][0], @object['store']['book'][1]], JsonPath.new('$..book[0:1:1]').on(@object)
51
+ assert_equal [@object['store']['book'][0]], JsonPath.new('$..book[0:1:1]').on(@object)
52
+ assert_equal [@object['store']['book'][0], @object['store']['book'][1]], JsonPath.new('$..book[0:2:1]').on(@object)
49
53
  assert_equal [@object['store']['book'][1], @object['store']['book'][3], @object['store']['book'][5]], JsonPath.new('$..book[1::2]').on(@object)
50
54
  assert_equal [@object['store']['book'][0], @object['store']['book'][2], @object['store']['book'][4], @object['store']['book'][6]], JsonPath.new('$..book[::2]').on(@object)
51
55
  assert_equal [@object['store']['book'][0], @object['store']['book'][2]], JsonPath.new('$..book[:-5:2]').on(@object)
52
56
  assert_equal [@object['store']['book'][5], @object['store']['book'][6]], JsonPath.new('$..book[5::]').on(@object)
53
57
  end
54
58
 
59
+ def test_slice_array_with_exclusive_end_correctly
60
+ assert_equal [@object['store']['book'][0], @object['store']['book'][1]], JsonPath.new('$..book[:2]').on(@object)
61
+ end
62
+
55
63
  def test_recognize_array_comma
56
64
  assert_equal [@object['store']['book'][0], @object['store']['book'][1]], JsonPath.new('$..book[0,1]').on(@object)
57
65
  assert_equal [@object['store']['book'][2], @object['store']['book'][6]], JsonPath.new('$..book[2,-1::]').on(@object)
@@ -62,16 +70,37 @@ class TestJsonpath < MiniTest::Unit::TestCase
62
70
  assert_equal [@object['store']['book'][0], @object['store']['book'][2]], JsonPath.new("$..book[?(@['price'] < 10)]").on(@object)
63
71
  assert_equal [@object['store']['book'][0], @object['store']['book'][2]], JsonPath.new("$..book[?(@['price'] == 9)]").on(@object)
64
72
  assert_equal [@object['store']['book'][3]], JsonPath.new("$..book[?(@['price'] > 20)]").on(@object)
73
+ assert_equal [
74
+ @object['store']['book'][0],
75
+ @object['store']['book'][4],
76
+ @object['store']['book'][5],
77
+ @object['store']['book'][6]
78
+ ], JsonPath.new("$..book[?(@['category'] != 'fiction')]").on(@object)
65
79
  end
66
80
 
67
- if RUBY_VERSION[/^1\.9/]
68
- def test_recognize_filters_on_val
69
- assert_equal [@object['store']['book'][1]['price'], @object['store']['book'][3]['price'], @object['store']['bicycle']['price']], JsonPath.new("$..price[?(@ > 10)]").on(@object)
70
- end
81
+ def test_or_operator
82
+ assert_equal [@object['store']['book'][1], @object['store']['book'][3]], JsonPath.new("$..book[?(@['price'] == 13 || @['price'] == 23)]").on(@object)
83
+ end
84
+
85
+ def test_and_operator
86
+ assert_equal [], JsonPath.new("$..book[?(@['price'] == 13 && @['price'] == 23)]").on(@object)
87
+ end
88
+
89
+ def test_and_operator_with_more_results
90
+ assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] < 23 && @['price'] > 9)]").on(@object)
91
+ end
92
+
93
+ def test_nested_grouping
94
+ path = "$..book[?((@['price'] == 19 && @['author'] == 'Herman Melville') || @['price'] == 23)]"
95
+ assert_equal [@object['store']['book'][3]], JsonPath.new(path).on(@object)
71
96
  end
72
97
 
73
- def test_no_eval
74
- assert_equal [], JsonPath.new('$..book[(@.length-2)]', :allow_eval => false).on(@object)
98
+ def test_eval_with_floating_point_and_and
99
+ assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] < 23.0 && @['price'] > 9.0)]").on(@object)
100
+ end
101
+
102
+ def test_eval_with_floating_point
103
+ assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] == 13.0)]").on(@object)
75
104
  end
76
105
 
77
106
  def test_paths_with_underscores
@@ -79,13 +108,98 @@ class TestJsonpath < MiniTest::Unit::TestCase
79
108
  end
80
109
 
81
110
  def test_path_with_hyphens
82
- assert_equal [@object['store']['bicycle']['single-speed']], JsonPath.new('$.store.bicycle.single-speed').on(@object)
111
+ assert_equal [@object['store']['bicycle']['single-speed']], JsonPath.new('$.store.bicycle.single-speed').on(@object)
112
+ end
113
+
114
+ def test_path_with_colon
115
+ assert_equal [@object['store']['bicycle']['make:model']], JsonPath.new('$.store.bicycle.make:model').on(@object)
83
116
  end
84
117
 
85
118
  def test_paths_with_numbers
86
119
  assert_equal [@object['store']['bicycle']['2seater']], JsonPath.new('$.store.bicycle.2seater').on(@object)
87
120
  end
88
121
 
122
+ def test_recognized_dot_notation_in_filters
123
+ assert_equal [@object['store']['book'][2], @object['store']['book'][3]], JsonPath.new('$..book[?(@.isbn)]').on(@object)
124
+ end
125
+
126
+ def test_works_on_non_hash
127
+ klass = Struct.new(:a, :b)
128
+ object = klass.new('some', 'value')
129
+
130
+ assert_equal ['value'], JsonPath.new('$.b').on(object)
131
+ end
132
+
133
+ def test_works_on_object
134
+ klass = Class.new{
135
+ attr_reader :b
136
+ def initialize(b)
137
+ @b = b
138
+ end
139
+ }
140
+ object = klass.new("value")
141
+
142
+ assert_equal ["value"], JsonPath.new('$.b').on(object)
143
+ end
144
+
145
+ def test_works_on_object_can_be_disabled
146
+ klass = Class.new{
147
+ attr_reader :b
148
+ def initialize(b)
149
+ @b = b
150
+ end
151
+ }
152
+ object = klass.new("value")
153
+
154
+ assert_equal [], JsonPath.new('$.b', allow_send: false).on(object)
155
+ end
156
+
157
+ def test_works_on_diggable
158
+ klass = Class.new{
159
+ attr_reader :h
160
+ def initialize(h)
161
+ @h = h
162
+ end
163
+ def dig(*keys)
164
+ @h.dig(*keys)
165
+ end
166
+ }
167
+
168
+ object = klass.new('a' => 'some', 'b' => 'value')
169
+ assert_equal ['value'], JsonPath.new('$.b').on(object)
170
+
171
+ object = {
172
+ "foo" => klass.new('a' => 'some', 'b' => 'value')
173
+ }
174
+ assert_equal ['value'], JsonPath.new('$.foo.b').on(object)
175
+ end
176
+
177
+ def test_works_on_non_hash_with_filters
178
+ klass = Struct.new(:a, :b)
179
+ first_object = klass.new('some', 'value')
180
+ second_object = klass.new('next', 'other value')
181
+
182
+ assert_equal ['other value'], JsonPath.new('$[?(@.a == "next")].b').on([first_object, second_object])
183
+ end
184
+
185
+ def test_works_on_hash_with_summary
186
+ object = {
187
+ "foo" => [{
188
+ "a" => "some",
189
+ "b" => "value"
190
+ }]
191
+ }
192
+ assert_equal [{ "b" => "value" }], JsonPath.new("$.foo[*](b)").on(object)
193
+ end
194
+
195
+ def test_works_on_non_hash_with_summary
196
+ klass = Struct.new(:a, :b)
197
+ object = {
198
+ "foo" => [klass.new("some", "value")]
199
+ }
200
+ assert_equal [{ "b" => "value" }], JsonPath.new("$.foo[*](b)").on(object)
201
+ end
202
+
89
203
  def test_recognize_array_with_evald_index
90
204
  assert_equal [@object['store']['book'][2]], JsonPath.new('$..book[(@.length-5)]').on(@object)
91
205
  end
@@ -95,11 +209,11 @@ class TestJsonpath < MiniTest::Unit::TestCase
95
209
  end
96
210
 
97
211
  def test_counting
98
- assert_equal 49, JsonPath.new('$..*').on(@object).to_a.size
212
+ assert_equal 57, JsonPath.new('$..*').on(@object).to_a.size
99
213
  end
100
214
 
101
215
  def test_space_in_path
102
- assert_equal ['e'], JsonPath.new("$.'c d'").on({"a" => "a","b" => "b", "c d" => "e"})
216
+ assert_equal ['e'], JsonPath.new("$.'c d'").on('a' => 'a', 'b' => 'b', 'c d' => 'e')
103
217
  end
104
218
 
105
219
  def test_class_method
@@ -129,103 +243,913 @@ class TestJsonpath < MiniTest::Unit::TestCase
129
243
  end
130
244
 
131
245
  def test_weird_gsub!
132
- h = {'hi' => 'there'}
133
- JsonPath.for(@object).gsub!('$.*') { |n| h }
246
+ h = { 'hi' => 'there' }
247
+ JsonPath.for(@object).gsub!('$.*') { |_| h }
134
248
  assert_equal h, @object
135
249
  end
136
250
 
251
+ def test_gsub_to_false!
252
+ h = { 'hi' => 'there' }
253
+ h2 = { 'hi' => false }
254
+ assert_equal h2, JsonPath.for(h).gsub!('$.hi') { |_| false }.to_hash
255
+ end
256
+
257
+ def test_where_selector
258
+ JsonPath.for(@object).gsub!('$..book.price[?(@ > 20)]') { |p| p + 10 }
259
+ end
260
+
137
261
  def test_compact
138
- h = {'hi' => 'there', 'you' => nil}
262
+ h = { 'hi' => 'there', 'you' => nil }
139
263
  JsonPath.for(h).compact!
140
- assert_equal({'hi' => 'there'}, h)
264
+ assert_equal({ 'hi' => 'there' }, h)
141
265
  end
142
266
 
143
267
  def test_delete
144
- h = {'hi' => 'there', 'you' => nil}
268
+ h = { 'hi' => 'there', 'you' => nil }
145
269
  JsonPath.for(h).delete!('*.hi')
146
- assert_equal({'you' => nil}, h)
270
+ assert_equal({ 'you' => nil }, h)
271
+ end
272
+
273
+ def test_delete_2
274
+ json = { 'store' => {
275
+ 'book' => [
276
+ { 'category' => 'reference',
277
+ 'author' => 'Nigel Rees',
278
+ 'title' => 'Sayings of the Century',
279
+ 'price' => 9,
280
+ 'tags' => %w[asdf asdf2] },
281
+ { 'category' => 'fiction',
282
+ 'author' => 'Evelyn Waugh',
283
+ 'title' => 'Sword of Honour',
284
+ 'price' => 13 },
285
+ { 'category' => 'fiction',
286
+ 'author' => 'Aasdf',
287
+ 'title' => 'Aaasdf2',
288
+ 'price' => 1 }
289
+ ]
290
+ } }
291
+ json_deleted = { 'store' => {
292
+ 'book' => [
293
+ { 'category' => 'fiction',
294
+ 'author' => 'Evelyn Waugh',
295
+ 'title' => 'Sword of Honour',
296
+ 'price' => 13 },
297
+ { 'category' => 'fiction',
298
+ 'author' => 'Aasdf',
299
+ 'title' => 'Aaasdf2',
300
+ 'price' => 1 }
301
+ ]
302
+ } }
303
+ assert_equal(json_deleted, JsonPath.for(json).delete("$..store.book[?(@.category == 'reference')]").obj)
304
+ end
305
+
306
+ def test_delete_3
307
+ json = { 'store' => {
308
+ 'book' => [
309
+ { 'category' => 'reference',
310
+ 'author' => 'Nigel Rees',
311
+ 'title' => 'Sayings of the Century',
312
+ 'price' => 9,
313
+ 'tags' => %w[asdf asdf2],
314
+ 'this' => {
315
+ 'delete_me' => [
316
+ 'no' => 'do not'
317
+ ]
318
+ } },
319
+ { 'category' => 'fiction',
320
+ 'author' => 'Evelyn Waugh',
321
+ 'title' => 'Sword of Honour',
322
+ 'price' => 13 },
323
+ { 'category' => 'fiction',
324
+ 'author' => 'Aasdf',
325
+ 'title' => 'Aaasdf2',
326
+ 'price' => 1 }
327
+ ]
328
+ } }
329
+ json_deleted = { 'store' => {
330
+ 'book' => [
331
+ { 'category' => 'reference',
332
+ 'author' => 'Nigel Rees',
333
+ 'title' => 'Sayings of the Century',
334
+ 'price' => 9,
335
+ 'tags' => %w[asdf asdf2],
336
+ 'this' => {} },
337
+ { 'category' => 'fiction',
338
+ 'author' => 'Evelyn Waugh',
339
+ 'title' => 'Sword of Honour',
340
+ 'price' => 13 },
341
+ { 'category' => 'fiction',
342
+ 'author' => 'Aasdf',
343
+ 'title' => 'Aaasdf2',
344
+ 'price' => 1 }
345
+ ]
346
+ } }
347
+ assert_equal(json_deleted, JsonPath.for(json).delete('$..store.book..delete_me').obj)
348
+ end
349
+
350
+ def test_delete_for_array
351
+ before = JsonPath.on(@object, '$..store.book[1]')
352
+ JsonPath.for(@object).delete!('$..store.book[0]')
353
+ after = JsonPath.on(@object, '$..store.book[0]')
354
+ assert_equal(after, before, 'Before is the second element. After should have been equal to the next element after delete.')
355
+ end
356
+
357
+ def test_at_sign_in_json_element
358
+ data =
359
+ { '@colors' =>
360
+ [{ '@r' => 255, '@g' => 0, '@b' => 0 },
361
+ { '@r' => 0, '@g' => 255, '@b' => 0 },
362
+ { '@r' => 0, '@g' => 0, '@b' => 255 }] }
363
+
364
+ assert_equal [255, 0, 0], JsonPath.on(data, '$..@r')
147
365
  end
148
366
 
149
367
  def test_wildcard
150
- assert_equal @object['store']['book'].collect{|e| e['price']}.compact, JsonPath.on(@object, '$..book[*].price')
368
+ assert_equal @object['store']['book'].collect { |e| e['price'] }.compact, JsonPath.on(@object, '$..book[*].price')
369
+ end
370
+
371
+ def test_wildcard_on_intermediary_element
372
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'c' => 1 } } }, '$.a..c')
373
+ end
374
+
375
+ def test_wildcard_on_intermediary_element_v2
376
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'd' => { 'c' => 1 } } } }, '$.a..c')
377
+ end
378
+
379
+ def test_wildcard_on_intermediary_element_v3
380
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'd' => { 'c' => 1 } } } }, '$.a.*..c')
381
+ end
382
+
383
+ def test_wildcard_on_intermediary_element_v4
384
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'd' => { 'c' => 1 } } } }, '$.a.*..c')
385
+ end
386
+
387
+ def test_wildcard_on_intermediary_element_v5
388
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'c' => 1 } } }, '$.a.*.c')
389
+ end
390
+
391
+ def test_wildcard_on_intermediary_element_v6
392
+ assert_equal ['red'], JsonPath.new('$.store.*.color').on(@object)
151
393
  end
152
394
 
153
395
  def test_wildcard_empty_array
154
- object = @object.merge("bicycle" => { "tire" => [] })
155
- assert_equal [], JsonPath.on(object, "$..bicycle.tire[*]")
396
+ object = @object.merge('bicycle' => { 'tire' => [] })
397
+ assert_equal [], JsonPath.on(object, '$..bicycle.tire[*]')
156
398
  end
157
399
 
158
- def test_support_filter_by_childnode_value
159
- assert_equal [@object['store']['book'][3]], JsonPath.new("$..book[?(@.price > 20)]").on(@object)
400
+ def test_support_filter_by_array_childnode_value
401
+ assert_equal [@object['store']['book'][3]], JsonPath.new('$..book[?(@.price > 20)]').on(@object)
160
402
  end
161
403
 
162
404
  def test_support_filter_by_childnode_value_with_inconsistent_children
163
- @object['store']['book'][0] = "string_instead_of_object"
164
- assert_equal [@object['store']['book'][3]], JsonPath.new("$..book[?(@.price > 20)]").on(@object)
405
+ @object['store']['book'][0] = 'string_instead_of_object'
406
+ assert_equal [@object['store']['book'][3]], JsonPath.new('$..book[?(@.price > 20)]').on(@object)
165
407
  end
166
408
 
167
409
  def test_support_filter_by_childnode_value_and_select_child_key
168
- assert_equal [23], JsonPath.new("$..book[?(@.price > 20)].price").on(@object)
410
+ assert_equal [23], JsonPath.new('$..book[?(@.price > 20)].price').on(@object)
169
411
  end
170
412
 
171
413
  def test_support_filter_by_childnode_value_over_childnode_and_select_child_key
172
- assert_equal ["Osennie Vizity"], JsonPath.new("$..book[?(@.written.year == 1996)].title").on(@object)
414
+ assert_equal ['Osennie Vizity'], JsonPath.new('$..book[?(@.written.year == 1996)].title').on(@object)
173
415
  end
174
-
175
- def example_object
176
- { "store"=> {
177
- "book" => [
178
- { "category"=> "reference",
179
- "author"=> "Nigel Rees",
180
- "title"=> "Sayings of the Century",
181
- "price"=> 9
416
+
417
+ def test_support_filter_by_object_childnode_value
418
+ data = {
419
+ 'data' => {
420
+ 'type' => 'users',
421
+ 'id' => '123'
422
+ }
423
+ }
424
+ assert_equal [{ 'type' => 'users', 'id' => '123' }], JsonPath.new("$.data[?(@.type == 'users')]").on(data)
425
+ assert_equal [], JsonPath.new("$.[?(@.type == 'admins')]").on(data)
426
+ end
427
+
428
+ def test_support_at_sign_in_member_names
429
+ assert_equal [@object['store']['@id']], JsonPath.new('$.store.@id').on(@object)
430
+ end
431
+
432
+ def test_support_dollar_sign_in_member_names
433
+ assert_equal [@object['store']['$meta-data']],
434
+ JsonPath.new('$.store.$meta-data').on(@object)
435
+ end
436
+
437
+ def test_support_underscore_in_member_names
438
+ assert_equal [@object['store']['_links']],
439
+ JsonPath.new('$.store._links').on(@object)
440
+ end
441
+
442
+ def test_dig_return_string
443
+ assert_equal ['asdf'], JsonPath.new("$.store.book..tags[?(@ == 'asdf')]").on(@object)
444
+ assert_equal [], JsonPath.new("$.store.book..tags[?(@ == 'not_asdf')]").on(@object)
445
+ end
446
+
447
+ def test_slash_in_value
448
+ data = {
449
+ 'data' => [{
450
+ 'type' => 'mps/awesome'
451
+ }, {
452
+ 'type' => 'not'
453
+ }]
454
+ }
455
+ assert_equal [{ 'type' => 'mps/awesome' }], JsonPath.new('$.data[?(@.type == "mps/awesome")]').on(data)
456
+ end
457
+
458
+ def test_floating_point_with_precision_marker
459
+ data = {
460
+ 'data' => {
461
+ 'type' => 0.00001
462
+ }
463
+ }
464
+ assert_equal [{ 'type' => 0.00001 }], JsonPath.new('$.data[?(@.type == 0.00001)]').on(data)
465
+ end
466
+
467
+ def test_digits_only_string
468
+ data = {
469
+ 'foo' => {
470
+ 'type' => 'users',
471
+ 'id' => '123'
472
+ }
473
+ }
474
+ assert_equal([{ 'type' => 'users', 'id' => '123' }], JsonPath.new("$.foo[?(@.id == '123')]").on(data))
475
+ end
476
+
477
+ def test_digits_only_string_in_array
478
+ data = {
479
+ 'foo' => [{
480
+ 'type' => 'users',
481
+ 'id' => '123'
482
+ }, {
483
+ 'type' => 'users',
484
+ 'id' => '321'
485
+ }]
486
+ }
487
+ assert_equal([{ 'type' => 'users', 'id' => '123' }], JsonPath.new("$.foo[?(@.id == '123')]").on(data))
488
+ end
489
+
490
+ def test_at_in_filter
491
+ jsonld = {
492
+ 'mentions' => [
493
+ {
494
+ 'name' => 'Delimara Powerplant',
495
+ 'identifier' => 'krzana://took/powerstation/Delimara Powerplant',
496
+ '@type' => 'Place',
497
+ 'geo' => {
498
+ 'latitude' => 35.83020073454,
499
+ 'longitude' => 14.55602645874
500
+ }
501
+ }
502
+ ]
503
+ }
504
+ assert_equal(['Place'], JsonPath.new("$..mentions[?(@['@type'] == 'Place')].@type").on(jsonld))
505
+ end
506
+
507
+ def test_dollar_in_filter
508
+ jsonld = {
509
+ 'mentions' => [
510
+ {
511
+ 'name' => 'Delimara Powerplant',
512
+ 'identifier' => 'krzana://took/powerstation/Delimara Powerplant',
513
+ '$type' => 'Place',
514
+ 'geo' => {
515
+ 'latitude' => 35.83020073454,
516
+ 'longitude' => 14.55602645874
517
+ }
518
+ }
519
+ ]
520
+ }
521
+ assert_equal(['Place'], JsonPath.new("$..mentions[?(@['$type'] == 'Place')].$type").on(jsonld))
522
+ end
523
+
524
+ def test_underscore_in_filter
525
+ jsonld = {
526
+ 'attributes' => [
527
+ {
528
+ 'store' => [
529
+ { 'with' => 'urn' },
530
+ { 'with_underscore' => 'urn:1' }
531
+ ]
532
+ }
533
+ ]
534
+ }
535
+ assert_equal(['urn:1'], JsonPath.new("$.attributes..store[?(@['with_underscore'] == 'urn:1')].with_underscore").on(jsonld))
536
+ end
537
+
538
+ def test_at_in_value
539
+ jsonld = {
540
+ 'mentions' =>
541
+ {
542
+ 'name' => 'Delimara Powerplant',
543
+ 'identifier' => 'krzana://took/powerstation/Delimara Powerplant',
544
+ 'type' => '@Place',
545
+ 'geo' => {
546
+ 'latitude' => 35.83020073454,
547
+ 'longitude' => 14.55602645874
548
+ }
549
+ }
550
+ }
551
+ assert_equal(['@Place'], JsonPath.new("$..mentions.type[?(@ == '@Place')]").on(jsonld))
552
+ end
553
+
554
+ def test_parens_in_value
555
+ data = {
556
+ 'data' => {
557
+ 'number' => '(492) 080-3961'
558
+ }
559
+ }
560
+ assert_equal [{ 'number' => '(492) 080-3961' }], JsonPath.new("$.data[?(@.number == '(492) 080-3961')]").on(data)
561
+ end
562
+
563
+ def test_boolean_parameter_value
564
+ data = {
565
+ 'data' => [{
566
+ 'isTrue' => true,
567
+ 'name' => 'testname1'
568
+ }, {
569
+ 'isTrue' => false,
570
+ 'name' => 'testname2'
571
+ }]
572
+ }
573
+ assert_equal [{ 'isTrue' => true, 'name' => 'testname1' }], JsonPath.new('$.data[?(@.isTrue)]').on(data)
574
+ end
575
+
576
+ def test_regex_simple
577
+ assert_equal %w[asdf asdf2], JsonPath.new('$.store.book..tags[?(@ =~ /asdf/)]').on(@object)
578
+ end
579
+
580
+ def test_regex_simple_miss
581
+ assert_equal [], JsonPath.new('$.store.book..tags[?(@ =~ /wut/)]').on(@object)
582
+ end
583
+
584
+ def test_regex_r
585
+ assert_equal %w[asdf asdf2], JsonPath.new('$.store.book..tags[?(@ =~ %r{asdf})]').on(@object)
586
+ end
587
+
588
+ def test_regex_flags
589
+ assert_equal [
590
+ @object['store']['book'][2],
591
+ @object['store']['book'][4],
592
+ @object['store']['book'][5],
593
+ @object['store']['book'][6]
594
+ ], JsonPath.new('$..book[?(@.author =~ /herman|lukyanenko/i)]').on(@object)
595
+ end
596
+
597
+ def test_regex_error
598
+ assert_raises ArgumentError do
599
+ JsonPath.new('$.store.book..tags[?(@ =~ asdf)]').on(@object)
600
+ end
601
+ end
602
+
603
+ def test_regression_1
604
+ json = {
605
+ ok: true,
606
+ channels: [
607
+ {
608
+ id: 'C09C5GYHF',
609
+ name: 'general'
610
+ },
611
+ {
612
+ id: 'C09C598QL',
613
+ name: 'random'
614
+ }
615
+ ]
616
+ }.to_json
617
+
618
+ assert_equal 'C09C5GYHF', JsonPath.on(json, "$..channels[?(@.name == 'general')].id")[0]
619
+ end
620
+
621
+ def test_regression_2
622
+ json = {
623
+ ok: true,
624
+ channels: [
625
+ {
626
+ id: 'C09C5GYHF',
627
+ name: 'general',
628
+ is_archived: false
182
629
  },
183
- { "category"=> "fiction",
184
- "author"=> "Evelyn Waugh",
185
- "title"=> "Sword of Honour",
186
- "price"=> 13
630
+ {
631
+ id: 'C09C598QL',
632
+ name: 'random',
633
+ is_archived: true
634
+ }
635
+ ]
636
+ }.to_json
637
+
638
+ assert_equal 'C09C5GYHF', JsonPath.on(json, '$..channels[?(@.is_archived == false)].id')[0]
639
+ end
640
+
641
+ def test_regression_3
642
+ json = {
643
+ ok: true,
644
+ channels: [
645
+ {
646
+ id: 'C09C5GYHF',
647
+ name: 'general',
648
+ is_archived: false
187
649
  },
188
- { "category"=> "fiction",
189
- "author"=> "Herman Melville",
190
- "title"=> "Moby Dick",
191
- "isbn"=> "0-553-21311-3",
192
- "price"=> 9
650
+ {
651
+ id: 'C09C598QL',
652
+ name: 'random',
653
+ is_archived: true
654
+ }
655
+ ]
656
+ }.to_json
657
+
658
+ assert_equal 'C09C598QL', JsonPath.on(json, '$..channels[?(@.is_archived)].id')[0]
659
+ end
660
+
661
+ def test_regression_4
662
+ json = {
663
+ ok: true,
664
+ channels: [
665
+ {
666
+ id: 'C09C5GYHF',
667
+ name: 'general',
668
+ is_archived: false
193
669
  },
194
- { "category"=> "fiction",
195
- "author"=> "J. R. R. Tolkien",
196
- "title"=> "The Lord of the Rings",
197
- "isbn"=> "0-395-19395-8",
198
- "price"=> 23
670
+ {
671
+ id: 'C09C598QL',
672
+ name: 'random',
673
+ is_archived: true
674
+ }
675
+ ]
676
+ }.to_json
677
+
678
+ assert_equal ['C09C5GYHF'], JsonPath.on(json, "$..channels[?(@.name == 'general')].id")
679
+ end
680
+
681
+ def test_regression_5
682
+ json = {
683
+ ok: true,
684
+ channels: [
685
+ {
686
+ id: 'C09C5GYHF',
687
+ name: 'general',
688
+ is_archived: 'false'
199
689
  },
200
- { "category"=> "russian_fiction",
201
- "author"=> "Lukyanenko",
202
- "title"=> "Imperatory Illuziy",
203
- "written" => {
204
- "year" => 1995
205
- }
690
+ {
691
+ id: 'C09C598QL',
692
+ name: 'random',
693
+ is_archived: true
694
+ }
695
+ ]
696
+ }.to_json
697
+
698
+ assert_equal 'C09C5GYHF', JsonPath.on(json, "$..channels[?(@.is_archived == 'false')].id")[0]
699
+ end
700
+
701
+ def test_quote
702
+ json = {
703
+ channels: [
704
+ {
705
+ name: "King's Speech"
706
+ }
707
+ ]
708
+ }.to_json
709
+
710
+ assert_equal [{ 'name' => "King\'s Speech" }], JsonPath.on(json, "$..channels[?(@.name == 'King\'s Speech')]")
711
+ end
712
+
713
+ def test_curly_brackets
714
+ data = {
715
+ '{data}' => 'data'
716
+ }
717
+ assert_equal ['data'], JsonPath.new('$.{data}').on(data)
718
+ end
719
+
720
+ def test_symbolize
721
+ data = '
722
+ {
723
+ "store": {
724
+ "bicycle": {
725
+ "price": 19.95,
726
+ "color": "red"
206
727
  },
207
- { "category"=> "russian_fiction",
208
- "author"=> "Lukyanenko",
209
- "title"=> "Osennie Vizity",
210
- "written" => {
211
- "year" => 1996
728
+ "book": [
729
+ {
730
+ "price": 8.95,
731
+ "category": "reference",
732
+ "title": "Sayings of the Century",
733
+ "author": "Nigel Rees"
734
+ },
735
+ {
736
+ "price": 12.99,
737
+ "category": "fiction",
738
+ "title": "Sword of Honour",
739
+ "author": "Evelyn Waugh"
740
+ },
741
+ {
742
+ "price": 8.99,
743
+ "category": "fiction",
744
+ "isbn": "0-553-21311-3",
745
+ "title": "Moby Dick",
746
+ "author": "Herman Melville",
747
+ "color": "blue"
748
+ },
749
+ {
750
+ "price": 22.99,
751
+ "category": "fiction",
752
+ "isbn": "0-395-19395-8",
753
+ "title": "The Lord of the Rings",
754
+ "author": "Tolkien"
212
755
  }
213
- },
214
- { "category"=> "russian_fiction",
215
- "author"=> "Lukyanenko",
216
- "title"=> "Ne vremya dlya drakonov",
217
- "written" => {
218
- "year" => 1997
756
+ ]
757
+ }
758
+ }
759
+ '
760
+ assert_equal [{ price: 8.95, category: 'reference', title: 'Sayings of the Century', author: 'Nigel Rees' }, { price: 8.99, category: 'fiction', isbn: '0-553-21311-3', title: 'Moby Dick', author: 'Herman Melville', color: 'blue' }], JsonPath.new('$..book[::2]').on(data, symbolize_keys: true)
761
+ end
762
+
763
+ def test_changed
764
+ json =
765
+ {
766
+ 'snapshot' => {
767
+ 'objects' => {
768
+ 'whatever' => [
769
+ {
770
+ 'column' => {
771
+ 'name' => 'ASSOCIATE_FLAG',
772
+ 'nullable' => true
773
+ }
774
+ },
775
+ {
776
+ 'column' => {
777
+ 'name' => 'AUTHOR',
778
+ 'nullable' => false
779
+ }
780
+ }
781
+ ]
219
782
  }
220
783
  }
784
+ }
785
+ assert_equal true, JsonPath.on(json, "$..column[?(@.name == 'ASSOCIATE_FLAG')].nullable")[0]
786
+ end
787
+
788
+ def test_another
789
+ json = {
790
+ initial: true,
791
+ not: true
792
+ }.to_json
793
+ assert_equal [{ 'initial' => true, 'not' => true }], JsonPath.on(json, '$.[?(@.initial == true)]')
794
+ json = {
795
+ initial: false,
796
+ not: true
797
+ }.to_json
798
+ assert_equal [], JsonPath.on(json, '$.initial[?(@)]')
799
+ assert_equal [], JsonPath.on(json, '$.[?(@.initial == true)]')
800
+ assert_equal [{ 'initial' => false, 'not' => true }], JsonPath.on(json, '$.[?(@.initial == false)]')
801
+ json = {
802
+ initial: 'false',
803
+ not: true
804
+ }.to_json
805
+ assert_equal [{ 'initial' => 'false', 'not' => true }], JsonPath.on(json, "$.[?(@.initial == 'false')]")
806
+ assert_equal [], JsonPath.on(json, '$.[?(@.initial == false)]')
807
+ end
808
+
809
+ def test_hanging
810
+ json = { initial: true }.to_json
811
+ success_path = '$.initial'
812
+ assert_equal [true], JsonPath.on(json, success_path)
813
+ broken_path = "$.initial\n"
814
+ assert_equal [true], JsonPath.on(json, broken_path)
815
+ end
816
+
817
+ def test_complex_nested_grouping
818
+ path = "$..book[?((@['author'] == 'Evelyn Waugh' || @['author'] == 'Herman Melville') && (@['price'] == 33 || @['price'] == 9))]"
819
+ assert_equal [@object['store']['book'][2]], JsonPath.new(path).on(@object)
820
+ end
821
+
822
+ def test_complex_nested_grouping_unmatched_parent
823
+ path = "$..book[?((@['author'] == 'Evelyn Waugh' || @['author'] == 'Herman Melville' && (@['price'] == 33 || @['price'] == 9))]"
824
+ err = assert_raises(ArgumentError, 'should have raised an exception') { JsonPath.new(path).on(@object) }
825
+ assert_match(/unmatched parenthesis in expression: \(\(false \|\| false && \(false \|\| true\)\)/, err.message)
826
+ end
827
+
828
+ def test_runtime_error_frozen_string
829
+ skip('in ruby version below 2.2.0 this error is not raised') if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.0') || Gem::Version.new(RUBY_VERSION) > Gem::Version::new('2.6')
830
+ json = '
831
+ {
832
+ "test": "something"
833
+ }
834
+ '.to_json
835
+ assert_raises(ArgumentError, "RuntimeError: character '|' not supported in query") do
836
+ JsonPath.on(json, '$.description|title')
837
+ end
838
+ end
839
+
840
+ def test_delete_more_items
841
+ a = { 'itemList' =>
842
+ [{ 'alfa' => 'beta1' },
843
+ { 'alfa' => 'beta2' },
844
+ { 'alfa' => 'beta3' },
845
+ { 'alfa' => 'beta4' },
846
+ { 'alfa' => 'beta5' },
847
+ { 'alfa' => 'beta6' },
848
+ { 'alfa' => 'beta7' },
849
+ { 'alfa' => 'beta8' },
850
+ { 'alfa' => 'beta9' },
851
+ { 'alfa' => 'beta10' },
852
+ { 'alfa' => 'beta11' },
853
+ { 'alfa' => 'beta12' }] }
854
+ expected = { 'itemList' => [{ 'alfa' => 'beta1' }] }
855
+ assert_equal expected, JsonPath.for(a.to_json).delete('$.itemList[1:12:1]').to_hash
856
+ end
857
+
858
+ def test_delete_more_items_with_stepping
859
+ a = { 'itemList' =>
860
+ [{ 'alfa' => 'beta1' },
861
+ { 'alfa' => 'beta2' },
862
+ { 'alfa' => 'beta3' },
863
+ { 'alfa' => 'beta4' },
864
+ { 'alfa' => 'beta5' },
865
+ { 'alfa' => 'beta6' },
866
+ { 'alfa' => 'beta7' },
867
+ { 'alfa' => 'beta8' },
868
+ { 'alfa' => 'beta9' },
869
+ { 'alfa' => 'beta10' },
870
+ { 'alfa' => 'beta11' },
871
+ { 'alfa' => 'beta12' }] }
872
+ expected = { 'itemList' =>
873
+ [{ 'alfa' => 'beta1' },
874
+ { 'alfa' => 'beta3' },
875
+ { 'alfa' => 'beta5' },
876
+ { 'alfa' => 'beta7' },
877
+ { 'alfa' => 'beta8' },
878
+ { 'alfa' => 'beta9' },
879
+ { 'alfa' => 'beta10' },
880
+ { 'alfa' => 'beta11' },
881
+ { 'alfa' => 'beta12' }] }
882
+ assert_equal expected, JsonPath.for(a.to_json).delete('$.itemList[1:6:2]').to_hash
883
+ end
884
+
885
+ def test_nested_values
886
+ json = '
887
+ {
888
+ "phoneNumbers": [
889
+ [{
890
+ "type" : "iPhone",
891
+ "number": "0123-4567-8888"
892
+ }],
893
+ [{
894
+ "type" : "home",
895
+ "number": "0123-4567-8910"
896
+ }]
897
+ ]
898
+ }
899
+ '.to_json
900
+ assert_equal [[{ 'type' => 'home', 'number' => '0123-4567-8910' }]], JsonPath.on(json, "$.phoneNumbers[?(@[0].type == 'home')]")
901
+ assert_equal [], JsonPath.on(json, "$.phoneNumbers[?(@[2].type == 'home')]")
902
+ json = '
903
+ {
904
+ "phoneNumbers":
905
+ {
906
+ "type" : "iPhone",
907
+ "number": "0123-4567-8888"
908
+ }
909
+ }
910
+ '.to_json
911
+ assert_equal [], JsonPath.on(json, "$.phoneNumbers[?(@[0].type == 'home')]")
912
+ end
913
+
914
+ def test_selecting_multiple_keys_on_hash
915
+ json = '
916
+ {
917
+ "category": "reference",
918
+ "author": "Nigel Rees",
919
+ "title": "Sayings of the Century",
920
+ "price": 8.95
921
+ }
922
+ '.to_json
923
+ assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, '$.(category,author)')
924
+ end
925
+
926
+ def test_selecting_multiple_keys_on_sub_hash
927
+ skip("Failing as the semantics of .(x,y) is unclear")
928
+ json = '
929
+ {
930
+ "book": {
931
+ "category": "reference",
932
+ "author": "Nigel Rees",
933
+ "title": "Sayings of the Century",
934
+ "price": 8.95
935
+ }
936
+ }
937
+ '.to_json
938
+ assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, '$.book.(category,author)')
939
+ end
940
+
941
+ def test_selecting_multiple_keys_on_array
942
+ json = '
943
+ {
944
+ "store": {
945
+ "book": [
946
+ {
947
+ "category": "reference",
948
+ "author": "Nigel Rees",
949
+ "title": "Sayings of the Century",
950
+ "price": 8.95
951
+ },
952
+ {
953
+ "category": "fiction",
954
+ "author": "Evelyn Waugh",
955
+ "title": "Sword of Honour",
956
+ "price": 12.99
957
+ }
958
+ ]
959
+ }
960
+ }
961
+ '.to_json
962
+
963
+ assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }, { 'category' => 'fiction', 'author' => 'Evelyn Waugh' }], JsonPath.on(json, '$.store.book[*](category,author)')
964
+ end
965
+
966
+ def test_selecting_multiple_keys_on_array_with_filter
967
+ json = '
968
+ {
969
+ "store": {
970
+ "book": [
971
+ {
972
+ "category": "reference",
973
+ "author": "Nigel Rees",
974
+ "title": "Sayings of the Century",
975
+ "price": 8.95
976
+ },
977
+ {
978
+ "category": "fiction",
979
+ "author": "Evelyn Waugh",
980
+ "title": "Sword of Honour",
981
+ "price": 12.99
982
+ }
983
+ ]
984
+ }
985
+ }
986
+ '.to_json
987
+
988
+ assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, "$.store.book[?(@['price'] == 8.95)](category,author)")
989
+ assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, "$.store.book[?(@['price'] == 8.95)]( category, author )")
990
+ end
991
+
992
+ def test_selecting_multiple_keys_with_filter_with_space_in_catergory
993
+ json = '
994
+ {
995
+ "store": {
996
+ "book": [
997
+ {
998
+ "cate gory": "reference",
999
+ "author": "Nigel Rees",
1000
+ "title": "Sayings of the Century",
1001
+ "price": 8.95
1002
+ },
1003
+ {
1004
+ "cate gory": "fiction",
1005
+ "author": "Evelyn Waugh",
1006
+ "title": "Sword of Honour",
1007
+ "price": 12.99
1008
+ }
1009
+ ]
1010
+ }
1011
+ }
1012
+ '.to_json
1013
+
1014
+ assert_equal [{ 'cate gory' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, "$.store.book[?(@['price'] == 8.95)]( cate gory, author )")
1015
+ end
1016
+
1017
+ def test_use_symbol_opt
1018
+ json = {
1019
+ store: {
1020
+ book: [
1021
+ {
1022
+ category: "reference",
1023
+ author: "Nigel Rees",
1024
+ title: "Sayings of the Century",
1025
+ price: 8.95
1026
+ },
1027
+ {
1028
+ category: "fiction",
1029
+ author: "Evelyn Waugh",
1030
+ title: "Sword of Honour",
1031
+ price: 12.99
1032
+ }
1033
+ ]
1034
+ }
1035
+ }
1036
+ on = ->(path){ JsonPath.on(json, path, use_symbols: true) }
1037
+ assert_equal ['reference', 'fiction'], on.("$.store.book[*].category")
1038
+ assert_equal ['reference', 'fiction'], on.("$..category")
1039
+ assert_equal ['reference'], on.("$.store.book[?(@['price'] == 8.95)].category")
1040
+ assert_equal [{'category' => 'reference'}], on.("$.store.book[?(@['price'] == 8.95)](category)")
1041
+ end
1042
+
1043
+ def test_object_method_send
1044
+ j = {height: 5, hash: "some_hash"}.to_json
1045
+ hs = JsonPath.new "$..send"
1046
+ assert_equal([], hs.on(j))
1047
+ hs = JsonPath.new "$..hash"
1048
+ assert_equal(["some_hash"], hs.on(j))
1049
+ hs = JsonPath.new "$..send"
1050
+ assert_equal([], hs.on(j))
1051
+ j = {height: 5, send: "should_still_work"}.to_json
1052
+ hs = JsonPath.new "$..send"
1053
+ assert_equal(['should_still_work'], hs.on(j))
1054
+ end
1055
+
1056
+ def test_index_access_by_number
1057
+ data = {
1058
+ '1': 'foo'
1059
+ }
1060
+ assert_equal ['foo'], JsonPath.new('$.1').on(data.to_json)
1061
+ end
1062
+
1063
+ def test_behavior_on_null_and_missing
1064
+ data = {
1065
+ "foo" => nil,
1066
+ "bar" => {
1067
+ "baz" => nil
1068
+ },
1069
+ "bars" => [
1070
+ { "foo" => 12 },
1071
+ { "foo" => nil },
1072
+ { }
1073
+ ]
1074
+ }
1075
+ assert_equal [nil], JsonPath.new('$.foo').on(data)
1076
+ assert_equal [nil], JsonPath.new('$.bar.baz').on(data)
1077
+ assert_equal [], JsonPath.new('$.baz').on(data)
1078
+ assert_equal [], JsonPath.new('$.bar.foo').on(data)
1079
+ assert_equal [12, nil], JsonPath.new('$.bars[*].foo').on(data)
1080
+ end
1081
+
1082
+ def test_default_path_leaf_to_null_opt
1083
+ data = {
1084
+ "foo" => nil,
1085
+ "bar" => {
1086
+ "baz" => nil
1087
+ },
1088
+ "bars" => [
1089
+ { "foo" => 12 },
1090
+ { "foo" => nil },
1091
+ { }
1092
+ ]
1093
+ }
1094
+ assert_equal [nil], JsonPath.new('$.foo', default_path_leaf_to_null: true).on(data)
1095
+ assert_equal [nil], JsonPath.new('$.bar.baz', default_path_leaf_to_null: true).on(data)
1096
+ assert_equal [nil], JsonPath.new('$.baz', default_path_leaf_to_null: true).on(data)
1097
+ assert_equal [nil], JsonPath.new('$.bar.foo', default_path_leaf_to_null: true).on(data)
1098
+ assert_equal [12, nil, nil], JsonPath.new('$.bars[*].foo', default_path_leaf_to_null: true).on(data)
1099
+ end
1100
+
1101
+ def example_object
1102
+ { 'store' => {
1103
+ 'book' => [
1104
+ { 'category' => 'reference',
1105
+ 'author' => 'Nigel Rees',
1106
+ 'title' => 'Sayings of the Century',
1107
+ 'price' => 9,
1108
+ 'tags' => %w[asdf asdf2] },
1109
+ { 'category' => 'fiction',
1110
+ 'author' => 'Evelyn Waugh',
1111
+ 'title' => 'Sword of Honour',
1112
+ 'price' => 13 },
1113
+ { 'category' => 'fiction',
1114
+ 'author' => 'Herman Melville',
1115
+ 'title' => 'Moby Dick',
1116
+ 'isbn' => '0-553-21311-3',
1117
+ 'price' => 9 },
1118
+ { 'category' => 'fiction',
1119
+ 'author' => 'J. R. R. Tolkien',
1120
+ 'title' => 'The Lord of the Rings',
1121
+ 'isbn' => '0-395-19395-8',
1122
+ 'price' => 23 },
1123
+ { 'category' => 'russian_fiction',
1124
+ 'author' => 'Lukyanenko',
1125
+ 'title' => 'Imperatory Illuziy',
1126
+ 'written' => {
1127
+ 'year' => 1995
1128
+ } },
1129
+ { 'category' => 'russian_fiction',
1130
+ 'author' => 'Lukyanenko',
1131
+ 'title' => 'Osennie Vizity',
1132
+ 'written' => {
1133
+ 'year' => 1996
1134
+ } },
1135
+ { 'category' => 'russian_fiction',
1136
+ 'author' => 'Lukyanenko',
1137
+ 'title' => 'Ne vremya dlya drakonov',
1138
+ 'written' => {
1139
+ 'year' => 1997
1140
+ } }
221
1141
  ],
222
- "bicycle"=> {
223
- "color"=> "red",
224
- "price"=> 20,
225
- "catalogue_number" => 12345,
226
- "single-speed" => "no",
227
- "2seater" => "yes"}
1142
+ 'bicycle' => {
1143
+ 'color' => 'red',
1144
+ 'price' => 20,
1145
+ 'catalogue_number' => 123_45,
1146
+ 'single-speed' => 'no',
1147
+ '2seater' => 'yes',
1148
+ 'make:model' => 'Zippy Sweetwheeler'
1149
+ },
1150
+ '@id' => 'http://example.org/store/42',
1151
+ '$meta-data' => 'whatevs',
1152
+ '_links' => { 'self' => {} }
228
1153
  } }
229
1154
  end
230
-
231
1155
  end