jsonpath 0.5.8 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0605409a770fbb28dd1352fa92753e7707d0df96
4
- data.tar.gz: ed5e1e0c2fc22721d9d9a71ca5ec97f15e07bfe5
3
+ metadata.gz: 0c8e5023499ffb2c83cb4eeed6d400f55f28bf8f
4
+ data.tar.gz: 81cb76411fd07a5d0ce6ef227d72682de7c9c4b6
5
5
  SHA512:
6
- metadata.gz: d3471d1c6ffdd79d987edf7772c945b2c5c74cd614086cc49e57588d47c6135310fd9ca6660c0e5e711521c9863b189de1067c8656165d46bf266eb2522a9491
7
- data.tar.gz: 091adbef8dc29e30172d0016cf67e6035af094426fd405a45fff52e0292b45340f1aab1804b5291f918b0916fe2f02b252cb8fa2d97e2ee13dcd330645ac9cf8
6
+ metadata.gz: e3475b1e4bdf85fcc9da42a353f81db379a41cc8baf01680096083ffda68a40deee85872632f1470ff94285295830e7933d8656cf59e8a4ff48cc860d89049e1
7
+ data.tar.gz: 7a89006179792222b0222a599fae3ceb4548d49b73f9cd89867716f29b567547f36edd5251bd1bc9d7ddb58f0d4150f87f9860021b587b04c0cff0183b45ddb5
@@ -1,5 +1,4 @@
1
1
  rvm:
2
- - 1.9.2
3
- - 1.9.3
4
- - 2.0.0
2
+ - 2.1.6
3
+ - 2.3.1
5
4
  - jruby
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Joshua Lin & Gergely Brautigam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -81,7 +81,7 @@ JsonPath.new('$..book[::2]').on(json)
81
81
  ...and evals.
82
82
 
83
83
  ~~~~~ {ruby}
84
- JsonPath.new('$..price[?(@ < 20)]').on(json)
84
+ JsonPath.new('$..price[?(@ < 10)]').on(json)
85
85
  # => [8.95, 8.99]
86
86
  ~~~~~
87
87
 
@@ -105,6 +105,10 @@ enum.any?{ |c| c == 'red' }
105
105
 
106
106
  You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor.
107
107
 
108
+ ### More examples
109
+
110
+ For more usage examples and variations on paths, please visit the tests. There are some more complex ones as well.
111
+
108
112
  ### Manipulation
109
113
 
110
114
  If you'd like to do substitution in a json object, you can use `#gsub` or `#gsub!` to modify the object in place.
@@ -130,3 +134,7 @@ o = JsonPath.for(json).
130
134
  to_hash
131
135
  # => {"candy" => "big turks"}
132
136
  ~~~~~
137
+
138
+ # Contributions
139
+
140
+ Please feel free to submit an Issue or a Pull Request any time you feel like you would like to contribute. Thank you!
data/Rakefile CHANGED
@@ -2,11 +2,8 @@ require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
4
  task :test do
5
- $: << 'lib'
6
- require 'minitest/autorun'
7
- require 'phocus'
8
- require 'jsonpath'
5
+ $LOAD_PATH << 'lib'
9
6
  Dir['./test/**/test_*.rb'].each { |test| require test }
10
7
  end
11
8
 
12
- task :default => :test
9
+ task default: :test
@@ -15,7 +15,7 @@ usage unless ARGV[0]
15
15
 
16
16
  jsonpath = JsonPath.new(ARGV[0])
17
17
  case ARGV[1]
18
- when nil #stdin
18
+ when nil # stdin
19
19
  puts MultiJson.encode(jsonpath.on(MultiJson.decode(STDIN.read)))
20
20
  when String
21
21
  puts MultiJson.encode(jsonpath.on(MultiJson.decode(File.exist?(ARGV[1]) ? File.read(ARGV[1]) : ARGV[1])))
@@ -5,20 +5,21 @@ require File.join(File.dirname(__FILE__), 'lib', 'jsonpath', 'version')
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'jsonpath'
7
7
  s.version = JsonPath::VERSION
8
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
- s.authors = ["Joshua Hull"]
10
- s.summary = "Ruby implementation of http://goessner.net/articles/JsonPath/"
11
- s.description = "Ruby implementation of http://goessner.net/articles/JsonPath/."
12
- s.email = %q{joshbuddy@gmail.com}
8
+ s.required_rubygems_version =
9
+ Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
10
+ s.authors = ['Joshua Hull', 'Gergely Brautigam']
11
+ s.summary = 'Ruby implementation of http://goessner.net/articles/JsonPath/'
12
+ s.description = 'Ruby implementation of http://goessner.net/articles/JsonPath/.'
13
+ s.email = ['joshbuddy@gmail.com', 'skarlso777@gmail.com']
13
14
  s.extra_rdoc_files = ['README.md']
