jsonpath 0.5.8 → 0.7.0

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