json_cli 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 29474e98dfc3d7fe8e3e3a3cd2237d3f3b2f7732
4
- data.tar.gz: 890920b7bae3ac0a7c6c2bef4a1d134f7b736c79
3
+ metadata.gz: 953941cd39ba18dc5b2d5a24e36c94ba9a6a2455
4
+ data.tar.gz: 29bb2592deb3b6a7b39b2fa35123f0f9fc914733
5
5
  SHA512:
6
- metadata.gz: df304ae76aece5d5831d54ceb15c0d104096f88589f84c2d7beb8d5e804964e04f97c4804f40359de2c18bb2cd7a50312cee33a400d5632c8690a183d3732164
7
- data.tar.gz: 6b88ca9485f19b68eb8352d347a1fc9999c1f42afe5ede5885ad2f3823b99717a92cef9e0c082f1d44c19d40e7555c7caa42d3b223bce04414f7c8b2487116ca
6
+ metadata.gz: ccc9bc8dee9b5d78958e8cc86c76e2913aebfaa53953936e8ba09c12a04aed23015d3b34872c2bd5b87e1022ec2e4e1f8fd9ec6c673880307da6fbd31116bf6c
7
+ data.tar.gz: f725d780cd019ad2dd5d021ef5764d8d7f637cc0037538d16d11f653725454c1f3ff08c1ca36a70d80d0a56f42bbe9401f0d474869c7d4beff2c037fe20a4e10
@@ -1,60 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'json_cli'
3
- require 'thor'
4
-
5
- module JsonCli
6
- class CLI < Thor
7
- desc "left_join RIGHT_FILE [LEFT_FILE]", "left outer join of json files"
8
- option :key, :required => true, :aliases => :k
9
- def left_join(right_file, left_file = nil)
10
- left_io = left_file.nil? ? STDIN : File.open(left_file, 'r')
11
- right_io = File.open(right_file, 'r')
12
- JsonCli::JoinJson.left_join(left_io, right_io, options[:key])
13
- left_io.close
14
- right_io.close
15
- end
16
-
17
- desc "right_join RIGHT_FILE [LEFT_FILE]", "right outer join of json files"
18
- option :key, :required => true, :aliases => :k
19
- def right_join(right_file, left_file = nil)
20
- left_io = left_file.nil? ? STDIN : File.open(left_file, 'r')
21
- right_io = File.open(right_file, 'r')
22
- JsonCli::JoinJson.right_join(left_io, right_io, options[:key])
23
- left_io.close
24
- right_io.close
25
- end
26
-
27
- desc "inner_join RIGHT_FILE [LEFT_FILE]", "inner join of json files"
28
- option :key, :required => true, :aliases => :k
29
- def inner_join(right_file, left_file = nil)
30
- left_io = left_file.nil? ? STDIN : File.open(left_file, 'r')
31
- right_io = File.open(right_file, 'r')
32
- JsonCli::JoinJson.inner_join(left_io, right_io, options[:key])
33
- left_io.close
34
- right_io.close
35
- end
36
-
37
- desc "unwind_array [JSON_FILE]", "unwind array of json file"
38
- option :key, :required => true, :aliases => :k
39
- def unwind_array(json_file = nil)
40
- io = json_file.nil? ? STDIN : File.open(json_file, 'r')
41
- JsonCli::UnwindJson.unwind_array(io, options[:key])
42
- io.close
43
- end
44
-
45
- desc "unwind_hash [JSON_FILE]", "unwind hash of json file"
46
- option :key, :required => true, :aliases => :k
47
- option :flatten, :type => :boolean, :aliases => :f
48
- option :key_label
49
- option :value_label
50
- def unwind_hash(json_file = nil)
51
- io = json_file.nil? ? STDIN : File.open(json_file, 'r')
52
- opt = {}
53
- options.select{|k,v| k != :key}.each{|k,v| opt[k.to_sym] = v}
54
- JsonCli::UnwindJson.unwind_hash(io, options[:key], opt)
55
- io.close
56
- end
57
- end
58
- end
59
2
 
3
+ require 'json_cli'
60
4
  JsonCli::CLI.start(ARGV)
@@ -1,7 +1,9 @@
1
1
  require 'json_cli/version'
2
+ require 'json_cli/cli'
3
+ require 'json_cli/command/base'
4
+ require 'json_cli/command/join'
5
+ require 'json_cli/command/unwind'
2
6
 
3
7
  # Base module
4
8
  module JsonCli
5
- require 'json_cli/join'
6
- require 'json_cli/unwind'
7
9
  end
