jsonpath 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -47,4 +47,10 @@ As well, you can include it as a library.
47
47
  # => 8.95
48
48
  ~~~~~
49
49
 
50
- You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor.
50
+ You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor.
51
+
52
+ If you'd like to do substitution in a json object, do this:
53
+ JsonPath.for({'test' => 'time'}).gsub('$..test') {|v| v << v}
54
+
55
+ The result will be
56
+ {'test' => 'timetime'}
data/bin/jsonpath CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/local/env ruby
2
2
 
3
3
  require 'jsonpath'
4
- require 'json'
4
+ require 'yajl'
5
5
 
6
6
  def usage
7
7
  puts "jsonpath [expression] (file|string)
@@ -17,8 +17,8 @@ jsonpath = JsonPath.new(ARGV[0])
17
17
  case ARGV[1]
18
18
  when nil #stdin
19
19
  until STDIN.eof?
20
- puts jsonpath.on(JSON.parse(STDIN.readline)).to_a.to_json
20
+ puts Yajl::Encoder.encode(jsonpath.on(Yajl::Parser.parse(STDIN.readline)))
21
21
  end
22
22
  when String
23
- puts jsonpath.on(JSON.parse(File.exist?(ARGV[1]) ? File.read(ARGV[1]) : ARGV[1])).to_a.to_json
23
+ puts Yajl::Encoder.encode(jsonpath.on(Yajl::Parser.parse(File.exist?(ARGV[1]) ? File.read(ARGV[1]) : ARGV[1])))
24
24
  end
data/jsonpath.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
22
 
23
23
  # dependencies
24
- s.add_runtime_dependency 'json'
24
+ s.add_runtime_dependency 'yajl-ruby'
25
25
  s.add_development_dependency 'code_stats'
26
26
  s.add_development_dependency 'rake'
27
27
  s.add_development_dependency 'minitest', '~> 2.2.0'
data/lib/jsonpath.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'strscan'
2
+ require 'jsonpath/proxy'
2
3
  require 'jsonpath/enumerable'
3
4
  require 'jsonpath/version'
4
5
 
@@ -61,4 +62,7 @@ class JsonPath
61
62
  self.new(path, opts).on(object)
62
63
  end
63
64
 
65
+ def self.for(obj)
66
+ Proxy.new(obj)
67
+ end
64
68
  end
@@ -1,32 +1,37 @@
1
1
  class JsonPath
2
2
  class Enumerable
3
3
  include ::Enumerable
4
+ attr_reader :allow_eval
5
+ alias_method :allow_eval?, :allow_eval
4
6
 
5
7
  def initialize(path, object, options = nil)
6
8
  @path, @object, @options = path.path, object, options
9
+ @allow_eval = @options && @options.key?(:allow_eval) ? @options[:allow_eval] : true
10
+ @mode = @options && @options[:mode]
7
11
  end
8
12
 
9
- def each(node = @object, pos = 0, &blk)
10
- return blk.call(node) if pos == @path.size
13
+ def each(context = @object, key = nil, pos = 0, &blk)
14
+ node = key ? context[key] : context
15
+ return yield_value(blk, context, key) if pos == @path.size
11
16
  case expr = @path[pos]
12
17
  when '*', '..'
13
- each(node, pos + 1, &blk)
18
+ each(context, key, pos + 1, &blk)
14
19
  when '$'
15
- each(node, pos + 1, &blk) if node == @object
20
+ each(context, key, pos + 1, &blk) if node == @object
16
21
  when '@'
17
- each(node, pos + 1, &blk)
22
+ each(context, key, pos + 1, &blk)
18
23
  when /^\[(.*)\]$/
19
24
  expr[1,expr.size - 2].split(',').each do |sub_path|
20
25
  case sub_path[0]
21
26
  when ?', ?"
22
27
  if node.is_a?(Hash)
23
- key = sub_path[1,sub_path.size - 2]
24
- each(node[key], pos + 1, &blk) if node.key?(key)
28
+ k = sub_path[1,sub_path.size - 2]
29
+ each(node, k, pos + 1, &blk) if node.key?(k)
25
30
  end
26
31
  when ??
27
32
  (node.is_a?(Hash) ? node.keys : (0..node.size)).each do |e|
28
33
  subenum = ::JsonPath.new(sub_path[2, sub_path.size - 3]).on(node[e])
