jazor 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- === General usage
9
+ == Usage
10
10
 
11
- jazor [options] [source] [slice]
11
+ jazor [options] [source] [expression ...]
12
12
 
13
- - The *source* option refers to a file, URL or string containing JSON data.
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
- === Examples
15
+ See the *--help* command line option.
21
16
 
22
- ==== Reading the entire JSON object from a URL
17
+ === Sources
23
18
 
24
- jazor 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=jazor'
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
- ==== Reading a JSON "slice" from a URL
26
+ === Expressions
27
27
 
28
- jazor 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=jazor' responseData.results[0].title
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
- ==== Testing the value of a JSON "slice"
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
- jazor 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=jazor' -t 'responseData.results.is_a?(Array)'
36
+ === Expression Testing
33
37
 
34
- === Help
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
- jazor --help
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
@@ -2,7 +2,7 @@ task :default => [:test]
2
2
 
3
3
  task :test do
4
4
  Dir[File.expand_path(File.join(File.dirname(__FILE__), 'test', 'test_*'))].each do |test|
5
- system "ruby #{test}"
5
+ system "ruby -I #{File.expand_path(File.join(File.dirname(__FILE__), 'lib'))} #{test}"
6
6
  end
7
7
  end
8
8
 
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
- module Jazor
11
-
12
- begin
13
-
14
- options = {
15
- :tests => [],
16
- :rest_data => {},
17
- :rest_headers => {},
18
- :rest_request => 'GET'
19
- }
20
-
21
- OptionParser.new do |opts|
22
- opts.version = VERSION
23
- opts.banner = "Usage: #{$0} [options] [source] [slice]\n" \
24
- + "Example: #{$0} http://example.com/example.json a.b.c\n\n"
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
- opts.on('-H', '--header HEADER', 'Header sent with REST request (e.g. <name>:<value>)') do |opt|
32
- key, value = opt.split(':')
33
- options[:rest_headers][key.strip] = value.strip
34
- end
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
- opts.on('-t', '--test EXPRESSION', 'Test expression (e.g. "a.b.c == 123")') do |opt|
37
- options[:tests] << opt
38
- end
31
+ opts.on('-t', '--test', 'Enable expression testing') do |opt|
32
+ options[:test] = true
33
+ end
39
34
 
40
- opts.on('-v', '--verbose', 'Turn on verbose logging') do |opt|
41
- LOG.level = Logger::DEBUG
42
- end
35
+ opts.on('-v', '--verbose', 'Enable verbose logging') do |opt|
36
+ Jazor::LOG.level = Logger::DEBUG
37
+ end
43
38
 
44
- opts.on('-X', '--request REQUEST', 'REST request method (default: %s)' % options[:rest_request]) do |opt|
45
- options[:rest_request] = opt
46
- end
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
- opts.on_tail('-h', '--help', 'Show this message') do
49
- puts opts.help
50
- exit
51
- end
43
+ opts.on_tail('-h', '--help', 'Show this message') do
44
+ puts opts.help
45
+ exit
46
+ end
52
47
 
53
- opts.on_tail('--version', 'Show version') do
54
- puts "%s %s\n%s <%s>\n%s" % [opts.program_name, opts.version, AUTHOR, AUTHOR_EMAIL, URL]
55
- exit
56
- end
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
- if ARGV.length == 0 && STDIN.fcntl(Fcntl::F_GETFL, 0) != 0
59
- puts opts.help
60
- exit(1)
61
- end
62
- end.parse!
63
-
64
- jazor = if STDIN.fcntl(Fcntl::F_GETFL, 0) == 0
65
- json = STDIN.read
66
- LOG.debug('Reading JSON from STDIN')
67
- Jazor.new(json)
68
- elsif !ARGV[0].nil?
69
- if ARGV[0] =~ URI::regexp
70
- LOG.debug('Reading JSON from URI: %s' % ARGV[0])
71
- Jazor.new(RestClient.send(options[:rest_request], ARGV.shift, options[:rest_headers], options[:rest_data]).body)
72
- elsif File.readable?(ARGV[0])
73
- LOG.debug('Reading JSON from file: %s' % ARGV[0])
74
- Jazor.new(IO.read(ARGV.shift))
75
- else
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.new(nil)
81
- end
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
- options[:tests].each do |test|
90
- if jazor.instance_eval(test)
91
- LOG.info("Test passed: %s" % test)
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: %s" % test)
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
@@ -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 = 'jazor'
11
- VERSION_INFO = ['0', '0', '4']
12
- VERSION = VERSION_INFO.join('.')
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
- URL = 'http://github.com/mconigliaro/jazor'
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
- class Jazor
21
-
22
- attr_reader :json
23
-
24
- def initialize(source=nil)
25
- @json = source.nil? ? nil : JSON.parse(source)
26
- end
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 + '?' + 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('%s %s: uri=%s headers=%s data=%s' % [self, method.to_s.upcase, File.join(uri_parsed.host, request_uri), headers.to_json, data.to_json])
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('%s result: code=%d body=%s' % [self, response.code, response.body.gsub("\r", ' ').gsub("\n", ' ')])
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
@@ -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 @jazor.json.is_a?(Hash)
21
- assert @jazor.json == @init_hash
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 @jazor.json.value0.is_a?(String)
26
- assert @jazor.json.value1.is_a?(Integer)
27
- assert @jazor.json.value2.is_a?(Float)
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 @jazor.json.send("value#{i}") == @init_hash["value#{i}"]
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 @jazor.json.array_of_values.is_a?(Array)
35
- assert @jazor.json.array_of_values == @init_hash['array_of_values']
36
- (0...@jazor.json.array_of_values.length).each do |i|
37
- assert @jazor.json.array_of_values[i].is_a?(Integer)
38
- assert @jazor.json.array_of_values[i] == @init_hash["array_of_values"][i]
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 @jazor.json.array_of_arrays.is_a?(Array)
44
- (0...@jazor.json.array_of_arrays.length).each do |i|
45
- assert @jazor.json.array_of_arrays[i].is_a?(Array)
46
- assert @jazor.json.array_of_arrays[i] == @init_hash['array_of_arrays'][i]
47
- (0...@jazor.json.array_of_arrays[i].length).each do |j|
48
- assert @jazor.json.array_of_arrays[i][j].is_a?(Integer)
49
- assert @jazor.json.array_of_arrays[i][j] == @init_hash['array_of_arrays'][i][j]
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 @jazor.json.array_of_objects.is_a?(Array)
56
- (0...@jazor.json.array_of_objects.length).each do |i|
57
- assert @jazor.json.array_of_objects[i].is_a?(Hash)
58
- assert @jazor.json.array_of_objects[i] == @init_hash['array_of_objects'][i]
59
- assert @jazor.json.array_of_objects[i].send("object#{i}").is_a?(Integer)
60
- assert @jazor.json.array_of_objects[i].send("object#{i}") == @init_hash['array_of_objects'][i]["object#{i}"]
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
@@ -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
- @init_url = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=jazor'
11
- @init_file = File.expand_path(File.join(File.dirname(__FILE__), 'test.json'))
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 = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'jazor'))
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 test_bad_json
37
- assert `#{@jazor_bin} '{"a":}'` =~ /ERROR/
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} -t 'value1==123'` =~ /passed/
49
- assert `#{@jazor_bin} #{@init_file} -t 'value1==1234'` =~ /failed/
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