@@ -0,0 +1,70 @@
1
+ require 'thor'
2
+
3
+ module JsonCli
4
+ # CLI class
5
+ class CLI < Thor
6
+ class_option :output, aliases: :o, default: '/dev/stdout'
7
+
8
+ desc 'left_join', 'left outer join of json files'
9
+ option :join_key, required: true, aliases: :k
10
+ option :join_file, required: true, aliases: :j
11
+ option :base_file, aliases: :b, default: '/dev/stdin'
12
+ def left_join
13
+ join_common(__method__)
14
+ end
15
+
16
+ desc 'right_join', 'right outer join of json files'
17
+ option :join_key, required: true, aliases: :k
18
+ option :join_file, required: true, aliases: :j
19
+ option :base_file, aliases: :b, default: '/dev/stdin'
20
+ def right_join
21
+ join_common(__method__)
22
+ end
23
+
24
+ desc 'inner_join', 'inner join of json files'
25
+ option :join_key, required: true, aliases: :k
26
+ option :join_file, required: true, aliases: :j
27
+ option :base_file, aliases: :b, default: '/dev/stdin'
28
+ def inner_join
29
+ join_common(__method__)
30
+ end
31
+
32
+ desc 'unwind_array [JSON_FILE]', 'unwind array of json file'
33
+ option :unwind_key, required: true, aliases: :k
34
+ def unwind_array(json_file = '/dev/stdin')
35
+ unwind_common(__method__, json_file)
36
+ end
37
+
38
+ desc 'unwind_hash [JSON_FILE]', 'unwind hash of json file'
39
+ option :unwind_key, required: true, aliases: :k
40
+ option :flatten, type: :boolean, aliases: :f
41
+ option :key_label
42
+ option :value_label
43
+ def unwind_hash(json_file = '/dev/stdin')
44
+ unwind_common(__method__, json_file)
45
+ end
46
+
47
+ private
48
+
49
+ def join_common(command)
50
+ left_io = File.open(options[:base_file], 'r')
51
+ right_io = File.open(options[:join_file], 'r')
52
+ JsonCli::Command::Join.new(left_io, right_io, opts).send(command)
53
+ [left_io, right_io, opts[:out]].each(&:close)
54
+ end
55
+
56
+ def unwind_common(command, json_file)
57
+ io = File.open(json_file, 'r')
58
+ JsonCli::Command::Unwind.new(io, opts).send(command)
59
+ [io, opts[:out]].each(&:close)
60
+ end
61
+
62
+ def opts
63
+ @opts ||= { out: File.open(options[:output], 'w') }
64
+ .merge(options.select { |key, _| key != :output })
65
+ .each_with_object({}) do |(key, val), hash|
66
+ hash[key.to_sym] = val
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,12 @@
1
+ # -*- mode: ruby; coding: utf-8 -*-
2
+
3
+ module JsonCli
4
+ module Command
5
+ # Base command class
6
+ class Base
7
+ def initialize(options)
8
+ @output = options[:out]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ # -*- mode: ruby; coding: utf-8 -*-
2
+ require 'multi_json'
3
+
4
+ module JsonCli
5
+ module Command
6
+ # Join JSON class
7
+ class Join < Base
8
+ def initialize(left_io, right_io, options)
9
+ super(options)
10
+ @left_io = left_io
11
+ @right_io = right_io
12
+ @join_key = options[:join_key]
13
+ end
14
+
15
+ def left_join
16
+ join(@left_io, @right_io)
17
+ end
18
+
19
+ def right_join
20
+ join(@right_io, @left_io)
21
+ end
22
+
23
+ def inner_join
24
+ right = io2hash(@right_io)
25
+ @left_io.each do |line|
26
+ obj = MultiJson.load(line.chomp)
27
+ next if !obj.key?(@join_key) ||
28
+ !right.key?((jk_val = obj[@join_key]))
29
+ @output.puts MultiJson.dump(obj.merge(right[jk_val]))
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def io2hash(io)
36
+ io.each_with_object({}) do |line, hash|
37
+ obj = MultiJson.load(line.chomp)
38
+ hash[obj.delete(@join_key)] = obj if obj.key?(@join_key)
39
+ end
40
+ end
41
+
42
+ def join(left_io, right_io)
43
+ right = io2hash(right_io)
44
+ left_io.each do |line|
45
+ obj = MultiJson.load(line.chomp)
46
+ obj.merge!(right[obj[@join_key]] || {}) if obj.key?(@join_key)
47
+ @output.puts MultiJson.dump(obj)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,68 @@
1
+ # -*- mode: ruby; coding: utf-8 -*-
2
+ require 'multi_json'
3
+
4
+ module JsonCli
5
+ module Command
6
+ # Unwind JSON class
7
+ class Unwind < Base
8
+ def initialize(io, options)
9
+ super(options)
10
+ @io = io
11
+ @unwind_key = options[:unwind_key]
12
+ @flatten = options[:flatten]
13
+ @key_label = options[:key_label] || 'key'
14
+ @value_label = options[:value_label] || 'value'
15
+ end
16
+
17
+ def unwind_array
18
+ @io.each do |line|
19
+ obj = MultiJson.load(line.chomp)
20
+ if !obj.key?(@unwind_key) || !obj[@unwind_key].is_a?(Array)
21
+ @output.puts MultiJson.dump(obj)
22
+ else
23
+ unwind_array_obj(obj)
24
+ end
25
+ end
26
+ end
27
+
28
+ def unwind_hash
29
+ @io.each do |line|
30
+ obj = MultiJson.load(line.chomp)
31
+ if !obj.key?(@unwind_key) || !obj[@unwind_key].is_a?(Hash)
32
+ @output.puts MultiJson.dump(obj)
33
+ elsif @flatten
34
+ unwind_hash_obj_flatten(obj)
35
+ else
36
+ unwind_hash_obj(obj)
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def unwind_array_obj(obj)
44
+ obj[@unwind_key].each do |val|
45
+ @output.puts MultiJson.dump(obj.merge(@unwind_key => val))
46
+ end
47
+ end
48
+
49
+ def unwind_hash_obj(obj)
50
+ obj[@unwind_key].each do |key, val|
51
+ jj = obj.merge(@unwind_key => { key => val })
52
+ @output.puts MultiJson.dump(jj)
53
+ end
54
+ end
55
+
56
+ def unwind_hash_obj_flatten(obj)
57
+ base = obj.select { |key, _| key != @unwind_key }
58
+ obj[@unwind_key].each do |key, val|
59
+ jj = base.merge(
60
+ @key_label => key,
61
+ @value_label => val
62
+ )
63
+ @output.puts MultiJson.dump(jj)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,4 +1,4 @@
1
1
  # Base module
