jsonpath 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-11-28
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/jsonpath.rb
7
+ script/console
8
+ script/destroy
9
+ script/generate
10
+ test/test_helper.rb
11
+ test/test_jsonpath.rb
data/README.txt ADDED
@@ -0,0 +1,46 @@
1
+ = jsonpath
2
+
3
+ == DESCRIPTION:
4
+
5
+ * FIX (describe your package)
6
+
7
+ == FEATURES/PROBLEMS:
8
+
9
+ * FIX (list of features or problems)
10
+
11
+ == SYNOPSIS:
12
+
13
+ FIX (code sample of usage)
14
+
15
+ == REQUIREMENTS:
16
+
17
+ * FIX (list of requirements)
18
+
19
+ == INSTALL:
20
+
21
+ * FIX (sudo gem install, anything else)
22
+
23
+ == LICENSE:
24
+
25
+ (The MIT License)
26
+
27
+ Copyright (c) 2008 FIXME full name
28
+
29
+ Permission is hereby granted, free of charge, to any person obtaining
30
+ a copy of this software and associated documentation files (the
31
+ 'Software'), to deal in the Software without restriction, including
32
+ without limitation the rights to use, copy, modify, merge, publish,
33
+ distribute, sublicense, and/or sell copies of the Software, and to
34
+ permit persons to whom the Software is furnished to do so, subject to
35
+ the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be
38
+ included in all copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
41
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
44
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
45
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
46
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require 'rake/testtask'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "jsonpath"
7
+ s.description = s.summary = "Ruby implementation of http://goessner.net/articles/JsonPath/"
8
+ s.email = "joshbuddy@gmail.com"
9
+ s.homepage = "http://github.com/joshbuddy/jsonpath"
10
+ s.authors = ['Joshua Hull']
11
+ s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
12
+ end
13
+ Jeweler::GemcutterTasks.new
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
+ end
17
+
18
+ require 'rake/rdoctask'
19
+ desc "Generate documentation"
20
+ Rake::RDocTask.new do |rd|
21
+ rd.main = "README.rdoc"
22
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
23
+ rd.rdoc_dir = 'rdoc'
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'spec'
28
+ require 'spec/rake/spectask'
29
+
30
+ Spec::Rake::SpecTask.new do |t|
31
+ t.spec_opts ||= []
32
+ t.spec_opts << "--options" << "spec/spec.opts"
33
+ t.spec_files = FileList['spec/*.rb']
34
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/lib/jsonpath.rb ADDED
@@ -0,0 +1,28 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+
5
+ class JsonPath
6
+ VERSION = '0.0.1'
7
+
8
+ def self.path(expression)
9
+ @expression = Expression.new(expression)
10
+ if block_given?
11
+ yield @expression
12
+ else
13
+ @expression
14
+ end
15
+ end
16
+
17
+ def self.wrap(object)
18
+ @wrapper = Wrapper.new(object)
19
+ if block_given?
20
+ yield @wrapper
21
+ else
22
+ @wrapper
23
+ end
24
+ end
25
+
26
+ end
27
+ require File.join('jsonpath', 'expression')
28
+ require File.join('jsonpath', 'wrapper')
@@ -0,0 +1,97 @@
1
+ require 'strscan'
2
+
3
+ class JsonPath
4
+ class Expression
5
+ def initialize(expression, object = nil)
6
+ scanner = StringScanner.new(expression)
7
+ @path = []
8
+ bracket_count = 0
9
+ while not scanner.eos?
10
+ token = scanner.scan_until(/($|\$|@|[a-zA-Z]+|\[.*?\]|\.\.|\.(?!\.))/)
11
+ case token
12
+ when '.'
13
+ # do nothing
14
+ when /^[a-zA-Z]+$/
15
+ @path << "['#{token}']"
16
+ else
17
+ bracket_count == 0 && @path << token or @path[-1] += token
18
+ bracket_count += token.count('[') - token.count(']')
19
+ end
20
+ end
21
+ @object = object
22
+ end
23
+
24
+ def test?(node = @object)
25
+ each(node) {|n| return true}
26
+ false
27
+ end
28
+
29
+ def to_a(node = @object)
30
+ store = []
31
+ each(node) {|o| store << o}
32
+ store
33
+ end
34
+
35
+ def map(node = @object)
36
+ store = []
37
+ each(node) {|o| store << (yield o)}
38
+ store
39
+ end
40
+
41
+ def each(node = @object, pos = 0, options = {}, &blk)
42
+ if pos == @path.size
43
+ return blk.call(node)
44
+ else
45
+ case expr = @path[pos]
46
+ when '*', '..'
47
+ each(node, pos + 1, &blk)
48
+ when '$'
49
+ each(node, pos + 1, &blk) if node == @object
50
+ when '@'
51
+ each(node, pos + 1, &blk)
52
+ when /^\[(.*)\]$/
53
+ expr[1,expr.size - 2].split(',').each do |sub_path|
54
+ case sub_path[0]
55
+ when ?', ?"
56
+ if node.is_a?(Hash)
57
+ key = sub_path[1,sub_path.size - 2]
58
+ each(node[key], pos + 1, &blk) if node.key?(key)
59
+ end
60
+ when ??
61
+ (node.is_a?(Hash) ? node.keys : (0..node.size)).each do |e|
62
+ ::JsonPath.path(sub_path[2,sub_path.size - 3]) do |jp|
63
+ @obj = node[e]
64
+ begin
65
+ each(node[e], pos + 1, &blk) if jp.test?(node[e])
66
+ rescue
67
+ # ignore ..
68
+ end
69
+ end
70
+ end
71
+ else
72
+ if node.is_a?(Array)
73
+ @obj = node
74
+ array_args = sub_path.gsub('@','@obj').split(':')
75
+ start_idx = (array_args[0] && eval(array_args[0]) || 0) % node.size
76
+ end_idx = (array_args[1] && eval(array_args[1]) || (sub_path.count(':') == 0 ? start_idx : -1)) % node.size
77
+ step = array_args[2] && eval(array_args[2]) || 1
78
+ (start_idx..end_idx).step(step) {|i| each(node[i], pos + 1, &blk)}
79
+ end
80
+ end
81
+ end
82
+ else
83
+ blk.call(node) if pos == (@path.size - 1) && eval("node #{@path[pos]}")
84
+ end
85
+
86
+ if pos > 0 && @path[pos-1] == '..'
87
+ case node
88
+ when Hash
89
+ node.values.each {|n| each(n, pos, &blk) }
90
+ when Array
91
+ node.each {|n| each(n, pos, &blk) }
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,14 @@
1
+ class JsonPath
2
+ class Wrapper
3
+ attr_accessor :object
4
+
5
+ def initialize(object)
6
+ @object = object
7
+ end
8
+
9
+ def path(expression)
10
+ @expression = Expression.new(expression, @object)
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,88 @@
1
+ require "rubygems"
2
+ require 'lib/jsonpath.rb'
3
+ require 'json'
4
+ describe "JsonPath" do
5
+
6
+ object = { "store"=> {
7
+ "book" => [
8
+ { "category"=> "reference",
9
+ "author"=> "Nigel Rees",
10
+ "title"=> "Sayings of the Century",
11
+ "price"=> 8.95
12
+ },
13
+ { "category"=> "fiction",
14
+ "author"=> "Evelyn Waugh",
15
+ "title"=> "Sword of Honour",
16
+ "price"=> 12.99
17
+ },
18
+ { "category"=> "fiction",
19
+ "author"=> "Herman Melville",
20
+ "title"=> "Moby Dick",
21
+ "isbn"=> "0-553-21311-3",
22
+ "price"=> 8.99
23
+ },
24
+ { "category"=> "fiction",
25
+ "author"=> "J. R. R. Tolkien",
26
+ "title"=> "The Lord of the Rings",
27
+ "isbn"=> "0-395-19395-8",
28
+ "price"=> 22.99
29
+ }
30
+ ],
31
+ "bicycle"=> {
32
+ "color"=> "red",
33
+ "price"=> 19.95
34
+ }
35
+ }
36
+ }
37
+ json = JsonPath.wrap(object)
38
+
39
+ it "should lookup a direct path" do
40
+ json.path('$.store.*').to_a.first['book'].size.should == 4
41
+ end
42
+
43
+ it "should retrieve all authors" do
44
+ json.path('$..author').to_a.should == [
45
+ object['store']['book'][0]['author'],
46
+ object['store']['book'][1]['author'],
47
+ object['store']['book'][2]['author'],
48
+ object['store']['book'][3]['author']
49
+ ]
50
+ end
51
+
52
+ it "should retrieve all prices" do
53
+ json.path('$..price').to_a.should == [
54
+ object['store']['bicycle']['price'],
55
+ object['store']['book'][0]['price'],
56
+ object['store']['book'][1]['price'],
57
+ object['store']['book'][2]['price'],
58
+ object['store']['book'][3]['price']
59
+ ]
60
+ end
61
+
62
+ it "should recognize all types of array splices" do
63
+ json.path('$..book[0:1:1]').to_a.should == [object['store']['book'][0], object['store']['book'][1]]
64
+ json.path('$..book[1::2]').to_a.should == [object['store']['book'][1], object['store']['book'][3]]
65
+ json.path('$..book[::2]').to_a.should == [object['store']['book'][0], object['store']['book'][2]]
66
+ json.path('$..book[:-2:2]').to_a.should == [object['store']['book'][0], object['store']['book'][2]]
67
+ json.path('$..book[2::]').to_a.should == [object['store']['book'][2], object['store']['book'][3]]
68
+ end
69
+
70
+ it "should recognize array comma syntax" do
71
+ json.path('$..book[0,1]').to_a.should == [object['store']['book'][0], object['store']['book'][1]]
72
+ json.path('$..book[2,-1::]').to_a.should == [object['store']['book'][2], object['store']['book'][3]]
73
+ end
74
+
75
+ it "should support filters" do
76
+ json.path("$..book[?(@['isbn'])]").to_a.should == [object['store']['book'][2], object['store']['book'][3]]
77
+ json.path("$..book[?(@['price'] < 10)]").to_a.should == [object['store']['book'][0], object['store']['book'][2]]
78
+ end
79
+
80
+ it "should support eval'd array indicies" do
81
+ json.path('$..book[(@.length-2)]').to_a.should == [object['store']['book'][2]]
82
+ end
83
+
84
+ it "should correct retrieve the right number of all nodes" do
85
+ json.path('$..*').to_a.size.should == 28
86
+ end
87
+
88
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,7 @@
1
+ --colour
2
+ --format
3
+ specdoc
4
+ --loadby
5
+ mtime
6
+ --reverse
7
+ --backtrace
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsonpath
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Hull
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-25 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Ruby implementation of http://goessner.net/articles/JsonPath/
17
+ email: joshbuddy@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.txt
24
+ files:
25
+ - History.txt
26
+ - Manifest.txt
27
+ - README.txt
28
+ - Rakefile
29
+ - VERSION
30
+ - lib/jsonpath.rb
31
+ - lib/jsonpath/expression.rb
32
+ - lib/jsonpath/wrapper.rb
33
+ - spec/jsonpath_spec.rb
34
+ - spec/spec.opts
35
+ has_rdoc: true
36
+ homepage: http://github.com/joshbuddy/jsonpath
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Ruby implementation of http://goessner.net/articles/JsonPath/
63
+ test_files:
64
+ - spec/jsonpath_spec.rb