jsonpath 0.7.2 → 0.8.2

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: 86c55c63011d6bd71967469b464bfeb537f7c54f
4
- data.tar.gz: 57d966160fa4106318d122cc2e088a9032925d47
2
+ SHA256:
3
+ metadata.gz: 250b6d90a78bf853330c06f41cacee3207c39e1cb5a13a0ea747f6511660fefb
4
+ data.tar.gz: 22a728dbfe6c1ca44884db61e4e2dc2e89c355b7fa4c308fb0bcfc9a2bb3e815
5
5
  SHA512:
6
- metadata.gz: 4e96817450882137b8907cd90bec5564a3ecd21da7803ca11034ae4a8c4b2a65bdc661efe0b210858af3b4a71e029d8689cbf30f5cefe2beb8047a9b2e07ffc4
7
- data.tar.gz: 050745e85fe208a99a963c54eac2426cdfaf701a9f6658e6cd2f5683bfa0730af1cf690a92e0d3fc8bb006afa8f8a2221c99d92178944689bb7f1b0efc35e095
6
+ metadata.gz: 44abf571b8d09b3dd4c5ec51a801ca064cfc08569a4121d58d456bd8403c3396b33f8f369992b258016374c8d823825f1619430c7434ebba2eec1bc19dd10b8c
7
+ data.tar.gz: 11ec6a115406f90775f3eb73b959befc1249e47d998b6679f5fcca671b04a694872282aa6ac6b79faad8d6fd1767c84b65abea536e0773518d76329ebb866ceb
data/README.md CHANGED
@@ -103,8 +103,6 @@ enum.any?{ |c| c == 'red' }
103
103
  # => true