2
2
  module JsonCli
3
- VERSION = '0.0.1'
3
+ VERSION = '0.0.2'
4
4
  end
File without changes
@@ -0,0 +1,2 @@
1
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"words":{"baseball":3,"soccer":2,"ichiro":1,"honda":2},"title":"A","authors":["alice"]}
2
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"words":{"sumo":3,"tennis":1,"japan":2},"title":"B","authors":["bob","john"]}
@@ -0,0 +1,3 @@
1
+ {"_id":"0001","title":"A","authors":["alice"],"timestamp":1385273700,"tags":["news","sports"],"words":{"baseball":3,"soccer":2,"ichiro":1,"honda":2}}
2
+ {"_id":"0002","title":"B","authors":["bob","john"],"timestamp":1385273730,"tags":["sports"],"words":{"sumo":3,"tennis":1,"japan":2}}
3
+ {"_id":"0004","title":"D","authors":["dave"]}
@@ -0,0 +1,4 @@
1
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"words":{"baseball":3,"soccer":2,"ichiro":1,"honda":2},"title":"A","authors":["alice"]}
2
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"words":{"sumo":3,"tennis":1,"japan":2},"title":"B","authors":["bob","john"]}
3
+ {"_id":"0003","timestamp":1385274100,"tags":["drama"],"words":{"furuhata":2,"ichiro":3}}
4
+ {"broken":"data"}
@@ -0,0 +1,5 @@
1
+ {"_id":"0001","timestamp":1385273700,"tags":"news","words":{"baseball":3,"soccer":2,"ichiro":1,"honda":2}}
2
+ {"_id":"0001","timestamp":1385273700,"tags":"sports","words":{"baseball":3,"soccer":2,"ichiro":1,"honda":2}}
3
+ {"_id":"0002","timestamp":1385273730,"tags":"sports","words":{"sumo":3,"tennis":1,"japan":2}}
4
+ {"_id":"0003","timestamp":1385274100,"tags":"drama","words":{"furuhata":2,"ichiro":3}}
5
+ {"broken":"data"}
@@ -0,0 +1,10 @@
1
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"words":{"baseball":3}}
2
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"words":{"soccer":2}}
3
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"words":{"ichiro":1}}
4
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"words":{"honda":2}}
5
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"words":{"sumo":3}}
6
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"words":{"tennis":1}}
7
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"words":{"japan":2}}
8
+ {"_id":"0003","timestamp":1385274100,"tags":["drama"],"words":{"furuhata":2}}
9
+ {"_id":"0003","timestamp":1385274100,"tags":["drama"],"words":{"ichiro":3}}
10
+ {"broken":"data"}
@@ -0,0 +1,10 @@
1
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"word":"baseball","count":3}
2
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"word":"soccer","count":2}
3
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"word":"ichiro","count":1}
4
+ {"_id":"0001","timestamp":1385273700,"tags":["news","sports"],"word":"honda","count":2}
5
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"word":"sumo","count":3}
6
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"word":"tennis","count":1}
7
+ {"_id":"0002","timestamp":1385273730,"tags":["sports"],"word":"japan","count":2}
8
+ {"_id":"0003","timestamp":1385274100,"tags":["drama"],"word":"furuhata","count":2}
9
+ {"_id":"0003","timestamp":1385274100,"tags":["drama"],"word":"ichiro","count":3}
10
+ {"broken":"data"}
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe JsonCli::CLI do
5
+ let(:cli) { described_class.new }
6
+
7
+ shared_examples_for 'cli' do
8
+ before do
9
+ @result_file = Tempfile.new(File.basename(__FILE__))
10
+ end
11
+
12
+ after do
13
+ @result_file.close!
14
+ end
15
+
16
+ it 'output expected JSON' do
17
+ args.map! { |f| File.join(FIXTURE_DIR, f) }
18
+ options.merge!(output: @result_file.path)
19
+ cli.invoke(command, args, options)
20
+ expect_file_path = File.join(FIXTURE_DIR, expect_file)
21
+ expect(File.read(@result_file.path)).to eq File.read(expect_file_path)
22
+ end
23
+ end
24
+
25
+ context 'join' do
26
+ let(:args) { [] }
27
+
28
+ describe '#left_join' do
29
+ let(:options) do
30
+ {
31
+ join_key: '_id',
32
+ base_file: File.join(FIXTURE_DIR, 'logfile.json'),
33
+ join_file: File.join(FIXTURE_DIR, 'attribute.json')
34
+ }
35
+ end
36
+ let(:command) { 'left_join' }
37
+ let(:expect_file) { 'join_logfile_attribute_id.json' }
38
+ it_behaves_like 'cli'
39
+ end
40
+
41
+ describe '#right_join' do
42
+ let(:options) do
43
+ {
44
+ join_key: '_id',
45
+ base_file: File.join(FIXTURE_DIR, 'attribute.json'),
46
+ join_file: File.join(FIXTURE_DIR, 'logfile.json')
47
+ }
48
+ end
49
+ let(:command) { 'right_join' }
50
+ let(:expect_file) { 'join_logfile_attribute_id.json' }
51
+ it_behaves_like 'cli'
52
+ end
53
+
54
+ describe '#inner_join' do
55
+ let(:options) do
56
+ {
57
+ join_key: '_id',
58
+ base_file: File.join(FIXTURE_DIR, 'logfile.json'),
59
+ join_file: File.join(FIXTURE_DIR, 'attribute.json')
60
+ }
61
+ end
62
+ let(:command) { 'inner_join' }
63
+ let(:expect_file) { 'inner_join_logfile_attribute_id.json' }
64
+ it_behaves_like 'cli'
65
+ end
66
+ end
67
+
68
+ context 'unwind' do
69
+ describe '#unwind_array' do
70
+ let(:args) { %w(logfile.json) }
71
+ let(:options) { { unwind_key: 'tags' } }
72
+ let(:command) { 'unwind_array' }
73
+ let(:expect_file) { 'unwind_logfile_tags.json' }
74
+ it_behaves_like 'cli'
75
+ end
76
+
77
+ describe '#unwind_hash' do
78
+ let(:args) { %w(logfile.json) }
79
+ let(:options) { { unwind_key: 'words' } }
80
+ let(:command) { 'unwind_hash' }
81
+ let(:expect_file) { 'unwind_logfile_words.json' }
82
+ it_behaves_like 'cli'
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe JsonCli::Command::Join do
4
+ shared_examples_for 'join json' do
5
+ it 'join json as expected' do
6
+ @left_io = File.open(File.join(FIXTURE_DIR, left_file))
7
+ @right_io = File.open(File.join(FIXTURE_DIR, right_file))
8
+ expect_file_path = File.join(FIXTURE_DIR, expect_file)
9
+ result = StringIO.new
10
+ opts = options.merge(out: result, join_key: join_key)
11
+ described_class.new(@left_io, @right_io, opts).send(command)
12
+ expect(result.string).to eq File.read(expect_file_path)
13
+ end
14
+
15
+ after do
16
+ @left_io.close if @left_io
17
+ @right_io.close if @right_io
18
+ end
19
+ end
20
+
21
+ describe '#left_join' do
22
+ let(:command) { 'left_join' }
23
+
24
+ context 'when normal file input' do
25
+ let(:left_file) { 'logfile.json' }
26
+ let(:right_file) { 'attribute.json' }
27
+ let(:join_key) { '_id' }
28
+ let(:options) { {} }
29
+ let(:expect_file) { 'join_logfile_attribute_id.json' }
30
+ it_behaves_like 'join json'
31
+ end
32
+
33
+ context 'when left is empty' do
34
+ let(:left_file) { 'empty.json' }
35
+ let(:right_file) { 'attribute.json' }
36
+ let(:join_key) { '_id' }
37
+ let(:options) { {} }
38
+ let(:expect_file) { 'empty.json' }
39
+ it_behaves_like 'join json'
40
+ end
41
+
42
+ context 'when right is empty' do
43
+ let(:left_file) { 'logfile.json' }
44
+ let(:right_file) { 'empty.json' }
45
+ let(:join_key) { '_id' }
46
+ let(:options) { {} }
47
+ let(:expect_file) { 'logfile.json' }
48
+ it_behaves_like 'join json'
49
+ end
50
+ end
51
+
52
+ describe '#right_join' do
53
+ let(:command) { 'right_join' }
54
+
55
+ context 'when normal file input' do
56
+ let(:left_file) { 'logfile.json' }
57
+ let(:right_file) { 'attribute.json' }
58
+ let(:join_key) { '_id' }
59
+ let(:options) { {} }
60
+ let(:expect_file) { 'join_attribute_logfile_id.json' }
61
+ it_behaves_like 'join json'
62
+ end
63
+ end
64
+
65
+ describe '#inner_join' do
66
+ let(:command) { 'inner_join' }
67
+
68
+ context 'when normal file input' do
69
+ let(:left_file) { 'logfile.json' }
70
+ let(:right_file) { 'attribute.json' }
71
+ let(:join_key) { '_id' }
72
+ let(:options) { {} }
73
+ let(:expect_file) { 'inner_join_logfile_attribute_id.json' }
74
+ it_behaves_like 'join json'
75
+ end
76
+
77
+ context 'when right is empty' do
78
+ let(:left_file) { 'logfile.json' }
79
+ let(:right_file) { 'empty.json' }
80
+ let(:join_key) { '_id' }
81
+ let(:options) { {} }
82
+ let(:expect_file) { 'empty.json' }
83
+ it_behaves_like 'join json'
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,53 @@
1
+ # -*- mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe JsonCli::Command::Unwind do
5
+ shared_examples_for 'unwind json' do
6
+ it 'unwind json as expected' do
7
+ @io = File.open(File.join(FIXTURE_DIR, input_file))
8
+ expect_file_path = File.join(FIXTURE_DIR, expect_file)
9
+ result = StringIO.new
10
+ opts = options.merge(out: result, unwind_key: unwind_key)
11
+ described_class.new(@io, opts).send(command)
12
+ expect(result.string).to eq File.read(expect_file_path)
13
+ end
14
+
15
+ after do
16
+ @io.close if @io
17
+ end
18
+ end
19
+
20
+ describe '#unwind_array' do
21
+ let(:command) { 'unwind_array' }
22
+
23
+ context 'when normal file input' do
24
+ let(:input_file) { 'logfile.json' }
25
+ let(:unwind_key) { 'tags' }
26
+ let(:options) { {} }
27
+ let(:expect_file) { 'unwind_logfile_tags.json' }
28
+ it_behaves_like 'unwind json'
29
+ end
30
+ end
31
+
32
+ describe '#unwind_hash' do
33
+ let(:command) { 'unwind_hash' }
34
+
35
+ context 'when normal file input' do
36
+ let(:input_file) { 'logfile.json' }
37
+ let(:unwind_key) { 'words' }
38
+ let(:options) { {} }
39
+ let(:expect_file) { 'unwind_logfile_words.json' }
40
+ it_behaves_like 'unwind json'
41
+ end
42
+
43
+ context 'when normal file input with flatten option' do
44
+ let(:input_file) { 'logfile.json' }
45
+ let(:unwind_key) { 'words' }
46
+ let(:expect_file) { 'unwind_logfile_words_flatten.json' }
47
+ let(:options) do
48
+ { flatten: true, key_label: 'word', value_label: 'count' }
49
+ end
50
+ it_behaves_like 'unwind json'
51
+ end
52
+ end
53
+ end
@@ -3,3 +3,5 @@ Coveralls.wear!
3
3
 