29
- each(node[e], pos + 1, &blk) if subenum.any?{|n| true}
34
+ each(node, e, pos + 1, &blk) if subenum.any?{|n| true}
30
35
  end
31
36
  else
32
37
  if node.is_a?(Array)
@@ -40,25 +45,41 @@ class JsonPath
40
45
  end_idx %= node.size
41
46
  step = process_function_or_literal(array_args[2], 1)
42
47
  next unless step
43
- (start_idx..end_idx).step(step) {|i| each(node[i], pos + 1, &blk)}
48
+ (start_idx..end_idx).step(step) {|i| each(node, i, pos + 1, &blk)}
44
49
  end
45
50
  end
46
51
  end
47
52
  else
48
- blk.call(node) if pos == (@path.size - 1) && node && allow_eval? && eval("node #{@path[pos]}")
53
+ if pos == (@path.size - 1) && node && allow_eval?
54
+ if eval("node #{@path[pos]}")
55
+ yield_value(blk, context, key)
56
+ end
57
+ end
49
58
  end
50
59
 
51
60
  if pos > 0 && @path[pos-1] == '..'
52
61
  case node
53
- when Hash
54
- node.values.each {|n| each(n, pos, &blk) }
55
- when Array
56
- node.each {|n| each(n, pos, &blk) }
62
+ when Hash then node.each {|k, v| each(node, k, pos, &blk) }
63
+ when Array then node.each_with_index {|n, i| each(node, i, pos, &blk) }
57
64
  end
58
65
  end
59
66
  end
60
67
 
61
68
  private
69
+ def yield_value(blk, context, key)
70
+ @substitute_with = nil
71
+ case @mode
72
+ when nil
73
+ blk.call(key ? context[key] : context)
74
+ when :substitute
75
+ if key
76
+ context[key] = blk.call(context[key])
77
+ else
78
+ context.replace(blk.call(context[key]))
79
+ end
80
+ end
81
+ end
82
+
62
83
  def process_function_or_literal(exp, default)
63
84
  if exp.nil?
64
85
  default
@@ -70,9 +91,5 @@ class JsonPath
70
91
  Integer(exp)
71
92
  end
72
93
  end
73
-
74
- def allow_eval?
75
- @options.nil? || @options[:allow_eval] != false
76
- end
77
94
  end
78
95
  end
@@ -0,0 +1,25 @@
1
+ class JsonPath
2
+ class Proxy
3
+ def initialize(obj)
4
+ @obj = obj
5
+ end
6
+
7
+ def gsub(path, replacement = nil, &replacement_block)
8
+ _gsub(deep_copy, path, replacement ? proc{replacement} : replacement_block)
9
+ end
10
+
11
+ def gsub!(path, replacement = nil, &replacement_block)
12
+ _gsub(@obj, path, replacement ? proc{replacement} : replacement_block)
13
+ end
14
+
15
+ private
16
+ def deep_copy
17
+ Marshal::load(Marshal::dump(@obj))
18
+ end
19
+
20
+ def _gsub(obj, path, replacement)
21
+ JsonPath.new(path, :mode => :substitute).enum_on(obj).each(&replacement)
22
+ obj
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  class JsonPath
2
- VERSION = '0.3.3'
2
+ VERSION = '0.4.0' unless const_defined?(:VERSION)
3
3
  end
@@ -1,40 +1,12 @@
1
1
  class TestJsonpath < MiniTest::Unit::TestCase
2
2
 
3
3
  def setup
4
- @object = { "store"=> {
5
- "book" => [
6
- { "category"=> "reference",
7
- "author"=> "Nigel Rees",
8
- "title"=> "Sayings of the Century",
9
- "price"=> 8.95
10
- },
11
- { "category"=> "fiction",
12
- "author"=> "Evelyn Waugh",
13
- "title"=> "Sword of Honour",
14
- "price"=> 12.99
15
- },
16
- { "category"=> "fiction",
17
- "author"=> "Herman Melville",
18
- "title"=> "Moby Dick",
19
- "isbn"=> "0-553-21311-3",
20
- "price"=> 8.99
21
- },
22
- { "category"=> "fiction",
23
- "author"=> "J. R. R. Tolkien",
24
- "title"=> "The Lord of the Rings",
25
- "isbn"=> "0-395-19395-8",
26
- "price"=> 22.99
27
- }
28
- ],
29
- "bicycle"=> {
30
- "color"=> "red",
31
- "price"=> 19.95,
32
- "catalogue_number" => 12345 }
33
- } }
4
+ @object = example_object
5
+ @object2 = example_object
34
6
  end