14
15
  s.files = `git ls-files`.split("\n")
15
- s.homepage = %q{http://github.com/joshbuddy/jsonpath}
16
- s.rdoc_options = ["--charset=UTF-8"]
17
- s.require_paths = ["lib"]
18
- s.rubygems_version = %q{1.3.7}
19
- s.test_files = `git ls-files`.split("\n").select{|f| f =~ /^spec/}
16
+ s.homepage = 'https://github.com/joshbuddy/jsonpath'
17
+ s.rdoc_options = ['--charset=UTF-8']
18
+ s.require_paths = ['lib']
19
+ s.rubygems_version = '1.3.7'
20
+ s.test_files = `git ls-files`.split("\n").select { |f| f =~ /^spec/ }
20
21
  s.rubyforge_project = 'jsonpath'
21
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
22
23
  s.licenses = ['MIT']
23
24
 
24
25
  # dependencies
@@ -4,9 +4,10 @@ require 'jsonpath/proxy'
4
4
  require 'jsonpath/enumerable'
5
5
  require 'jsonpath/version'
6
6
 
7
+ # JsonPath: initializes the class with a given JsonPath and parses that path
8
+ # into a token array.
7
9
  class JsonPath
8
-
9
- PATH_ALL = '$..*'
10
+ PATH_ALL = '$..*'.freeze
10
11
 
11
12
  attr_accessor :path
12
13
 
@@ -14,39 +15,19 @@ class JsonPath
14
15
  @opts = opts
15
16
  scanner = StringScanner.new(path)
16
17
  @path = []
17
- while not scanner.eos?
18
- if token = scanner.scan(/\$/)
19
- @path << token
20
- elsif token = scanner.scan(/@/)
18
+ until scanner.eos?
19
+ if token = scanner.scan(/\$|@\B|\*|\.\./)
21
20
  @path << token
22
- elsif token = scanner.scan(/[a-zA-Z0-9_-]+/)
21
+ elsif token = scanner.scan(/[\$@a-zA-Z0-9:_-]+/)
23
22
  @path << "['#{token}']"
24
23
  elsif token = scanner.scan(/'(.*?)'/)
25
24
  @path << "[#{token}]"
26
25
  elsif token = scanner.scan(/\[/)
27
- count = 1
28
- while !count.zero?
29
- if t = scanner.scan(/\[/)
30
- token << t
31
- count += 1
32
- elsif t = scanner.scan(/\]/)
33
- token << t
34
- count -= 1
35
- elsif t = scanner.scan(/[^\[\]]+/)
36
- token << t
37
- elsif scanner.eos?
38
- raise ArgumentError, 'unclosed bracket'
39
- end
40
- end
41
- @path << token
26
+ @path << find_matching_brackets(token, scanner)
42
27
  elsif token = scanner.scan(/\]/)
43
28
  raise ArgumentError, 'unmatched closing bracket'
44
- elsif token = scanner.scan(/\.\./)
45
- @path << token
46
29
  elsif scanner.scan(/\./)
47
30
  nil
48
- elsif token = scanner.scan(/\*/)
49
- @path << token
50
31
  elsif token = scanner.scan(/[><=] \d+/)
51
32
  @path.last << token
52
33
  elsif token = scanner.scan(/./)
@@ -55,6 +36,24 @@ class JsonPath
55
36
  end
56
37
  end
57
38
 
39
+ def find_matching_brackets(token, scanner)
40
+ count = 1
41
+ until count.zero?
42
+ if t = scanner.scan(/\[/)
43
+ token << t
44
+ count += 1
45
+ elsif t = scanner.scan(/\]/)
46
+ token << t
47
+ count -= 1
48
+ elsif t = scanner.scan(/[^\[\]]+/)
49
+ token << t
50
+ elsif scanner.eos?
51
+ raise ArgumentError, 'unclosed bracket'
52
+ end
53
+ end
54
+ token
55
+ end
56
+
58
57
  def join(join_path)
59
58
  res = deep_clone
60
59
  res.path += JsonPath.new(join_path).path
@@ -70,12 +69,13 @@ class JsonPath
70
69
  end
71
70
 
72
71
  def enum_on(obj_or_str, mode = nil)
73
- JsonPath::Enumerable.new(self, self.class.process_object(obj_or_str), mode, @opts)
72
+ JsonPath::Enumerable.new(self, self.class.process_object(obj_or_str), mode,
73
+ @opts)
74
74
  end
75
75
  alias_method :[], :enum_on
76
76
 
77
77
  def self.on(obj_or_str, path, opts = nil)
78
- self.new(path, opts).on(process_object(obj_or_str))
78
+ new(path, opts).on(process_object(obj_or_str))
79
79
  end
80
80
 
81
81
  def self.for(obj_or_str)
@@ -83,6 +83,7 @@ class JsonPath
83
83
  end
84
84
 
85
85
  private
86
+
86
87
  def self.process_object(obj_or_str)
87
88
  obj_or_str.is_a?(String) ? MultiJson.decode(obj_or_str) : obj_or_str
88
89
  end
@@ -5,8 +5,15 @@ class JsonPath
5
5
  alias_method :allow_eval?, :allow_eval
6
6
 
7
7
  def initialize(path, object, mode, options = nil)
8
- @path, @object, @mode, @options = path.path, object, mode, options
9
- @allow_eval = @options && @options.key?(:allow_eval) ? @options[:allow_eval] : true
8
+ @path = path.path
9
+ @object = object
10
+ @mode = mode
11
+ @options = options
12
+ @allow_eval = if @options && @options.key?(:allow_eval)
13
+ @options[:allow_eval]
14
+ else
15
+ true
16
+ end
10
17
  end
11
18
 
12
19
  def each(context = @object, key = nil, pos = 0, &blk)
@@ -14,74 +21,78 @@ class JsonPath
14
21
  @_current_node = node
15
22
  return yield_value(blk, context, key) if pos == @path.size
16
23
  case expr = @path[pos]
17
- when '*', '..'
24
+ when '*', '..', '@'
18
25
  each(context, key, pos + 1, &blk)
19
26
  when '$'
20
27
  each(context, key, pos + 1, &blk) if node == @object
21
- when '@'
22
- each(context, key, pos + 1, &blk)
23
28
  when /^\[(.*)\]$/
24
- expr[1,expr.size - 2].split(',').each do |sub_path|
25
- case sub_path[0]
26
- when ?', ?"
27
- if node.is_a?(Hash)
28
- k = sub_path[1,sub_path.size - 2]
29
- each(node, k, pos + 1, &blk) if node.key?(k)
30
- end
31
- when ??
32
- raise "Cannot use ?(...) unless eval is enabled" unless allow_eval?
33
- case node
34
- when Hash, Array
35
- (node.is_a?(Hash) ? node.keys : (0..node.size)).each do |e|
36
- @_current_node = node[e]
37
- if process_function_or_literal(sub_path[1, sub_path.size - 1])
38
- each(@_current_node, nil, pos + 1, &blk)
39
- end
40
- end
41
- else
42
- yield node if process_function_or_literal(sub_path[1, sub_path.size - 1])
43
- end
44
- else
45
- if node.is_a?(Array)
46
- next if node.empty?
47
- array_args = sub_path.split(':')
48
- if array_args[0] == ?*
49
- start_idx = 0
50
- end_idx = node.size - 1
51
- else
52
- start_idx = process_function_or_literal(array_args[0], 0)
53
- next unless start_idx
54
- end_idx = (array_args[1] && process_function_or_literal(array_args[1], -1) || (sub_path.count(':') == 0 ? start_idx : -1))
55
- next unless end_idx
56
- if start_idx == end_idx
57
- next unless start_idx < node.size
58
- end
59
- end
60
- start_idx %= node.size
61
- end_idx %= node.size
62
- step = process_function_or_literal(array_args[2], 1)
63
- next unless step
64
- (start_idx..end_idx).step(step) {|i| each(node, i, pos + 1, &blk)}
65
- end
66
- end
67
- end
29
+ handle_wildecard(node, expr, context, key, pos, &blk)
68
30
  else
69
31
  if pos == (@path.size - 1) && node && allow_eval?
70
- if eval("node #{@path[pos]}")
71
- yield_value(blk, context, key)
72
- end
32
+ yield_value(blk, context, key) if instance_eval("node #{@path[pos]}")
73
33
  end
74
34
  end
75
35
 
76
- if pos > 0 && @path[pos-1] == '..'
36
+ if pos > 0 && @path[pos - 1] == '..' || (@path[pos - 1] == '*' && @path[pos] != '..')
77
37
  case node
78
- when Hash then node.each {|k, v| each(node, k, pos, &blk) }
79
- when Array then node.each_with_index {|n, i| each(node, i, pos, &blk) }
38
+ when Hash then node.each { |k, _| each(node, k, pos, &blk) }
39
+ when Array then node.each_with_index { |_, i| each(node, i, pos, &blk) }
80
40
  end
81
41
  end
82
42
  end
83
43
 
84
44
  private
45
+
46
+ def handle_wildecard(node, expr, context, key, pos, &blk)
47
+ expr[1, expr.size - 2].split(',').each do |sub_path|
48
+ case sub_path[0]
49
+ when '\'', '"'
50
+ next unless node.is_a?(Hash)
51
+ k = sub_path[1, sub_path.size - 2]
52
+ each(node, k, pos + 1, &blk) if node.key?(k)
53
+ when '?'
54
+ handle_question_mark(sub_path, node, pos, &blk)
55
+ else
56
+ next unless node.is_a?(Array) && !node.empty?
57
+ array_args = sub_path.split(':')
58
+ if array_args[0] == '*'
59
+ start_idx = 0
60
+ end_idx = node.size - 1
61
+ else
62
+ start_idx = process_function_or_literal(array_args[0], 0)
63
+ next unless start_idx
64
+ end_idx = (array_args[1] && process_function_or_literal(array_args[1], -1) || (sub_path.count(':') == 0 ? start_idx : -1))
65
+ next unless end_idx
66
+ next if start_idx == end_idx && start_idx >= node.size
67
+ end
68
+ start_idx %= node.size
69
+ end_idx %= node.size
70
+ step = process_function_or_literal(array_args[2], 1)
71
+ next unless step
72
+ (start_idx..end_idx).step(step) { |i| each(node, i, pos + 1, &blk) }
73
+ end
74
+ end
75
+ end
76
+
77
+ def handle_question_mark(sub_path, node, pos, &blk)
78
+ raise 'Cannot use ?(...) unless eval is enabled' unless allow_eval?
79
+ case node
80
+ when Array
81
+ node.size.times do |index|
82
+ @_current_node = node[index]
83
+ if process_function_or_literal(sub_path[1, sub_path.size - 1])
84
+ each(@_current_node, nil, pos + 1, &blk)
85
+ end
86
+ end
87
+ when Hash
88
+ if process_function_or_literal(sub_path[1, sub_path.size - 1])
89
+ each(@_current_node, nil, pos + 1, &blk)
90
+ end
91
+ else
92
+ yield node if process_function_or_literal(sub_path[1, sub_path.size - 1])
93
+ end
94
+ end
95
+
85
96
  def yield_value(blk, context, key)
86
97
  case @mode
87
98
  when nil
@@ -100,35 +111,42 @@ class JsonPath
100
111
  end
101
112
 
102
113
  def process_function_or_literal(exp, default = nil)
103
- if exp.nil?
104
- default
105
- elsif exp[0] == ?(
106
- return nil unless allow_eval? && @_current_node
107
- identifiers = /@?(\.(\w+))+/.match(exp)
114
+ return default if exp.nil? || exp.empty?
115
+ return Integer(exp) if exp[0] != '('
116
+ return nil unless allow_eval? && @_current_node
108
117
 
109
- if !identifiers.nil? && !@_current_node.methods.include?(identifiers[2].to_sym)
110
- exp_to_eval = exp.dup
111
- exp_to_eval[identifiers[0]] = identifiers[0].split('.').map{|el| el == '@' ? '@_current_node' : "['#{el}']"}.join
112
- begin
113
- return eval(exp_to_eval)
114
- rescue StandardError # if eval failed because of bad arguments or missing methods
115
- return default
116
- end
117
- end
118
+ identifiers = /@?(\.(\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
+ unless identifiers.nil? ||
125
+ @_current_node.methods.include?(identifiers[2].to_sym)
126
+
127
+ exp_to_eval = exp.dup
128
+ exp_to_eval[identifiers[0]] = identifiers[0].split('.').map do |el|
129
+ el == '@' ? '@_current_node' : "['#{el}']"
130
+ end.join
118
131
 
119
- # otherwise eval as is
120
- # TODO: this eval is wrong, because hash accessor could be nil and nil cannot be compared with anything,
121
- # for instance, @_current_node['price'] - we can't be sure that 'price' are in every node, but it's only in several nodes
122
- # I wrapped this eval into rescue returning false when error, but this eval should be refactored.
123
132
  begin
124
- eval(exp.gsub(/@/, '@_current_node'))
125
- rescue
126
- false
133
+ return instance_eval(exp_to_eval)
134
+ # if eval failed because of bad arguments or missing methods
135
+ rescue StandardError
136
+ return default
127
137
  end
128
- elsif exp.empty?
129
- default
130
- else
131
- Integer(exp)
138
+ end
139
+
140
+ # otherwise eval as is
141
+ # TODO: this eval is wrong, because hash accessor could be nil and nil
142
+ # cannot be compared with anything, for instance,
143
+ # @a_current_node['price'] - we can't be sure that 'price' are in every
144
+ # node, but it's only in several nodes I wrapped this eval into rescue
145
+ # returning false when error, but this eval should be refactored.
146
+ begin
147
+ instance_eval(exp.gsub(/@/, '@_current_node'))
148
+ rescue
149
+ false
132
150
  end
133
151
  end
134
152
  end
@@ -1,18 +1,18 @@
1
1
  class JsonPath
2
2
  class Proxy
3
3
  attr_reader :obj
4
- alias_method :to_hash, :obj
4
+ alias to_hash obj
5
5
 
6
6
  def initialize(obj)
7
7
  @obj = obj
8
8
  end
9
9
 
10
10
  def gsub(path, replacement = nil, &replacement_block)
11
- _gsub(_deep_copy, path, replacement ? proc{replacement} : replacement_block)
11
+ _gsub(_deep_copy, path, replacement ? proc { replacement } : replacement_block)
12
12
  end
13
13
 
14
14
  def gsub!(path, replacement = nil, &replacement_block)
15
- _gsub(@obj, path, replacement ? proc{replacement} : replacement_block)
15
+ _gsub(@obj, path, replacement ? proc { replacement } : replacement_block)
16
16
  end
17
17
 
18
18
  def delete(path = JsonPath::PATH_ALL)
@@ -32,8 +32,9 @@ class JsonPath
32
32
  end
33
33
 
34
34
  private
35
+
35
36
  def _deep_copy
36
- Marshal::load(Marshal::dump(@obj))
37
+ Marshal.load(Marshal.dump(@obj))
37
38
  end
38
39
 
39
40
  def _gsub(obj, path, replacement)
@@ -51,4 +52,4 @@ class JsonPath
51
52
  Proxy.new(obj)
52
53
  end
53
54
  end
54
- end
55
+ end
@@ -1,3 +1,3 @@
1
1
  class JsonPath
2
- VERSION = '0.5.8'
3
- end
2
+ VERSION = '0.7.0'.freeze
3
+ end
@@ -1,17 +1,17 @@
1
- class TestJsonpath < MiniTest::Unit::TestCase
1
+ require 'minitest/autorun'
2
+ require 'phocus'
3
+ require 'jsonpath'
2
4
 
5
+ class TestJsonpath < MiniTest::Unit::TestCase
3
6
  def setup
4
7
  @object = example_object
5
8
  @object2 = example_object
6
9
  end
7
10
 
8
11
  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
- }
12
+ assert_raises(ArgumentError) { JsonPath.new('$.store.book[0') }
13
+ assert_raises(ArgumentError) { JsonPath.new('$.store.book[0]]') }
14
+ assert_equal [9], JsonPath.new('$.store.book[0].price').on(@object)
15
15
  end
16
16
 
17
17
  def test_lookup_direct_path
@@ -66,12 +66,12 @@ class TestJsonpath < MiniTest::Unit::TestCase
66
66
 
67
67
  if RUBY_VERSION[/^1\.9/]
68
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)
69
+ assert_equal [@object['store']['book'][1]['price'], @object['store']['book'][3]['price'], @object['store']['bicycle']['price']], JsonPath.new('$..price[?(@ > 10)]').on(@object)
70
70
  end
71
71
  end
72
72
 
73
73
  def test_no_eval
74
- assert_equal [], JsonPath.new('$..book[(@.length-2)]', :allow_eval => false).on(@object)
74
+ assert_equal [], JsonPath.new('$..book[(@.length-2)]', allow_eval: false).on(@object)
75
75
  end
76
76
 
77
77
  def test_paths_with_underscores
@@ -79,7 +79,11 @@ class TestJsonpath < MiniTest::Unit::TestCase
79
79
  end
80
80
 
81
81
  def test_path_with_hyphens
82
- assert_equal [@object['store']['bicycle']['single-speed']], JsonPath.new('$.store.bicycle.single-speed').on(@object)
82
+ assert_equal [@object['store']['bicycle']['single-speed']], JsonPath.new('$.store.bicycle.single-speed').on(@object)
83
+ end
84
+
85
+ def test_path_with_colon
86
+ assert_equal [@object['store']['bicycle']['make:model']], JsonPath.new('$.store.bicycle.make:model').on(@object)
83
87
  end
84
88
 
85
89
  def test_paths_with_numbers
@@ -95,11 +99,11 @@ class TestJsonpath < MiniTest::Unit::TestCase
95
99
  end
96
100
 
97
101
  def test_counting
98
- assert_equal 49, JsonPath.new('$..*').on(@object).to_a.size
102
+ assert_equal 50, JsonPath.new('$..*').on(@object).to_a.size
99
103
  end
100
104
 
101
105
  def test_space_in_path
102
- assert_equal ['e'], JsonPath.new("$.'c d'").on({"a" => "a","b" => "b", "c d" => "e"})
106
+ assert_equal ['e'], JsonPath.new("$.'c d'").on('a' => 'a', 'b' => 'b', 'c d' => 'e')
103
107
  end
104
108
 
105
109
  def test_class_method
@@ -129,103 +133,152 @@ class TestJsonpath < MiniTest::Unit::TestCase
129
133
  end
130
134
 
131
135
  def test_weird_gsub!
132
- h = {'hi' => 'there'}
133
- JsonPath.for(@object).gsub!('$.*') { |n| h }
136
+ h = { 'hi' => 'there' }
137
+ JsonPath.for(@object).gsub!('$.*') { |_| h }
134
138
  assert_equal h, @object
135
139
  end
136
140
 
141
+ def test_gsub_to_false!
142
+ h = { 'hi' => 'there' }
143
+ h2 = { 'hi' => false }
144
+ assert_equal h2, JsonPath.for(h).gsub!('$.hi') { |_| false }.to_hash
145
+ end
146
+
147
+ def test_where_selector
148
+ JsonPath.for(@object).gsub!('$..book.price[?(@ > 20)]') { |p| p + 10 }
149
+ end
150
+
137
151
  def test_compact
138
- h = {'hi' => 'there', 'you' => nil}
152
+ h = { 'hi' => 'there', 'you' => nil }
139
153
  JsonPath.for(h).compact!
140
- assert_equal({'hi' => 'there'}, h)
154
+ assert_equal({ 'hi' => 'there' }, h)
141
155
  end
142
156
 
143
157
  def test_delete
144
- h = {'hi' => 'there', 'you' => nil}
158
+ h = { 'hi' => 'there', 'you' => nil }
145
159
  JsonPath.for(h).delete!('*.hi')
146
- assert_equal({'you' => nil}, h)
160
+ assert_equal({ 'you' => nil }, h)
161
+ end
162
+
163
+ def test_at_sign_in_json_element
164
+ data =
165
+ { '@colors' =>
166
+ [{ '@r' => 255, '@g' => 0, '@b' => 0 },
167
+ { '@r' => 0, '@g' => 255, '@b' => 0 },
168
+ { '@r' => 0, '@g' => 0, '@b' => 255 }] }
169
+
170
+ assert_equal [255, 0, 0], JsonPath.on(data, '$..@r')
147
171
  end
148
172
 
149
173
  def test_wildcard
150
- assert_equal @object['store']['book'].collect{|e| e['price']}.compact, JsonPath.on(@object, '$..book[*].price')
174
+ assert_equal @object['store']['book'].collect { |e| e['price'] }.compact, JsonPath.on(@object, '$..book[*].price')
175
+ end
176
+
177
+ def test_wildcard_on_intermediary_element
178
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'c' => 1 } } }, '$.a..c')
179
+ end
180
+
181
+ def test_wildcard_on_intermediary_element_v2
182
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'd' => { 'c' => 1 } } } }, '$.a..c')
183
+ end
184
+
185
+ def test_wildcard_on_intermediary_element_v3
186
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'd' => { 'c' => 1 } } } }, '$.a.*..c')
187
+ end
188
+
189
+ def test_wildcard_on_intermediary_element_v4
190
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'd' => { 'c' => 1 } } } }, '$.a.*..c')
191
+ end
192
+
193
+ def test_wildcard_on_intermediary_element_v5
194
+ assert_equal [1], JsonPath.on({ 'a' => { 'b' => { 'c' => 1 } } }, '$.a.*.c')
195
+ end
196
+
197
+ def test_wildcard_on_intermediary_element_v6
198
+ assert_equal ['red'], JsonPath.new('$.store.*.color').on(@object)
151
199
  end
