jsonpath 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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/