4
4
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
5
  require 'json_cli'
6
+
7
+ FIXTURE_DIR = 'spec/fixture'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaaki Kikuchi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-04 00:00:00.000000000 Z
11
+ date: 2014-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -169,15 +169,24 @@ files:
169
169
  - bin/json_cli
170
170
  - json_cli.gemspec
171
171
  - lib/json_cli.rb
172
- - lib/json_cli/join.rb
173
- - lib/json_cli/unwind.rb
172
+ - lib/json_cli/cli.rb
173
+ - lib/json_cli/command/base.rb
174
+ - lib/json_cli/command/join.rb
175
+ - lib/json_cli/command/unwind.rb
174
176
  - lib/json_cli/version.rb
175
- - spec/attribute.json
176
- - spec/empty.json
177
- - spec/json_cli/join_spec.rb
178
- - spec/json_cli/unwind_spec.rb
177
+ - spec/fixture/attribute.json
178
+ - spec/fixture/empty.json
179
+ - spec/fixture/inner_join_logfile_attribute_id.json
180
+ - spec/fixture/join_attribute_logfile_id.json
181
+ - spec/fixture/join_logfile_attribute_id.json
182
+ - spec/fixture/logfile.json
183
+ - spec/fixture/unwind_logfile_tags.json
184
+ - spec/fixture/unwind_logfile_words.json
185
+ - spec/fixture/unwind_logfile_words_flatten.json
186
+ - spec/json_cli/cli_spec.rb
187
+ - spec/json_cli/command/join_spec.rb
188
+ - spec/json_cli/command/unwind_spec.rb
179
189
  - spec/json_cli_spec.rb
