Skip to content

Commit a605269

Browse files
authored
A lot of refactoring and addresses pull #39 and #41. (#46)
* A lot of refactoring and addresses pull #39 and #41. * Closes #44 and Closes #30. * Bumped version. * Updated readme.
1 parent e4474e0 commit a605269

12 files changed

Lines changed: 321 additions & 222 deletions

File tree

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
rvm:
2-
- 1.9.2
3-
- 1.9.3
4-
- 2.0.0
2+
- 2.1.6
3+
- 2.3.1
54
- jruby

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ enum.any?{ |c| c == 'red' }
105105

106106
You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor.
107107

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+
108112
### Manipulation
109113

110114
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).
130134
to_hash
131135
# => {"candy" => "big turks"}
132136
~~~~~
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!

Rakefile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ require 'bundler'
22
Bundler::GemHelper.install_tasks
33

44
task :test do
5-
$: << 'lib'
6-
require 'minitest/autorun'
7-
require 'phocus'
8-
require 'jsonpath'
5+
$LOAD_PATH << 'lib'
96
Dir['./test/**/test_*.rb'].each { |test| require test }
107
end
118

12-
task :default => :test
9+
task default: :test

bin/jsonpath

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ usage unless ARGV[0]
1515

1616
jsonpath = JsonPath.new(ARGV[0])
1717
case ARGV[1]
18-
when nil #stdin
18+
when nil # stdin
1919
puts MultiJson.encode(jsonpath.on(MultiJson.decode(STDIN.read)))
2020
when String
2121
puts MultiJson.encode(jsonpath.on(MultiJson.decode(File.exist?(ARGV[1]) ? File.read(ARGV[1]) : ARGV[1])))

jsonpath.gemspec

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@ require File.join(File.dirname(__FILE__), 'lib', 'jsonpath', 'version')
55
Gem::Specification.new do |s|
66
s.name = 'jsonpath'
77
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']
1314
s.extra_rdoc_files = ['README.md']
1415
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/ }
2021
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) }
2223
s.licenses = ['MIT']
2324

2425
# dependencies

lib/jsonpath.rb

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,30 @@
44
require 'jsonpath/enumerable'
55
require 'jsonpath/version'
66

7+
# JsonPath: initializes the class with a given JsonPath and parses that path
8+
# into a token array.
79
class JsonPath
8-
9-
PATH_ALL = '$..*'
10+
PATH_ALL = '$..*'.freeze
1011

1112
attr_accessor :path
1213

1314
def initialize(path, opts = nil)
1415
@opts = opts
1516
scanner = StringScanner.new(path)
1617
@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|\*|\.\./)
2120
@path << token
22-
elsif token = scanner.scan(/[a-zA-Z0-9_-]+/)
21+
elsif token = scanner.scan(/[\$@a-zA-Z0-9:_-]+/)
2322
@path << "['#{token}']"
2423
elsif token = scanner.scan(/'(.*?)'/)
2524
@path << "[#{token}]"
2625
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)
4227
elsif token = scanner.scan(/\]/)
4328
raise ArgumentError, 'unmatched closing bracket'
44-
elsif token = scanner.scan(/\.\./)
45-
@path << token
4629
elsif scanner.scan(/\./)
4730
nil
48-
elsif token = scanner.scan(/\*/)
49-
@path << token
5031
elsif token = scanner.scan(/[><=] \d+/)
5132
@path.last << token
5233
elsif token = scanner.scan(/./)
@@ -55,6 +36,24 @@ def initialize(path, opts = nil)
5536
end
5637
end
5738

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+
5857
def join(join_path)
5958
res = deep_clone
6059
res.path += JsonPath.new(join_path).path
@@ -70,19 +69,21 @@ def first(obj_or_str, *args)
7069
end
7170

7271
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)
7474
end
7575
alias_method :[], :enum_on
7676

7777
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))
7979
end
8080

8181
def self.for(obj_or_str)
8282
Proxy.new(process_object(obj_or_str))
8383
end
8484

8585
private
86+
8687
def self.process_object(obj_or_str)
8788
obj_or_str.is_a?(String) ? MultiJson.decode(obj_or_str) : obj_or_str
8889
end

0 commit comments

Comments
 (0)