jsonpath 1.0.0 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 86c7a2524bbfbf973b55437a09068d62ae594a05
4
- data.tar.gz: 8653a33163faa160f9eef04155e3906ff6419343
2
+ SHA256:
3
+ metadata.gz: d45aa52a5d4a9500f243abaa6ff69131a7e5a8653ffacd1ff0787946774d12f3
4
+ data.tar.gz: bd0903ab08210ac2e5a44a56f548104c1924f2d9a9db9d5006a51db86aafc648
5
5
  SHA512:
6
- metadata.gz: ca8350260d52a9c24fc1782393975e74a14639c6152a6d4e450efde8e980882aefd0009ed1c6063cd24be2d36e5f64c16e81e361a42c075b35494c613b1c57d2
7
- data.tar.gz: aa60fa92da9e3ba857f3914ebb902a473bada3f53a4486997d04a7d37530c942fbe93b13e3ac4cd4e39253ae2b57cdc8191afd4875ab1d9a34e678afb41ae2dc
6
+ metadata.gz: 96a161911383f2c7870bd5450020aaaaceb1ea4305672f5a08493016e4fdcc5fb346001b3e8a377c16588c7bcd5adf9b0be1ef51d18b483404619324600501bb
7
+ data.tar.gz: 2c824f8916a742f0e5a9201c858c683bf2df7e9c7d5e38f7b7206fc8f511f69741afb3378993f3ed7e8f396148a14596ce7b9e78f0a739d2aeafc15591a0b585
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2018-07-07 21:29:45 +0200 using RuboCop version 0.57.2.
3
+ # on 2019-01-25 09:23:04 +0100 using RuboCop version 0.63.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -18,31 +18,32 @@ Lint/IneffectiveAccessModifier:
18
18
  Exclude:
19
19
  - 'lib/jsonpath.rb'
20
20
 
21
- # Offense count: 16
21
+ # Offense count: 17
22
22
  Metrics/AbcSize:
23
- Max: 54
23
+ Max: 60
24
24
 
25
25
  # Offense count: 2
26
26
  # Configuration parameters: CountComments, ExcludedMethods.
27
+ # ExcludedMethods: refine
27
28
  Metrics/BlockLength:
28
- Max: 33
29
+ Max: 37
29
30
 
30
31
  # Offense count: 1
31
32
  # Configuration parameters: CountBlocks.
32
33
  Metrics/BlockNesting:
33
34
  Max: 4
34
35
 
35
- # Offense count: 2
36
+ # Offense count: 3
36
37
  # Configuration parameters: CountComments.
37
38
  Metrics/ClassLength:
38
- Max: 578
39
+ Max: 739
39
40
 
40
- # Offense count: 6
41
+ # Offense count: 7
41
42
  Metrics/CyclomaticComplexity:
42
- Max: 18
43
+ Max: 20
43
44
 
44
- # Offense count: 24
45
- # Configuration parameters: CountComments.
45
+ # Offense count: 26
46
+ # Configuration parameters: CountComments, ExcludedMethods.
46
47
  Metrics/MethodLength:
47
48
  Max: 52
48
49
 
@@ -53,9 +54,16 @@ Metrics/ParameterLists:
53
54
 
54
55
  # Offense count: 6
55
56
  Metrics/PerceivedComplexity:
56
- Max: 20
57
+ Max: 21
57
58
 
58
59
  # Offense count: 1
60
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
61
+ # AllowedNames: io, id, to, by, on, in, at, ip, db
62
+ Naming/UncommunicativeMethodParamName:
63
+ Exclude:
64
+ - 'lib/jsonpath/parser.rb'
65
+
66
+ # Offense count: 15
59
67
  # Configuration parameters: AllowedChars.
60
68
  Style/AsciiComments:
61
69
  Exclude:
@@ -69,11 +77,12 @@ Style/Documentation:
69
77
  - 'lib/jsonpath/enumerable.rb'
70
78
  - 'lib/jsonpath/proxy.rb'
71
79
 