104
104
  ```
105
105
 
106
- You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor.
107
-
108
106
  ### More examples
109
107
 
110
108
  For more usage examples and variations on paths, please visit the tests. There are some more complex ones as well.
data/lib/jsonpath.rb CHANGED
@@ -3,6 +3,7 @@ require 'multi_json'
3
3
  require 'jsonpath/proxy'
4
4
  require 'jsonpath/enumerable'
5
5
  require 'jsonpath/version'
6
+ require 'jsonpath/parser'
6
7
 
7
8
  # JsonPath: initializes the class with a given JsonPath and parses that path
8
9
  # into a token array.
@@ -1,19 +1,12 @@
1
1
  class JsonPath
2
2
  class Enumerable
3
3
  include ::Enumerable
4
- attr_reader :allow_eval
5
- alias_method :allow_eval?, :allow_eval
6
4
 
7
5
  def initialize(path, object, mode, options = nil)
8
6
  @path = path.path
9
7
  @object = object
10
8
  @mode = mode
11
9
  @options = options
12
- @allow_eval = if @options && @options.key?(:allow_eval)
13
- @options[:allow_eval]
14
- else
15
- true
16
- end
17
10
  end
18
11
 
19
12
  def each(context = @object, key = nil, pos = 0, &blk)
@@ -27,10 +20,6 @@ class JsonPath
27
20
  each(context, key, pos + 1, &blk) if node == @object
28
21
  when /^\[(.*)\]$/
29
22
  handle_wildecard(node, expr, context, key, pos, &blk)
30
- else
31
- if pos == (@path.size - 1) && node && allow_eval?
32
- yield_value(blk, context, key) if instance_eval("node #{@path[pos]}")
33
- end
34
23
  end
35
24
 
36
25
  if pos > 0 && @path[pos - 1] == '..' || (@path[pos - 1] == '*' && @path[pos] != '..')
@@ -75,11 +64,12 @@ class JsonPath
75
64
  end
76
65
 
77
66
  def handle_question_mark(sub_path, node, pos, &blk)
78
- raise 'Cannot use ?(...) unless eval is enabled' unless allow_eval?
79
67
  case node
80
68
  when Array
81
69
  node.size.times do |index|
82
70
  @_current_node = node[index]
71
+ # exps = sub_path[1, sub_path.size - 1]
72
+ # if @_current_node.send("[#{exps.gsub(/@/, '@_current_node')}]")
83
73
  if process_function_or_literal(sub_path[1, sub_path.size - 1])
84
74
  each(@_current_node, nil, pos + 1, &blk)
85
75
  end
@@ -113,40 +103,22 @@ class JsonPath
113
103
  def process_function_or_literal(exp, default = nil)
114
104
  return default if exp.nil? || exp.empty?
115
105
  return Integer(exp) if exp[0] != '('
116
- return nil unless allow_eval? && @_current_node
106
+ return nil unless @_current_node
117
107
 
118
108
  identifiers = /@?((?<!\d)\.(?!\d)(\w+))+/.match(exp)
119
- # puts JsonPath.on(@_current_node, "#{identifiers}") unless identifiers.nil? ||
120
- # @_current_node
121
- # .methods
122
- # .include?(identifiers[2].to_sym)
123
-
124
109
  unless identifiers.nil? ||
125
110
  @_current_node.methods.include?(identifiers[2].to_sym)
126
111
  exp_to_eval = exp.dup
127
112
  exp_to_eval[identifiers[0]] = identifiers[0].split('.').map do |el|
128
- el == '@' ? '@_current_node' : "['#{el}']"
113
+ el == '@' ? '@' : "['#{el}']"
129
114
  end.join
130
-
131
115
  begin
132
- return instance_eval(exp_to_eval)
133
- # if eval failed because of bad arguments or missing methods
116
+ return JsonPath::Parser.new(@_current_node).parse(exp_to_eval)
134
117
  rescue StandardError
135
118
  return default
136
119
  end
137
120
  end
138
-
139
- # otherwise eval as is
140
- # TODO: this eval is wrong, because hash accessor could be nil and nil
141
- # cannot be compared with anything, for instance,
142
- # @a_current_node['price'] - we can't be sure that 'price' are in every
143
- # node, but it's only in several nodes I wrapped this eval into rescue
144
- # returning false when error, but this eval should be refactored.
145
- begin
146
- instance_eval(exp.gsub(/@/, '@_current_node'))
147
- rescue
148
- false
149
- end
121
+ JsonPath::Parser.new(@_current_node).parse(exp)
150
122
  end
151
123
  end
152
124
  end
@@ -0,0 +1,62 @@
1
+ require 'strscan'
2
+
3
+ class JsonPath
4
+ # Parser parses and evaluates an expression passed to @_current_node.
5
+ class Parser
6
+ def initialize(node)
7
+ @_current_node = node
8
+ end
9
+
10
+ def parse(exp)
11
+ exps = exp.split(/(&&)|(\|\|)/)
12
+ ret = parse_exp(exps.shift)
13
+ exps.each_with_index do |item, index|
14
+ case item
15
+ when '&&'
16
+ ret &&= parse_exp(exps[index + 1])
17
+ when '||'
18
+ ret ||= parse_exp(exps[index + 1])
19
+ end
20
+ end
21
+ ret
22
+ end
23
+
24
+ def parse_exp(exp)
25
+ exp = exp.gsub(/@/, '').gsub(/[\(\)]/, '')
26
+ scanner = StringScanner.new(exp)
27
+ elements = []
28
+ until scanner.eos?
29
+ if scanner.scan(/\./)
30
+ sym = scanner.scan(/\w+/)
31
+ op = scanner.scan(/./)
32
+ num = scanner.scan(/\d+/)
33
+ return @_current_node.send(sym.to_sym).send(op.to_sym, num.to_i)
34
+ end
35
+ if t = scanner.scan(/\['\w+'\]+/)
36
+ elements << t.gsub(/\[|\]|'|\s+/, '')
37
+ elsif t = scanner.scan(/\s+[<>=][<>=]?\s+?/)
38
+ operator = t
39
+ elsif t = scanner.scan(/(\s+)?'?(\w+)?[.,]?(\w+)?'?(\s+)?/) # @TODO: At this point I should trim somewhere...
40
+ operand = t.delete("'").strip
41
+ elsif t = scanner.scan(/.*/)
42
+ raise "Could not process symbol: #{t}"
43
+ end
44
+ end
45
+ el = dig(elements, @_current_node)
46
+ return false unless el
47
+ return true if operator.nil? && el
48
+ operand = operand.to_f if operand.to_i.to_s == operand || operand.to_f.to_s == operand
49
+ el.send(operator.strip, operand)
50
+ end
51
+
52
+ private
53
+
54
+ # @TODO: Remove this once JsonPath no longer supports ruby versions below 2.3
55
+ def dig(keys, hash)
56
+ return nil unless hash.key?(keys.first)
57
+ return hash.fetch(keys.first) if keys.size == 1
58
+ prev = keys.shift
59
+ dig(keys, hash.fetch(prev))
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,3 @@
1
1
  class JsonPath
2
- VERSION = '0.7.2'.freeze
2
+ VERSION = '0.8.2'.freeze
3
3
  end
@@ -80,8 +80,8 @@ class TestJsonpath < MiniTest::Unit::TestCase
80
80
  assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] < 23.0 && @['price'] > 9.0)]").on(@object)
81
81
  end
82
82
 
83
- def test_no_eval
84
- assert_equal [], JsonPath.new('$..book[(@.length-2)]', allow_eval: false).on(@object)
83
+ def test_eval_with_floating_point
84
+ assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] == 13.0)]").on(@object)
85
85
  end
86
86
 
87
87
  def test_paths_with_underscores
@@ -109,7 +109,7 @@ class TestJsonpath < MiniTest::Unit::TestCase
109
109
  end
110
110
 
111
111
  def test_counting
112
- assert_equal 54, JsonPath.new('$..*').on(@object).to_a.size
112
+ assert_equal 57, JsonPath.new('$..*').on(@object).to_a.size
113
113
  end
114
114
 
115
115
  def test_space_in_path
@@ -255,6 +255,10 @@ class TestJsonpath < MiniTest::Unit::TestCase
255
255
  JsonPath.new("$.store._links").on(@object)
256
256
  end
257
257
 
258
+ # def test_filter_support_include
259
+ # #assert_equal true, JsonPath.new("$.store.book[(@.tags == 'asdf3')]").on(@object)
260
+ # assert_equal true, JsonPath.new("$.store.book..tags[?(@ == 'asdf')]").on(@object)
261
+ # end
258
262
 
259
263
  def example_object
260
264
  { 'store' => {
@@ -262,7 +266,8 @@ class TestJsonpath < MiniTest::Unit::TestCase
262
266
  { 'category' => 'reference',
263
267
  'author' => 'Nigel Rees',
264
268
  'title' => 'Sayings of the Century',
265
- 'price' => 9 },
269
+ 'price' => 9,
270
+ 'tags' => ['asdf', 'asdf2']},
266
271
  { 'category' => 'fiction',
267
272
  'author' => 'Evelyn Waugh',
268
273
  'title' => 'Sword of Honour',
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: 0.7.2
4
+ version: 0.8.2
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: 2017-05-10 00:00:00.000000000 Z
12
+ date: 2017-05-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -117,6 +117,7 @@ files:
117
117
  - jsonpath.gemspec
118
118
  - lib/jsonpath.rb
119
119
  - lib/jsonpath/enumerable.rb
120
+ - lib/jsonpath/parser.rb
120
121
  - lib/jsonpath/proxy.rb
121
122
  - lib/jsonpath/version.rb
122
123
  - test/test_jsonpath.rb