152
200
 
153
201
  def test_wildcard_empty_array
154
- object = @object.merge("bicycle" => { "tire" => [] })
155
- assert_equal [], JsonPath.on(object, "$..bicycle.tire[*]")
202
+ object = @object.merge('bicycle' => { 'tire' => [] })
203
+ assert_equal [], JsonPath.on(object, '$..bicycle.tire[*]')
156
204
  end
157
205
 
158
- def test_support_filter_by_childnode_value
159
- assert_equal [@object['store']['book'][3]], JsonPath.new("$..book[?(@.price > 20)]").on(@object)
206
+ def test_support_filter_by_array_childnode_value
207
+ assert_equal [@object['store']['book'][3]], JsonPath.new('$..book[?(@.price > 20)]').on(@object)
160
208
  end
161
209
 
162
210
  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)
211
+ @object['store']['book'][0] = 'string_instead_of_object'
212
+ assert_equal [@object['store']['book'][3]], JsonPath.new('$..book[?(@.price > 20)]').on(@object)
165
213
  end
166
214
 
167
215
  def test_support_filter_by_childnode_value_and_select_child_key
168
- assert_equal [23], JsonPath.new("$..book[?(@.price > 20)].price").on(@object)
216
+ assert_equal [23], JsonPath.new('$..book[?(@.price > 20)].price').on(@object)
169
217
  end
