jazor 0.0.4 → 0.1.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/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
|