jsonpath 0.7.2 → 0.8.2

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: 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