170
218
 
171
219
  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)
220
+ assert_equal ['Osennie Vizity'], JsonPath.new('$..book[?(@.written.year == 1996)].title').on(@object)
173
221
  end
174
-
222
+
223
+ def test_support_filter_by_object_childnode_value
224
+ data = {
225
+ 'data' => {
226
+ 'type' => 'users',
227
+ 'id' => '123'
228
+ }
229
+ }
230
+ assert_equal [{ 'type' => 'users', 'id' => '123' }], JsonPath.new("$.data[?(@.type == 'users')]").on(data)
231
+ assert_equal [], JsonPath.new("$.data[?(@.type == 'admins')]").on(data)
232
+ end
233
+
175
234
  def example_object
176
- { "store"=> {
177
- "book" => [
178
- { "category"=> "reference",
179
- "author"=> "Nigel Rees",
180
- "title"=> "Sayings of the Century",
181
- "price"=> 9
182
- },
183
- { "category"=> "fiction",
184
- "author"=> "Evelyn Waugh",
185
- "title"=> "Sword of Honour",
186
- "price"=> 13
187
- },
188
- { "category"=> "fiction",
189
- "author"=> "Herman Melville",
190
- "title"=> "Moby Dick",
191
- "isbn"=> "0-553-21311-3",
192
- "price"=> 9
193
- },
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
199
- },
200
- { "category"=> "russian_fiction",
201
- "author"=> "Lukyanenko",
202
- "title"=> "Imperatory Illuziy",
203
- "written" => {
204
- "year" => 1995
205
- }
206
- },
207
- { "category"=> "russian_fiction",
208
- "author"=> "Lukyanenko",
209
- "title"=> "Osennie Vizity",
210
- "written" => {
211
- "year" => 1996
212
- }
213
- },
214
- { "category"=> "russian_fiction",
215
- "author"=> "Lukyanenko",
216
- "title"=> "Ne vremya dlya drakonov",
217
- "written" => {
218
- "year" => 1997
219
- }
220
- }
235
+ { 'store' => {
236
+ 'book' => [
237
+ { 'category' => 'reference',
238
+ 'author' => 'Nigel Rees',
239
+ 'title' => 'Sayings of the Century',
240
+ 'price' => 9 },
241
+ { 'category' => 'fiction',
242
+ 'author' => 'Evelyn Waugh',
243
+ 'title' => 'Sword of Honour',
244
+ 'price' => 13 },
245
+ { 'category' => 'fiction',
246
+ 'author' => 'Herman Melville',
247
+ 'title' => 'Moby Dick',
248
+ 'isbn' => '0-553-21311-3',
249
+ 'price' => 9 },
250
+ { 'category' => 'fiction',
251
+ 'author' => 'J. R. R. Tolkien',
252
+ 'title' => 'The Lord of the Rings',
253
+ 'isbn' => '0-395-19395-8',
254
+ 'price' => 23 },
255
+ { 'category' => 'russian_fiction',
256
+ 'author' => 'Lukyanenko',
257
+ 'title' => 'Imperatory Illuziy',
258
+ 'written' => {
259
+ 'year' => 1995
260
+ } },
261
+ { 'category' => 'russian_fiction',
262
+ 'author' => 'Lukyanenko',
263
+ 'title' => 'Osennie Vizity',
264
+ 'written' => {
265
+ 'year' => 1996
266
+ } },
267
+ { 'category' => 'russian_fiction',
268
+ 'author' => 'Lukyanenko',
269
+ 'title' => 'Ne vremya dlya drakonov',
270
+ 'written' => {
271
+ 'year' => 1997
272
+ } }
221
273
  ],
222
- "bicycle"=> {
223
- "color"=> "red",
224
- "price"=> 20,
225
- "catalogue_number" => 12345,
226
- "single-speed" => "no",
227
- "2seater" => "yes"}
274
+ 'bicycle' => {
275
+ 'color' => 'red',
276
+ 'price' => 20,
277
+ 'catalogue_number' => 123_45,
278
+ 'single-speed' => 'no',
279
+ '2seater' => 'yes',
280
+ 'make:model' => 'Zippy Sweetwheeler'
281
+ }
228
282
  } }
229
283
  end
230
-
231
284
  end
@@ -1,6 +1,10 @@
1
+ require 'minitest/autorun'
2
+ require 'phocus'
3
+ require 'jsonpath'
4
+
1
5
  class TestJsonpathBin < MiniTest::Unit::TestCase
2
6
  def setup
3
- @runner = "ruby -Ilib bin/jsonpath"
7
+ @runner = 'ruby -Ilib bin/jsonpath'
4
8
  @original_dir = Dir.pwd
5
9
  Dir.chdir(File.join(File.dirname(__FILE__), '..'))
6
10
  end
@@ -11,11 +15,7 @@ class TestJsonpathBin < MiniTest::Unit::TestCase
11
15
  end
12
16
 
13
17
  def test_stdin
14
- assert_equal '["time"]', `echo '{"test": "time"}' | #{@runner} '$.test'`.strip
15
- end
16
-
17
- def test_stdin
18
- File.open('/tmp/test.json', 'w'){|f| f << '{"test": "time"}'}
18
+ File.open('/tmp/test.json', 'w') { |f| f << '{"test": "time"}' }
19
19
  assert_equal '["time"]', `#{@runner} '$.test' /tmp/test.json`.strip
20
20
  end
21
- end
21
+ end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonpath
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.8
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Hull
8
+ - Gergely Brautigam
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-11-09 00:00:00.000000000 Z
12
+ date: 2017-05-02 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: multi_json
@@ -95,7 +96,9 @@ dependencies:
95
96
  - !ruby/object:Gem::Version
96
97
  version: '0'
97
98
  description: Ruby implementation of http://goessner.net/articles/JsonPath/.
98
- email: joshbuddy@gmail.com
99
+ email:
100
+ - joshbuddy@gmail.com
101
+ - skarlso777@gmail.com
99
102
  executables:
100
103
  - jsonpath
101
104
  extensions: []
@@ -107,6 +110,7 @@ files:
107
110
  - ".rspec"
108
111
  - ".travis.yml"
109
112
  - Gemfile
113
+ - LICENSE.md
110
114
  - README.md
111
115
  - Rakefile
112
116
  - bin/jsonpath
@@ -117,7 +121,7 @@ files:
117
121
  - lib/jsonpath/version.rb
118
122
  - test/test_jsonpath.rb
119
123
  - test/test_jsonpath_bin.rb
120
- homepage: http://github.com/joshbuddy/jsonpath
124
+ homepage: https://github.com/joshbuddy/jsonpath
121
125
  licenses:
122
126
  - MIT
123
127
  metadata: {}
@@ -138,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
142
  version: '0'
139
143
  requirements: []
140
144
  rubyforge_project: jsonpath
141
- rubygems_version: 2.4.6
145
+ rubygems_version: 2.6.10
142
146
  signing_key:
143
147
  specification_version: 4
144
148
  summary: Ruby implementation of http://goessner.net/articles/JsonPath/