72
- # Offense count: 1
80
+ # Offense count: 3
73
81
  # Configuration parameters: MinBodyLength.
74
82
  Style/GuardClause:
75
83
  Exclude:
76
84
  - 'lib/jsonpath/enumerable.rb'
85
+ - 'lib/jsonpath/parser.rb'
77
86
 
78
87
  # Offense count: 2
79
88
  # Cop supports --auto-correct.
@@ -81,16 +90,22 @@ Style/IfUnlessModifier:
81
90
  Exclude:
82
91
  - 'lib/jsonpath/enumerable.rb'
83
92
 
84
- # Offense count: 2
93
+ # Offense count: 1
94
+ Style/MultipleComparison:
95
+ Exclude:
96
+ - 'lib/jsonpath/parser.rb'
97
+
98
+ # Offense count: 3
85
99
  # Cop supports --auto-correct.
86
- # Configuration parameters: AutoCorrect, EnforcedStyle.
100
+ # Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
87
101
  # SupportedStyles: predicate, comparison
88
102
  Style/NumericPredicate:
89
103
  Exclude:
90
104
  - 'spec/**/*'
91
105
  - 'lib/jsonpath/enumerable.rb'
106
+ - 'lib/jsonpath/parser.rb'
92
107
 
93
- # Offense count: 2
108
+ # Offense count: 3
94
109
  # Cop supports --auto-correct.
95
110
  # Configuration parameters: EnforcedStyle, AllowInnerSlashes.
96
111
  # SupportedStyles: slashes, percent_r, mixed
@@ -98,15 +113,15 @@ Style/RegexpLiteral:
98
113
  Exclude:
99
114
  - 'lib/jsonpath/parser.rb'
100
115
 
101
- # Offense count: 3
116
+ # Offense count: 4
102
117
  # Cop supports --auto-correct.
103
118
  Style/RescueModifier:
104
119
  Exclude:
105
120
  - 'lib/jsonpath/enumerable.rb'
106
121
  - 'lib/jsonpath/parser.rb'
107
122
 
108
- # Offense count: 71
123
+ # Offense count: 89
109
124
  # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
110
125
  # URISchemes: http, https
111
126
  Metrics/LineLength:
112
- Max: 175
127
+ Max: 296
@@ -33,10 +33,12 @@ class JsonPath
33
33
  nil
34
34
  elsif token = scanner.scan(/[><=] \d+/)
35
35
  @path.last << token
36
- # TODO: If there are characters that it can't match in the previous legs, this will throw
37
- # a RuntimeError: can't modify frozen String error.
38
36
  elsif token = scanner.scan(/./)
39
- @path.last << token
37
+ begin
38
+ @path.last << token
39
+ rescue RuntimeError
40
+ raise ArgumentError, "character '#{token}' not supported in query"
41
+ end
40
42
  end
41
43
  end
42
44
  end
@@ -69,7 +71,7 @@ class JsonPath
69
71
  a = enum_on(obj_or_str).to_a
70
72
  if opts[:symbolize_keys]
71
73
  a.map! do |e|
72
- e.inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo }
74
+ e.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v; }
73
75
  end
74
76
  end
75
77
  a
@@ -15,6 +15,7 @@ class JsonPath
15
15
  node = key ? context[key] : context
16
16
  @_current_node = node
17
17
  return yield_value(blk, context, key) if pos == @path.size
18
+
18
19
  case expr = @path[pos]
19
20
  when '*', '..', '@'
20
21
  each(context, key, pos + 1, &blk)
@@ -47,6 +48,7 @@ class JsonPath
47
48
  handle_question_mark(sub_path, node, pos, &blk)
48
49
  else
49
50
  next if node.is_a?(Array) && node.empty?
51
+
50
52
  array_args = sub_path.split(':')
51
53
  if array_args[0] == '*'
52
54
  start_idx = 0
@@ -58,6 +60,7 @@ class JsonPath
58
60
  else
59
61
  start_idx = process_function_or_literal(array_args[0], 0)
60
62
  next unless start_idx
