jsonpath 0.3.3 → 0.4.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.
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: []