180
- - spec/logfile.json
181
190
  - spec/spec_helper.rb
182
191
  homepage: ''
183
192
  licenses:
@@ -204,10 +213,17 @@ signing_key:
204
213
  specification_version: 4
205
214
  summary: JSON command line tools
206
215
  test_files:
207
- - spec/attribute.json
208
- - spec/empty.json
209
- - spec/json_cli/join_spec.rb
210
- - spec/json_cli/unwind_spec.rb
216
+ - spec/fixture/attribute.json
217
+ - spec/fixture/empty.json
218
+ - spec/fixture/inner_join_logfile_attribute_id.json
219
+ - spec/fixture/join_attribute_logfile_id.json
220
+ - spec/fixture/join_logfile_attribute_id.json
221
+ - spec/fixture/logfile.json
222
+ - spec/fixture/unwind_logfile_tags.json
223
+ - spec/fixture/unwind_logfile_words.json
224
+ - spec/fixture/unwind_logfile_words_flatten.json
225
+ - spec/json_cli/cli_spec.rb
226
+ - spec/json_cli/command/join_spec.rb
227
+ - spec/json_cli/command/unwind_spec.rb
211
228
  - spec/json_cli_spec.rb
212
- - spec/logfile.json
213
229
  - spec/spec_helper.rb