63
+
61
64
  end_idx = array_args[1] && ensure_exclusive_end_index(process_function_or_literal(array_args[1], -1)) || -1
62
65
  next unless end_idx
63
66
  next if start_idx == end_idx && start_idx >= node.size
@@ -66,6 +69,7 @@ class JsonPath
66
69
  end_idx %= node.size
67
70
  step = process_function_or_literal(array_args[2], 1)
68
71
  next unless step
72
+
69
73
  if @mode == :delete
70
74
  (start_idx..end_idx).step(step) { |i| node[i] = nil }
71
75
  node.compact!
@@ -14,39 +14,39 @@ class JsonPath
14
14
  # parse will parse an expression in the following way.
15
15
  # Split the expression up into an array of legs for && and || operators.
16
16
  # Parse this array into a map for which the keys are the parsed legs
17
- # of the split. This map is then used to replace the expression with their
17
+ #  of the split. This map is then used to replace the expression with their
18
18
  # corresponding boolean or numeric value. This might look something like this:
19
19
  # ((false || false) && (false || true))
20
- # Once this string is assembled... we proceed to evaluate from left to right.
21
- # The above string is broken down like this:
20
+ #  Once this string is assembled... we proceed to evaluate from left to right.
21
+ #  The above string is broken down like this:
22
22
  # (false && (false || true))
23
23
  # (false && true)
24
- # false
24
+ #  false
25
25
  def parse(exp)
26
26
  exps = exp.split(/(&&)|(\|\|)/)
27
27
  construct_expression_map(exps)
28
- @_expr_map.each {|k, v| exp.sub!(k, "#{v}")}
28
+ @_expr_map.each { |k, v| exp.sub!(k, v.to_s) }
29
29
  raise ArgumentError, "unmatched parenthesis in expression: #{exp}" unless check_parenthesis_count(exp)
30
- while (exp.include?("("))
31
- exp = parse_parentheses(exp)
32
- end
30
+
31
+ exp = parse_parentheses(exp) while exp.include?('(')
33
32
  bool_or_exp(exp)
34
33
  end
35
34
 
36
35
  # Construct a map for which the keys are the expressions
37
- # and the values are the corresponding parsed results.
36
+ #  and the values are the corresponding parsed results.
38
37
  # Exp.:
39
38
  # {"(@['author'] =~ /herman|lukyanenko/i)"=>0}
40
39
  # {"@['isTrue']"=>true}
41
40
  def construct_expression_map(exps)
42
- exps.each_with_index do |item, index|
41
+ exps.each_with_index do |item, _index|
43
42
  next if item == '&&' || item == '||'
