jazor 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.txt +23 -675
- data/README.rdoc +50 -18
- data/Rakefile +1 -1
- data/bin/jazor +70 -78
- data/lib/jazor.rb +20 -31
- data/test/test_jazor.rb +26 -42
- data/test/test_jazor_bin.rb +9 -15
- data/test/test_rest_client.rb +2 -4
- metadata +77 -10
data/README.rdoc
CHANGED
@@ -1,36 +1,68 @@
|
|
1
1
|
= Jazor
|
2
2
|
|
3
|
-
Jazor is a simple command line JSON parsing tool.
|
3
|
+
Jazor (rhymes with "razor") is a simple command line JSON parsing tool.
|
4
4
|
|
5
5
|
== Installation
|
6
6
|
|
7
7
|
gem install jazor
|
8
8
|
|
9
|
-
|
9
|
+
== Usage
|
10
10
|
|
11
|
-
jazor [options] [source] [
|
11
|
+
jazor [options] [source] [expression ...]
|
12
12
|
|
13
|
-
|
14
|
-
Jazor also accepts input from STDIN.
|
15
|
-
- The *slice* option refers to the piece of JSON data you're interested in. This
|
16
|
-
is usually just the name of an attribute, but you can also access it like a
|
17
|
-
hash (e.g. json['<attribute>']), or if the JSON object is an array, like an
|
18
|
-
array (e.g. json[0], etc.).
|
13
|
+
=== Options
|
19
14
|
|
20
|
-
|
15
|
+
See the *--help* command line option.
|
21
16
|
|
22
|
-
|
17
|
+
=== Sources
|
23
18
|
|
24
|
-
|
19
|
+
The *source* argument refers to a file, URL or string containing a JSON object.
|
20
|
+
Since attempts to implement a full-featured HTTP client within Jazor would have
|
21
|
+
been futile, Jazor also accepts input from STDIN. This means if you ever need
|
22
|
+
to use an advanced HTTP option that Jazor doesn't implement, you can always use
|
23
|
+
a *real* HTTP client (e.g. {cURL}[http://curl.haxx.se/]) and simply pipe the
|
24
|
+
output to Jazor.
|
25
25
|
|
26
|
-
|
26
|
+
=== Expressions
|
27
27
|
|
28
|
-
|
28
|
+
Jazor accepts one or more Ruby *expressions* which are simply eval'ed within
|
29
|
+
the context of your JSON object. After Jazor parses your JSON input into native
|
30
|
+
Ruby data types (Hash, Array, etc.), these expressions are used to slice and
|
31
|
+
dice the data any way you want. The results will be "pretty printed" to STDOUT.
|
29
32
|
|
30
|
-
|
33
|
+
Note that hash keys can be accessed via standard Ruby (e.g. foo['bar'],
|
34
|
+
foo.fetch('bar'), etc.) or Javascript (e.g. foo.bar) syntax.
|
31
35
|
|
32
|
-
|
36
|
+
=== Expression Testing
|
33
37
|
|
34
|
-
|
38
|
+
Expression testing (*--test*) allows you to test the "truthiness" of your
|
39
|
+
expression results. If any expression returns a "falsy" value, Jazor will exit
|
40
|
+
with a non-zero return code. This is useful for calling Jazor from within shell
|
41
|
+
scripts.
|
35
42
|
|
36
|
-
|
43
|
+
== Examples
|
44
|
+
|
45
|
+
$ jazor http://github.com/api/v2/json/commits/list/mconigliaro/jazor/master commits.count
|
46
|
+
16
|
47
|
+
|
48
|
+
$ curl --silent http://github.com/api/v2/json/commits/list/mconigliaro/jazor/master | jazor commits.last.message
|
49
|
+
initial commit
|
50
|
+
|
51
|
+
$ jazor '{ "foo": "abc", "bar": [1, 2, 3] }' 'foo.split(//)'
|
52
|
+
["a", "b", "c"]
|
53
|
+
|
54
|
+
$ jazor '{ "foo": "abc", "bar": [1, 2, 3] }' 'bar.inject { |memo,obj| memo + obj }'
|
55
|
+
6
|
56
|
+
|
57
|
+
$ jazor '[1, 2, 3, 4, 5]' 'select { |obj| obj.even? }'
|
58
|
+
[2, 4]
|
59
|
+
|
60
|
+
$ jazor '[1, 2, 3, 4, 5]' 'select { |obj| obj.odd? }'
|
61
|
+
[1, 3, 5]
|
62
|
+
|
63
|
+
$ jazor '["a", "b", "c"]' self.count
|
64
|
+
3
|
65
|
+
|
66
|
+
== Author
|
67
|
+
|
68
|
+
Michael T. Conigliaro <mike [at] conigliaro [dot] org>
|
data/Rakefile
CHANGED
data/bin/jazor
CHANGED
@@ -7,98 +7,90 @@ $:.push File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
7
7
|
require 'jazor'
|
8
8
|
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
opts.on('-d', '--data DATA', 'Data sent with REST request (e.g. <name>=<value>)') do |opt|
|
27
|
-
key, value = opt.split('=')
|
28
|
-
options[:rest_data][key] = value
|
29
|
-
end
|
10
|
+
options = {
|
11
|
+
:test => false,
|
12
|
+
:rest_data => {},
|
13
|
+
:rest_headers => {},
|
14
|
+
:rest_request => 'GET'
|
15
|
+
}
|
16
|
+
|
17
|
+
OptionParser.new do |opts|
|
18
|
+
opts.version = Jazor::VERSION
|
19
|
+
opts.banner = "Usage: #{$0} [options] [source] [expression ...]\n\n"
|
20
|
+
|
21
|
+
opts.on('-d', '--data NAME=VALUE', 'Data sent with REST request') do |opt|
|
22
|
+
key, value = opt.split('=')
|
23
|
+
options[:rest_data][key.strip] = value.strip
|
24
|
+
end
|
30
25
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
26
|
+
opts.on('-H', '--header NAME:VALUE', 'Header sent with REST request') do |opt|
|
27
|
+
key, value = opt.split(':')
|
28
|
+
options[:rest_headers][key.strip] = value.strip
|
29
|
+
end
|
35
30
|
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
opts.on('-t', '--test', 'Enable expression testing') do |opt|
|
32
|
+
options[:test] = true
|
33
|
+
end
|
39
34
|
|
40
|
-
|
41
|
-
|
42
|
-
|
35
|
+
opts.on('-v', '--verbose', 'Enable verbose logging') do |opt|
|
36
|
+
Jazor::LOG.level = Logger::DEBUG
|
37
|
+
end
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
opts.on('-X', '--request REQUEST', 'REST request method (default: %s)' % options[:rest_request]) do |opt|
|
40
|
+
options[:rest_request] = opt
|
41
|
+
end
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
43
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
44
|
+
puts opts.help
|
45
|
+
exit
|
46
|
+
end
|
52
47
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
48
|
+
opts.on_tail('--version', 'Show version') do
|
49
|
+
puts "%s %s\n%s <%s>\n%s" % [opts.program_name, opts.version, Jazor::AUTHOR, Jazor::AUTHOR_EMAIL, Jazor::URL]
|
50
|
+
exit
|
51
|
+
end
|
57
52
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
LOG.debug('Reading JSON from ARGV: %s' % ARGV[0])
|
77
|
-
Jazor.new(ARGV.shift)
|
78
|
-
end
|
53
|
+
if ARGV.length == 0 && STDIN.fcntl(Fcntl::F_GETFL, 0) != 0
|
54
|
+
puts opts.help
|
55
|
+
exit(1)
|
56
|
+
end
|
57
|
+
end.parse!
|
58
|
+
|
59
|
+
begin
|
60
|
+
|
61
|
+
obj = if STDIN.fcntl(Fcntl::F_GETFL, 0) == 0
|
62
|
+
Jazor::LOG.debug('Reading JSON from STDIN')
|
63
|
+
Jazor::parse(STDIN.read)
|
64
|
+
elsif !ARGV[0].nil?
|
65
|
+
if ARGV[0] =~ URI::regexp
|
66
|
+
Jazor::LOG.debug("Reading JSON from URI: #{ARGV[0]}")
|
67
|
+
Jazor::parse(Jazor::RestClient.send(options[:rest_request], ARGV.shift, options[:rest_headers], options[:rest_data]).body)
|
68
|
+
elsif File.readable?(ARGV[0])
|
69
|
+
Jazor::LOG.debug("Reading JSON from file: #{ARGV[0]}")
|
70
|
+
Jazor::parse(IO.read(ARGV.shift))
|
79
71
|
else
|
80
|
-
Jazor.
|
81
|
-
|
82
|
-
|
83
|
-
slices = ARGV.length > 0 ? ARGV[0..ARGV.length] : [nil]
|
84
|
-
slices.each do |slice|
|
85
|
-
obj = jazor.get_slice(slice)
|
86
|
-
puts [Hash, Array].include?(obj.class) ? JSON.pretty_generate(obj) : obj
|
72
|
+
Jazor::LOG.debug("Reading JSON from ARGV: #{ARGV[0]}")
|
73
|
+
Jazor::parse(ARGV.shift)
|
87
74
|
end
|
75
|
+
end
|
88
76
|
|
89
|
-
|
90
|
-
|
91
|
-
|
77
|
+
(ARGV.length > 0 ? ARGV[0..ARGV.length] : [nil]).each do |expression|
|
78
|
+
result = expression.nil? ? obj : obj.instance_eval(expression)
|
79
|
+
if options[:test]
|
80
|
+
if result
|
81
|
+
Jazor::LOG.info("Test passed: #{expression} => #{[Hash, Array].include?(result.class) ? result.to_json : result}")
|
92
82
|
else
|
93
|
-
LOG.error("Test failed:
|
83
|
+
Jazor::LOG.error("Test failed: #{expression} => #{[Hash, Array].include?(result.class) ? result.to_json : result}")
|
94
84
|
exit(1)
|
95
85
|
end
|
86
|
+
else
|
87
|
+
puts [Hash, Array].include?(result.class) ? JSON.pretty_generate(result) : result
|
96
88
|
end
|
97
|
-
|
98
|
-
rescue StandardError => e
|
99
|
-
LOG.error(e.message)
|
100
|
-
puts e.backtrace if LOG.level == Logger::DEBUG
|
101
|
-
exit(1)
|
102
89
|
end
|
103
90
|
|
91
|
+
rescue StandardError => e
|
92
|
+
puts e
|
93
|
+
puts e.backtrace
|
94
|
+
Jazor::LOG.error(e.message)
|
95
|
+
exit(1)
|
104
96
|
end
|
data/lib/jazor.rb
CHANGED
@@ -1,63 +1,52 @@
|
|
1
|
-
require 'json'
|
2
1
|
require 'logger'
|
3
2
|
require 'net/http'
|
4
3
|
require 'pp'
|
5
4
|
require 'uri'
|
6
5
|
|
6
|
+
require 'rubygems'
|
7
|
+
require 'json'
|
8
|
+
|
7
9
|
|
8
10
|
module Jazor
|
9
11
|
|
10
|
-
NAME
|
11
|
-
|
12
|
-
|
13
|
-
AUTHOR = 'Michael T. Conigliaro'
|
12
|
+
NAME = 'jazor'
|
13
|
+
VERSION = '0.1.0'
|
14
|
+
AUTHOR = 'Michael T. Conigliaro'
|
14
15
|
AUTHOR_EMAIL = 'mike [at] conigliaro [dot] org'
|
15
|
-
|
16
|
+
DESCRIPTION = 'Jazor is a simple command line JSON parsing tool'
|
17
|
+
URL = 'http://github.com/mconigliaro/jazor'
|
16
18
|
|
17
19
|
LOG = Logger.new(STDOUT)
|
18
20
|
LOG.level = Logger::INFO
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def get_slice(slice=nil)
|
29
|
-
if slice.nil?
|
30
|
-
@json
|
31
|
-
elsif slice =~ /^json/
|
32
|
-
instance_eval(slice)
|
33
|
-
else
|
34
|
-
@json.instance_eval(slice)
|
22
|
+
def self.parse(input=nil)
|
23
|
+
# FIXME: https://github.com/flori/json/issues/16
|
24
|
+
obj = (JSON.parse(input) rescue false) || (Integer(input) rescue false) || (Float(input) rescue false) || String(input)
|
25
|
+
if obj.is_a?(String)
|
26
|
+
if obj == 'true'
|
27
|
+
obj = true
|
28
|
+
elsif obj == 'false'
|
29
|
+
obj = false
|
35
30
|
end
|
36
31
|
end
|
37
|
-
|
38
|
-
def method_missing(name)
|
39
|
-
get_slice(name.to_s)
|
40
|
-
end
|
41
|
-
|
32
|
+
obj
|
42
33
|
end
|
43
34
|
|
44
35
|
class RestClient
|
45
|
-
|
46
36
|
def self.method_missing(method, uri, headers={}, data={})
|
47
37
|
uri_parsed = URI.parse(uri)
|
48
38
|
http = Net::HTTP.new(uri_parsed.host, port=uri_parsed.port)
|
49
|
-
request_uri = uri_parsed.query ? uri_parsed.path
|
39
|
+
request_uri = uri_parsed.query ? "#{uri_parsed.path}?#{uri_parsed.query}" : uri_parsed.path
|
50
40
|
request = Net::HTTP.const_get(method.to_s.capitalize).new(request_uri)
|
51
41
|
headers.each { |k,v| request.add_field(k, v) }
|
52
42
|
request.set_form_data(data)
|
53
43
|
|
54
|
-
LOG.debug(
|
44
|
+
LOG.debug("#{self} #{method.to_s.upcase}: uri=#{File.join(uri_parsed.host, request_uri)} headers=#{headers.to_json} data=#{data.to_json}")
|
55
45
|
response = http.request(request)
|
56
|
-
LOG.debug(
|
46
|
+
LOG.debug("#{self} result: code=#{response.code} body=%s" % response.body.gsub("\r", ' ').gsub("\n", ' '))
|
57
47
|
|
58
48
|
response
|
59
49
|
end
|
60
|
-
|
61
50
|
end
|
62
51
|
|
63
52
|
end
|
data/test/test_jazor.rb
CHANGED
@@ -1,73 +1,57 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
|
3
|
-
$:.push File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
require 'jazor'
|
5
|
-
|
6
|
-
|
7
3
|
class TestExecute < Test::Unit::TestCase
|
8
4
|
|
9
5
|
def setup
|
6
|
+
require 'jazor'
|
7
|
+
|
10
8
|
@init_json = IO.read(File.expand_path(File.join(File.dirname(__FILE__), 'test.json')))
|
11
9
|
@init_hash = JSON.parse(@init_json)
|
12
|
-
@jazor = Jazor::Jazor.new(@init_json)
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_init
|
16
|
-
Jazor::Jazor.new(@init_json)
|
17
10
|
end
|
18
11
|
|
19
12
|
def test_root_object
|
20
|
-
assert @
|
21
|
-
assert @
|
13
|
+
assert Jazor::parse(@init_json).is_a?(Hash)
|
14
|
+
assert Jazor::parse(@init_json) == @init_hash
|
22
15
|
end
|
23
16
|
|
24
17
|
def test_values
|
25
|
-
assert @
|
26
|
-
assert @
|
27
|
-
assert @
|
18
|
+
assert Jazor::parse(@init_json).value0.is_a?(String)
|
19
|
+
assert Jazor::parse(@init_json).value1.is_a?(Integer)
|
20
|
+
assert Jazor::parse(@init_json).value2.is_a?(Float)
|
28
21
|
(0..2).each do |i|
|
29
|
-
assert @
|
22
|
+
assert Jazor::parse(@init_json).send("value#{i}") == @init_hash["value#{i}"]
|
30
23
|
end
|
31
24
|
end
|
32
25
|
|
33
26
|
def test_array_of_values
|
34
|
-
assert @
|
35
|
-
assert @
|
36
|
-
(0
|
37
|
-
assert @
|
38
|
-
assert @
|
27
|
+
assert Jazor::parse(@init_json).array_of_values.is_a?(Array)
|
28
|
+
assert Jazor::parse(@init_json).array_of_values == @init_hash['array_of_values']
|
29
|
+
(0...Jazor::parse(@init_json).array_of_values.length).each do |i|
|
30
|
+
assert Jazor::parse(@init_json).array_of_values[i].is_a?(Integer)
|
31
|
+
assert Jazor::parse(@init_json).array_of_values[i] == @init_hash["array_of_values"][i]
|
39
32
|
end
|
40
33
|
end
|
41
34
|
|
42
35
|
def test_array_of_arrays
|
43
|
-
assert @
|
44
|
-
(0
|
45
|
-
assert @
|
46
|
-
assert @
|
47
|
-
(0
|
48
|
-
assert @
|
49
|
-
assert @
|
36
|
+
assert Jazor::parse(@init_json).array_of_arrays.is_a?(Array)
|
37
|
+
(0...Jazor::parse(@init_json).array_of_arrays.length).each do |i|
|
38
|
+
assert Jazor::parse(@init_json).array_of_arrays[i].is_a?(Array)
|
39
|
+
assert Jazor::parse(@init_json).array_of_arrays[i] == @init_hash['array_of_arrays'][i]
|
40
|
+
(0...Jazor::parse(@init_json).array_of_arrays[i].length).each do |j|
|
41
|
+
assert Jazor::parse(@init_json).array_of_arrays[i][j].is_a?(Integer)
|
42
|
+
assert Jazor::parse(@init_json).array_of_arrays[i][j] == @init_hash['array_of_arrays'][i][j]
|
50
43
|
end
|
51
44
|
end
|
52
45
|
end
|
53
46
|
|
54
47
|
def test_array_of_objects
|
55
|
-
assert @
|
56
|
-
(0
|
57
|
-
assert @
|
58
|
-
assert @
|
59
|
-
assert @
|
60
|
-
assert @
|
48
|
+
assert Jazor::parse(@init_json).array_of_objects.is_a?(Array)
|
49
|
+
(0...Jazor::parse(@init_json).array_of_objects.length).each do |i|
|
50
|
+
assert Jazor::parse(@init_json).array_of_objects[i].is_a?(Hash)
|
51
|
+
assert Jazor::parse(@init_json).array_of_objects[i] == @init_hash['array_of_objects'][i]
|
52
|
+
assert Jazor::parse(@init_json).array_of_objects[i].send("object#{i}").is_a?(Integer)
|
53
|
+
assert Jazor::parse(@init_json).array_of_objects[i].send("object#{i}") == @init_hash['array_of_objects'][i]["object#{i}"]
|
61
54
|
end
|
62
55
|
end
|
63
56
|
|
64
|
-
def test_get_nil_slice_returns_complete_object()
|
65
|
-
assert @jazor.get_slice.is_a?(Hash)
|
66
|
-
assert @jazor.get_slice == @init_hash
|
67
|
-
end
|
68
|
-
|
69
|
-
def test_get_slice()
|
70
|
-
assert @jazor.get_slice('nested_object') == @init_hash['nested_object']
|
71
|
-
end
|
72
|
-
|
73
57
|
end
|
data/test/test_jazor_bin.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
|
3
|
-
$:.push File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
require 'jazor'
|
5
|
-
|
6
|
-
|
7
3
|
class TestExecute < Test::Unit::TestCase
|
8
4
|
|
9
5
|
def setup
|
10
|
-
|
11
|
-
|
6
|
+
require 'jazor'
|
7
|
+
|
8
|
+
@init_url = 'http://github.com/api/v2/json/commits/list/mconigliaro/jazor/master'
|
9
|
+
@init_file = File.expand_path(File.join(File.dirname(__FILE__), 'test.json'))
|
12
10
|
@init_string = IO.read(@init_file)
|
13
|
-
@jazor_bin
|
11
|
+
@jazor_bin = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'jazor'))
|
14
12
|
end
|
15
13
|
|
16
14
|
def test_without_paramaters
|
@@ -33,20 +31,16 @@ class TestExecute < Test::Unit::TestCase
|
|
33
31
|
assert JSON.parse(`#{@jazor_bin} '#{@init_string}'`).is_a?(Hash)
|
34
32
|
end
|
35
33
|
|
36
|
-
def
|
37
|
-
assert `#{@jazor_bin}
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_json_from_file_with_slice
|
41
|
-
assert eval(`#{@jazor_bin} #{@init_file} value1`).is_a?(Numeric)
|
34
|
+
def test_json_from_file_with_expression
|
35
|
+
assert eval(`#{@jazor_bin} #{@init_file} value1`).is_a?(Integer)
|
42
36
|
assert JSON.parse(`#{@jazor_bin} #{@init_file} nested_object`).is_a?(Hash)
|
43
37
|
assert JSON.parse(`#{@jazor_bin} #{@init_file} nested_object.array_of_values`).is_a?(Array)
|
44
38
|
assert eval(`#{@jazor_bin} #{@init_file} dontexist`).nil?
|
45
39
|
end
|
46
40
|
|
47
41
|
def test_json_from_file_with_tests
|
48
|
-
assert `#{@jazor_bin} #{@init_file}
|
49
|
-
assert `#{@jazor_bin} #{@init_file}
|
42
|
+
assert `#{@jazor_bin} -t #{@init_file} 'value1==123'` =~ /passed/
|
43
|
+
assert `#{@jazor_bin} -t #{@init_file} 'value1==1234'` =~ /failed/
|
50
44
|
end
|
51
45
|
|
52
46
|
end
|