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.
@@ -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