43
+
44
44
  item = item.strip.gsub(/\)*$/, '').gsub(/^\(*/, '')
45
45
  @_expr_map[item] = parse_exp(item)
46
46
  end
47
47
  end
48
48
 
49
- # using a scanner break down the individual expressions and determine if
49
+ #  using a scanner break down the individual expressions and determine if
50
50
  # there is a match in the JSON for it or not.
51
51
  def parse_exp(exp)
52
52
  exp = exp.sub(/@/, '').gsub(/^\(/, '').gsub(/\)$/, '').tr('"', '\'').strip
@@ -100,34 +100,33 @@ class JsonPath
100
100
  return nil unless hash.is_a? Hash
101
101
  return nil unless hash.key?(keys.first)
102
102
  return hash.fetch(keys.first) if keys.size == 1
103
+
103
104
  prev = keys.shift
104
105
  dig(keys, hash.fetch(prev))
105
106
  end
106
107
 
107
- # This will break down a parenthesis from the left to the right
108
- # and replace the given expression with it's returned value.
108
+ #  This will break down a parenthesis from the left to the right
109
+ #  and replace the given expression with it's returned value.
109
110
  # It does this in order to make it easy to eliminate groups
110
111
  # one-by-one.
111
112
  def parse_parentheses(str)
112
113
  opening_index = 0
113
114
  closing_index = 0
114
115
 
115
- (0..str.length-1).step(1) do |i|
116
- if str[i] == '('
117
- opening_index = i
118
- end
116
+ (0..str.length - 1).step(1) do |i|
117
+ opening_index = i if str[i] == '('
119
118
  if str[i] == ')'
120
119
  closing_index = i
121
120
  break
122
121
  end
123
122
  end
124
123
 
125
- to_parse = str[opening_index+1..closing_index-1]
124
+ to_parse = str[opening_index + 1..closing_index - 1]
126
125
 
127
- # handle cases like (true && true || false && true) in
126
+ #  handle cases like (true && true || false && true) in
128
127
  # one giant parenthesis.
129
128
  top = to_parse.split(/(&&)|(\|\|)/)
130
- top = top.map{|t| t.strip}
129
+ top = top.map(&:strip)
131
130
  res = bool_or_exp(top.shift)
132
131
  top.each_with_index do |item, index|
133
132
  case item
@@ -138,42 +137,44 @@ class JsonPath
138
137
  end
139
138
  end
140
139
 
141
- # if we are at the last item, the opening index will be 0
140
+ #  if we are at the last item, the opening index will be 0
142
141
  # and the closing index will be the last index. To avoid
143
142
  # off-by-one errors we simply return the result at that point.
144
- if closing_index+1 >= str.length && opening_index == 0
145
- return "#{res}"
143
+ if closing_index + 1 >= str.length && opening_index == 0
144
+ return res.to_s
146
145
  else
147
- return "#{str[0..opening_index-1]}#{res}#{str[closing_index+1..str.length]}"
146
+ return "#{str[0..opening_index - 1]}#{res}#{str[closing_index + 1..str.length]}"
148
147
  end
149
148
  end
150
149
 
151
- # This is convoluted and I should probably refactor it somehow.
152
- # The map that is created will contain strings since essentially I'm
150
+ #  This is convoluted and I should probably refactor it somehow.
151
+ #  The map that is created will contain strings since essentially I'm
153
152
  # constructing a string like `true || true && false`.
154
153
  # With eval the need for this would disappear but never the less, here
155
- # it is. The fact is that the results can be either boolean, or a number
154
+ #  it is. The fact is that the results can be either boolean, or a number
156
155
  # in case there is only indexing happening like give me the 3rd item... or
157
156
  # it also can be nil in case of regexes or things that aren't found.
158
157
  # Hence, I have to be clever here to see what kind of variable I need to
159
158
  # provide back.
160
159
  def bool_or_exp(b)
161
- if "#{b}" == 'true'
160
+ if b.to_s == 'true'
162
161
  return true
163
- elsif "#{b}" == 'false'
162
+ elsif b.to_s == 'false'
164
163
  return false
165
- elsif "#{b}" == ""
164
+ elsif b.to_s == ''
166
165
  return nil
167
166
  end
167
+
168
168
  b = Float(b) rescue b
169
169
  b
170
170
  end
171
171
 
172
172
  # this simply makes sure that we aren't getting into the whole
173
- # parenthesis parsing business without knowing that every parenthesis
173
+ #  parenthesis parsing business without knowing that every parenthesis
174
174
  # has its pair.
175
175
  def check_parenthesis_count(exp)
176
- return true unless exp.include?("(")
176
+ return true unless exp.include?('(')
177
+
177
178
  depth = 0
178
179
  exp.chars.each do |c|
179
180
  if c == '('
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class JsonPath
4
- VERSION = '1.0.0'.freeze
4
+ VERSION = '1.0.1'.freeze
5
5
  end
@@ -228,18 +228,15 @@ class TestJsonpath < MiniTest::Unit::TestCase
228
228
  'delete_me' => [
229
229
  'no' => 'do not'
230
230
  ]
231
- }
232
- },
231
+ } },
233
232
  { 'category' => 'fiction',
234
233
  'author' => 'Evelyn Waugh',
235
234
  'title' => 'Sword of Honour',
236
- 'price' => 13
237
- },
235
+ 'price' => 13 },
238
236
  { 'category' => 'fiction',
239
237
  'author' => 'Aasdf',
240
238
  'title' => 'Aaasdf2',
241
- 'price' => 1
242
- }
239
+ 'price' => 1 }
243
240
  ]