@@ -1,41 +0,0 @@
1
- # -*- mode: ruby; coding: utf-8 -*-
2
- require 'multi_json'
3
-
4
- module JsonCli
5
- # Join JSON class
6
- class JoinJson
7
- def self.left_join(left_io, right_io, join_key, out = STDOUT)
8
- right = io2hash(right_io, join_key)
9
- left_io.each do |l|
10
- j = MultiJson.load(l.chomp)
11
- j.merge!(right[j[join_key]] || {}) if j.key?(join_key)
12
- out.puts MultiJson.dump(j)
13
- end
14
- out.flush
15
- end
16
-
17
- def self.right_join(left_io, right_io, join_key, out = STDOUT)
18
- left_join(right_io, left_io, join_key, out)
19
- end
20
-
21
- def self.inner_join(left_io, right_io, join_key, out = STDOUT)
22
- right = io2hash(right_io, join_key)
23
- left_io.each do |l|
24
- j = MultiJson.load(l.chomp)
25
- next if !j.key?(join_key) || !right.key?(j[join_key])
26
- out.puts MultiJson.dump(j.merge(right[j[join_key]]))
27
- end
28
- out.flush
29
- end
30
-
31
- def self.io2hash(io, key)
32
- hash = {}
33
- io.each do |l|
34
- j = MultiJson.load(l.chomp)
35
- next unless j.key?(key)
36
- hash[j[key]] = j.select { |k, _| k != key }
37
- end
38
- hash
39
- end
40
- end
41
- end
@@ -1,43 +0,0 @@
1
- # -*- mode: ruby; coding: utf-8 -*-
2
- require 'multi_json'
3
-
4
- module JsonCli
5
- # Unwind JSON class
6
- class UnwindJson
7
- def self.unwind_array(io, unwind_key, opt = {})
8
- opt[:out] ||= STDOUT
9
- io.each do |l|
10
- j = MultiJson.load(l.chomp)
11
- if j.key?(unwind_key) && j[unwind_key].is_a?(Array)
12
- j[unwind_key].each do |v|
13
- opt[:out].puts MultiJson.dump(j.merge(unwind_key => v))
14
- end
15
- else
16
- opt[:out].puts MultiJson.dump(j)
17
- end
18
- end
19
- end
20
-
21
- def self.unwind_hash(io, unwind_key, opt = {})
22
- opt[:out] ||= STDOUT
23
- opt[:key_label] ||= 'key'
24
- opt[:value_label] ||= 'value'
25
- io.each do |l|
26
- j = MultiJson.load(l.chomp)
27
- if j.key?(unwind_key) && j[unwind_key].is_a?(Hash)
28
- base = j.select { |k, _| k != unwind_key } if opt[:flatten]
29
- j[unwind_key].each do |k, v|
30
- if opt[:flatten]
31
- jj = base.merge(opt[:key_label] => k, opt[:value_label] => v)
32
- else
33
- jj = j.merge(unwind_key => { k => v })
34
- end
35
- opt[:out].puts MultiJson.dump(jj)
36
- end
37
- else
38
- opt[:out].puts MultiJson.dump(j)
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,112 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe JsonCli::JoinJson do
4
- describe '#left_join' do
5
- context 'when normal file input' do
6
- it 'joins right to left' do
7
- @left_io = File.open('spec/logfile.json', 'r')
8
- @right_io = File.open('spec/attribute.json', 'r')
9
- key = '_id'
10
- result = StringIO.new
11
- JsonCli::JoinJson.left_join(@left_io, @right_io, key, result)
12
- lines = result.string.each_line.to_a.map { |l| l.chomp }
13
- expect(lines.size).to eq(4)
14
- expect(lines[0]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
15
- %q("tags":["news","sports"],"words":{"baseball":3,"soccer":2,) +
16
- %q("ichiro":1,"honda":2},"title":"A","authors":["alice"]}))
17
- expect(lines[1]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
18
- %q("tags":["sports"],"words":{"sumo":3,"tennis":1,"japan":2},) +
19
- %q("title":"B","authors":["bob","john"]}))
20
- expect(lines[2]).to eq(%q({"_id":"0003","timestamp":1385274100,) +
21
- %q("tags":["drama"],"words":{"furuhata":2,"ichiro":3}}))
22
- expect(lines[3]).to eq(%q({"broken":"data"}))
23
- end
24
- end
25
-
26
- context 'when left is empty' do
27
- it 'outputs nothing' do
28
- @left_io = File.open('spec/empty.json', 'r')
29
- @right_io = File.open('spec/attribute.json', 'r')
30
- key = '_id'
31
- result = StringIO.new
32
- JsonCli::JoinJson.left_join(@left_io, @right_io, key, result)
33
- lines = result.string.each_line.to_a.map { |l| l.chomp }
34
- expect(lines).to be_empty
35
- end
36
- end
37
-
38
- context 'when right is empty' do
39
- it 'outputs just left' do
40
- @left_io = File.open('spec/logfile.json', 'r')
41
- @right_io = File.open('spec/empty.json', 'r')
42
- key = '_id'
43
- result = StringIO.new
44
- JsonCli::JoinJson.left_join(@left_io, @right_io, key, result)
45
- lines = result.string.each_line.to_a.map { |l| l.chomp }
46
- @left_io.rewind
47
- left_lines = @left_io.each.to_a.map { |l| l.chomp }
48
- expect(lines).to eq(left_lines)
49
- end
50
- end
51
- end
52
-
53
- describe '#right_join' do
54
- context 'when normal file input' do
55
- it 'joins left to right' do
56
- @left_io = File.open('spec/logfile.json', 'r')
57
- @right_io = File.open('spec/attribute.json', 'r')
58
- key = '_id'
59
- result = StringIO.new
60
- JsonCli::JoinJson.right_join(@left_io, @right_io, key, result)
61
- lines = result.string.each_line.to_a.map { |l| l.chomp }
62
- expect(lines.size).to eq(3)
63
- expect(lines[0]).to eq(%q({"_id":"0001","title":"A",) +
64
- %q("authors":["alice"],"timestamp":1385273700,) +
65
- %q("tags":["news","sports"],"words":{"baseball":3,) +
66
- %q("soccer":2,"ichiro":1,"honda":2}}))
67
- expect(lines[1]).to eq(%q({"_id":"0002","title":"B",) +
68
- %q("authors":["bob","john"],"timestamp":1385273730,) +
69
- %q("tags":["sports"],"words":{"sumo":3,"tennis":1,"japan":2}}))
70
- expect(lines[2]).to eq(%q({"_id":"0004","title":"D",) +
71
- %q("authors":["dave"]}))
72
- end
73
- end
74
- end
75
-
76
- describe '#inner_join' do
77
- context 'when normal file input' do
78
- it 'joins inner' do
79
- @left_io = File.open('spec/logfile.json', 'r')
80
- @right_io = File.open('spec/attribute.json', 'r')
81
- key = '_id'
82
- result = StringIO.new
83
- JsonCli::JoinJson.inner_join(@left_io, @right_io, key, result)
84
- lines = result.string.each_line.to_a.map { |l| l.chomp }
85
- expect(lines.size).to eq(2)
86
- expect(lines[0]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
87
- %q("tags":["news","sports"],"words":{"baseball":3,"soccer":2,) +
88
- %q("ichiro":1,"honda":2},"title":"A","authors":["alice"]}))
89
- expect(lines[1]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
90
- %q("tags":["sports"],"words":{"sumo":3,"tennis":1,"japan":2},) +
91
- %q("title":"B","authors":["bob","john"]}))
92
- end
93
- end
94
-
95
- context 'when right is empty' do
96
- it 'outputs nothing' do
97
- @left_io = File.open('spec/logfile.json', 'r')
98
- @right_io = File.open('spec/empty.json', 'r')
99
- key = '_id'
100
- result = StringIO.new
101
- JsonCli::JoinJson.inner_join(@left_io, @right_io, key, result)
102
- lines = result.string.each_line.to_a.map { |l| l.chomp }
103
- expect(lines).to be_empty
104
- end
105
- end
106
- end
107
-
108
- after do
109
- @left_io.close if @left_io
110
- @right_io.close if @right_io
111
- end
112
- end
@@ -1,90 +0,0 @@
1
- # -*- mode: ruby; coding: utf-8 -*-
2
- require 'spec_helper'
3
-
4
- describe JsonCli::UnwindJson do
5
- describe '#unwind_array' do
6
- context 'when normal file input' do
7
- it 'unwinds array values' do
8
- @io = File.open('spec/logfile.json', 'r')
9
- key = 'tags'
10
- result = StringIO.new
11
- JsonCli::UnwindJson.unwind_array(@io, key, out: result)
12
- lines = result.string.each_line.to_a.map { |l| l.chomp }
13
- expect(lines.size).to eq(5)
14
- expect(lines[0]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
15
- %q("tags":"news","words":{"baseball":3,"soccer":2,"ichiro":1,) +
16
- %q("honda":2}}))
17
- expect(lines[1]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
18
- %q("tags":"sports","words":{"baseball":3,"soccer":2,"ichiro":1,) +
19
- %q("honda":2}}))
20
- expect(lines[2]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
21
- %q("tags":"sports","words":{"sumo":3,"tennis":1,"japan":2}}))
22
- expect(lines[3]).to eq(%q({"_id":"0003","timestamp":1385274100,) +
23
- %q("tags":"drama","words":{"furuhata":2,"ichiro":3}}))
24
- expect(lines[4]).to eq(%q({"broken":"data"}))
25
- end
26
- end
27
- end
28
-
29
- describe '#unwind_array' do
30
- context 'when normal file input' do
31
- it 'unwinds hash values without flatten' do
32
- @io = File.open('spec/logfile.json', 'r')
33
- key = 'words'
34
- result = StringIO.new
35
- JsonCli::UnwindJson.unwind_hash(@io, key, out: result)
36
- lines = result.string.each_line.to_a.map { |l| l.chomp }
37
- expect(lines.size).to eq(10)
38
- expect(lines[0]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
39
- %q("tags":["news","sports"],"words":{"baseball":3}}))
40
- expect(lines[1]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
41
- %q("tags":["news","sports"],"words":{"soccer":2}}))
42
- expect(lines[2]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
43
- %q("tags":["news","sports"],"words":{"ichiro":1}}))
44
- expect(lines[3]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
45
- %q("tags":["news","sports"],"words":{"honda":2}}))
46
- expect(lines[4]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
47
- %q("tags":["sports"],"words":{"sumo":3}}))
48
- expect(lines[5]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
49
- %q("tags":["sports"],"words":{"tennis":1}}))
50
- expect(lines[6]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
51
- %q("tags":["sports"],"words":{"japan":2}}))
52
- expect(lines[7]).to eq(%q({"_id":"0003","timestamp":1385274100,) +
53
- %q("tags":["drama"],"words":{"furuhata":2}}))
54
- expect(lines[8]).to eq(%q({"_id":"0003","timestamp":1385274100,) +
55
- %q("tags":["drama"],"words":{"ichiro":3}}))
56
- expect(lines[9]).to eq(%q({"broken":"data"}))
57
- end
58
-
59
- it 'unwinds hash values with flatten' do
60
- @io = File.open('spec/logfile.json', 'r')
61
- key = 'words'
62
- result = StringIO.new
63
- JsonCli::UnwindJson.unwind_hash(
64
- @io, key,
65
- out: result, flatten: true, key_label: 'word', value_label: 'count')
66
- lines = result.string.each_line.to_a.map { |l| l.chomp }
67
- expect(lines.size).to eq(10)
68
- expect(lines[0]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
69
- %q("tags":["news","sports"],"word":"baseball","count":3}))
70
- expect(lines[1]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
71
- %q("tags":["news","sports"],"word":"soccer","count":2}))
72
- expect(lines[2]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
73
- %q("tags":["news","sports"],"word":"ichiro","count":1}))
74
- expect(lines[3]).to eq(%q({"_id":"0001","timestamp":1385273700,) +
75
- %q("tags":["news","sports"],"word":"honda","count":2}))
76
- expect(lines[4]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
77
- %q("tags":["sports"],"word":"sumo","count":3}))
78
- expect(lines[5]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
79
- %q("tags":["sports"],"word":"tennis","count":1}))
80
- expect(lines[6]).to eq(%q({"_id":"0002","timestamp":1385273730,) +
81
- %q("tags":["sports"],"word":"japan","count":2}))
82
- expect(lines[7]).to eq(%q({"_id":"0003","timestamp":1385274100,) +
83
- %q("tags":["drama"],"word":"furuhata","count":2}))
84
- expect(lines[8]).to eq(%q({"_id":"0003","timestamp":1385274100,) +
85
- %q("tags":["drama"],"word":"ichiro","count":3}))
86
- expect(lines[9]).to eq(%q({"broken":"data"}))
87
- end
88
- end
89
- end
90
- end