json_cli 0.0.1 → 0.0.2

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