244
241
  } }
245
242
  json_deleted = { 'store' => {
@@ -249,18 +246,15 @@ class TestJsonpath < MiniTest::Unit::TestCase
249
246
  'title' => 'Sayings of the Century',
250
247
  'price' => 9,
251
248
  'tags' => %w[asdf asdf2],
252
- 'this' => {}
253
- },
249
+ 'this' => {} },
254
250
  { 'category' => 'fiction',
255
251
  'author' => 'Evelyn Waugh',
256
252
  'title' => 'Sword of Honour',
257
- 'price' => 13
258
- },
253
+ 'price' => 13 },
259
254
  { 'category' => 'fiction',
260
255
  'author' => 'Aasdf',
261
256
  'title' => 'Aaasdf2',
262
- 'price' => 1
263
- }
257
+ 'price' => 1 }
264
258
  ]
265
259
  } }
266
260
  assert_equal(json_deleted, JsonPath.for(json).delete('$..store.book..delete_me').obj)
@@ -605,19 +599,19 @@ class TestJsonpath < MiniTest::Unit::TestCase
605
599
  json = {
606
600
  channels: [
607
601
  {
608
- name: "King's Speech",
602
+ name: "King's Speech"
609
603
  }
610
604
  ]
611
605
  }.to_json
612
606
 
613
- assert_equal [{"name" => "King\'s Speech"}], JsonPath.on(json, "$..channels[?(@.name == 'King\'s Speech')]")
607
+ assert_equal [{ 'name' => "King\'s Speech" }], JsonPath.on(json, "$..channels[?(@.name == 'King\'s Speech')]")
614
608
  end
615
609
 
616
610
  def test_curly_brackets
617
611
  data = {
618
612
  '{data}' => 'data'
619
613
  }
620
- assert_equal ['data'], JsonPath.new("$.{data}").on(data)
614
+ assert_equal ['data'], JsonPath.new('$.{data}').on(data)
621
615
  end
622
616
 
623
617
  def test_symbolize
@@ -660,7 +654,7 @@ class TestJsonpath < MiniTest::Unit::TestCase
660
654
  }
661
655
  }
662
656
  '
663
- 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})
657
+ 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)
664
658
  end
665
659
 
666
660
  def test_changed
@@ -724,10 +718,22 @@ class TestJsonpath < MiniTest::Unit::TestCase
724
718
 
725
719
  def test_complex_nested_grouping_unmatched_parent
726
720
  path = "$..book[?((@['author'] == 'Evelyn Waugh' || @['author'] == 'Herman Melville' && (@['price'] == 33 || @['price'] == 9))]"
727
- err = assert_raises(ArgumentError, "should have raised an exception") { JsonPath.new(path).on(@object)}
721
+ err = assert_raises(ArgumentError, 'should have raised an exception') { JsonPath.new(path).on(@object) }
728
722
  assert_match(/unmatched parenthesis in expression: \(\(false \|\| false && \(false \|\| true\)\)/, err.message)
729
723
  end
730
724
 
725
+ def test_runtime_error_frozen_string
726
+ 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')
727
+ json = '
728
+ {
729
+ "test": "something"
730
+ }
731
+ '.to_json
732
+ assert_raises(ArgumentError, "RuntimeError: character '|' not supported in query") do
733
+ JsonPath.on(json, '$.description|title')
734
+ end
735
+ end
736
+
731
737
  def test_delete_more_items
732
738
  a = { 'itemList' =>
733
739
  [{ 'alfa' => 'beta1' },
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonpath
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Hull
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-01-20 00:00:00.000000000 Z
12
+ date: 2019-01-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  version: '0'
160
160
  requirements: []
161
161
  rubyforge_project: jsonpath
162
- rubygems_version: 2.6.13
162
+ rubygems_version: 2.7.8
163
163
  signing_key:
164
164
  specification_version: 4
165
165
  summary: Ruby implementation of http://goessner.net/articles/JsonPath/