35
7
 
36
8
  def test_lookup_direct_path
37
- assert_equal 4, JsonPath.new('$.store.*').on(@object).to_a.first['book'].size
9
+ assert_equal 4, JsonPath.new('$.store.*').on(@object).first['book'].size
38
10
  end
39
11
 
40
12
  def test_retrieve_all_authors
@@ -103,4 +75,61 @@ class TestJsonpath < MiniTest::Unit::TestCase
103
75
  assert_equal JsonPath.new('$..author').on(@object), JsonPath.on(@object, '$..author')
104
76
  end
105
77
 
78
+ def test_gsub
79
+ @object2['store']['bicycle']['price'] += 10
80
+ @object2['store']['book'][0]['price'] += 10
81
+ @object2['store']['book'][1]['price'] += 10
82
+ @object2['store']['book'][2]['price'] += 10
83
+ @object2['store']['book'][3]['price'] += 10
84
+ assert_equal @object2, JsonPath.for(@object).gsub('$..price') { |p| p + 10 }
85
+ end
86
+
87
+ def test_gsub!
88
+ JsonPath.for(@object).gsub!('$..price') { |p| p + 10 }
89
+ assert_equal 30, @object['store']['bicycle']['price']
90
+ assert_equal 19, @object['store']['book'][0]['price']
91
+ assert_equal 23, @object['store']['book'][1]['price']
92
+ assert_equal 19, @object['store']['book'][2]['price']
93
+ assert_equal 33, @object['store']['book'][3]['price']
94
+ end
95
+
96
+ def test_weird_gsub!
97
+ h = {'hi' => 'there'}
98
+ JsonPath.for(@object).gsub!('$.*') { |n| h }
99
+ assert_equal h, @object
100
+ end
101
+
102
+ def example_object
103
+ { "store"=> {
104
+ "book" => [
105
+ { "category"=> "reference",
106
+ "author"=> "Nigel Rees",
107
+ "title"=> "Sayings of the Century",
108
+ "price"=> 9
109
+ },
110
+ { "category"=> "fiction",
111
+ "author"=> "Evelyn Waugh",
112
+ "title"=> "Sword of Honour",
113
+ "price"=> 13
114
+ },
115
+ { "category"=> "fiction",
116
+ "author"=> "Herman Melville",
117
+ "title"=> "Moby Dick",
118
+ "isbn"=> "0-553-21311-3",
119
+ "price"=> 9
120
+ },
121
+ { "category"=> "fiction",
122
+ "author"=> "J. R. R. Tolkien",
123
+ "title"=> "The Lord of the Rings",
124
+ "isbn"=> "0-395-19395-8",
125
+ "price"=> 23
126
+ }
127
+ ],
128
+ "bicycle"=> {
129
+ "color"=> "red",
130
+ "price"=> 20,
131
+ "catalogue_number" => 12345 }
132
+ } }
133
+ end
134
+
106
135
  end
@@ -0,0 +1,21 @@
1
+ class TestJsonpathBin < MiniTest::Unit::TestCase
2
+ def setup
3
+ @runner = "ruby -Ilib bin/jsonpath"
4
+ @original_dir = Dir.pwd
5
+ Dir.chdir(File.join(File.dirname(__FILE__), '..'))
6
+ end
7
+
8
+ def teardown
9
+ Dir.chdir(@original_dir)
10
+ `rm /tmp/test.json`
11
+ end
12
+
13
+ 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"}'}
19
+ assert_equal '["time"]', `#{@runner} '$.test' /tmp/test.json`.strip
20
+ end
21
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonpath
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 3
10
- version: 0.3.3
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joshua Hull
@@ -15,11 +15,11 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-08 00:00:00 -07:00
18
+ date: 2011-06-16 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: json
22
+ name: yajl-ruby
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
@@ -125,8 +125,10 @@ files:
125
125
  - jsonpath.gemspec
126
126
  - lib/jsonpath.rb
127
127
  - lib/jsonpath/enumerable.rb
128
+ - lib/jsonpath/proxy.rb
128
129
  - lib/jsonpath/version.rb
129
130
  - test/test_jsonpath.rb
131
+ - test/test_jsonpath_bin.rb
130
132
  has_rdoc: true
131
133
  homepage: http://github.com/joshbuddy/jsonpath
132
134
  licenses: []