tb 0.4 → 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +16 -5
- data/lib/tb/cmd_git_log.rb +29 -15
- data/lib/tb/cmd_melt.rb +105 -0
- data/lib/tb/cmd_svn_log.rb +13 -4
- data/lib/tb/cmd_unmelt.rb +106 -0
- data/lib/tb/cmdtop.rb +2 -0
- data/lib/tb/enumerator.rb +1 -0
- data/lib/tb/func.rb +11 -11
- data/test/test_cmd_git_log.rb +28 -0
- data/test/test_cmd_group.rb +117 -0
- data/test/test_cmd_melt.rb +172 -0
- data/test/test_cmd_svn_log.rb +21 -0
- data/test/test_cmd_unmelt.rb +157 -0
- metadata +10 -2
data/README
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
tb provides a command and a library for manipulating tables:
|
4
4
|
Unix filter like operations (grep, sort, cat, cut, ls, etc.),
|
5
5
|
SQL like operations (join, group, etc.),
|
6
|
+
other table operations (gsub, rename, cross, melt, unmelt, etc.),
|
6
7
|
information extractions (git-log, svn-log, tar-tvf),
|
7
8
|
and more.
|
8
9
|
|
@@ -78,6 +79,7 @@ Following example searches languages which name contains a non-alphabet characte
|
|
78
79
|
F#,2002
|
79
80
|
|
80
81
|
"grep" subcommand can take Ruby expression, instead of a regexp.
|
82
|
+
The variable, "_", contains a hash which represents a record.
|
81
83
|
|
82
84
|
% tb grep --ruby '(1990..1999).include?(_["year"].to_i)' sample/langs.csv
|
83
85
|
language,year
|
@@ -145,17 +147,21 @@ There are more subcommands.
|
|
145
147
|
tb join [OPTS] [TABLE1 TABLE2 ...]
|
146
148
|
tb consecutive [OPTS] [TABLE ...]
|
147
149
|
tb group [OPTS] KEY-FIELD1,... [TABLE ...]
|
148
|
-
tb cross [OPTS]
|
150
|
+
tb cross [OPTS] VKEY-FIELD1,... HKEY-FIELD1,... [TABLE ...]
|
151
|
+
tb melt KEY-FIELDS-LIST [OPTS] [TABLE ...]
|
152
|
+
tb nest [OPTS] NEWFIELD,OLDFIELD1,OLDFIELD2,... [TABLE ...]
|
153
|
+
tb unnest [OPTS] FIELD [TABLE ...]
|
149
154
|
tb shape [OPTS] [TABLE ...]
|
150
155
|
tb mheader [OPTS] [TABLE]
|
151
156
|
tb crop [OPTS] [TABLE ...]
|
152
157
|
tb ls [OPTS] [FILE ...]
|
158
|
+
tb tar-tvf [OPTS] [TAR-FILE ...]
|
153
159
|
tb svn-log [OPTS] -- [SVN-LOG-ARGS]
|
160
|
+
tb git-log [OPTS] [GIT-DIR ...]
|
154
161
|
|
155
|
-
|
156
|
-
|
157
|
-
tb command has many subcommands.
|
162
|
+
tb help -s shows one line summary of the subcommands.
|
158
163
|
|
164
|
+
% tb help -s
|
159
165
|
help : Show help message of tb command.
|
160
166
|
to-csv : Convert a table to CSV (Comma Separated Value).
|
161
167
|
to-tsv : Convert a table to TSV (Tab Separated Value).
|
@@ -173,12 +179,17 @@ tb command has many subcommands.
|
|
173
179
|
join : Concatenate tables horizontally as left/right/full natural join.
|
174
180
|
consecutive : Concatenate consecutive rows.
|
175
181
|
group : Group and aggregate rows.
|
176
|
-
cross : Create a contingency table
|
182
|
+
cross : Create a cross table. (a.k.a contingency table, pivot table)
|
183
|
+
melt : split value fields into records.
|
184
|
+
nest : Nest fields.
|
185
|
+
unnest : Unnest a field.
|
177
186
|
shape : Show table size.
|
178
187
|
mheader : Collapse multi rows header.
|
179
188
|
crop : Extract rectangle in a table.
|
180
189
|
ls : List directory entries as a table.
|
190
|
+
tar-tvf : Show the file listing of tar file.
|
181
191
|
svn-log : Show the SVN log as a table.
|
192
|
+
git-log : Show the GIT log as a table.
|
182
193
|
|
183
194
|
== Install
|
184
195
|
|
data/lib/tb/cmd_git_log.rb
CHANGED
@@ -82,14 +82,16 @@ def (Tb::Cmd).git_log_with_git_log(dir)
|
|
82
82
|
'log',
|
83
83
|
"--pretty=#{Tb::Cmd::GIT_LOG_PRETTY_FORMAT}",
|
84
84
|
'--decorate=full',
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
85
|
+
'--raw',
|
86
|
+
'--numstat',
|
87
|
+
'--abbrev=40',
|
88
|
+
'.',
|
89
|
+
{:chdir=>dir}
|
89
90
|
]
|
90
91
|
$stderr.puts "git command line: #{command.inspect}" if 1 <= Tb::Cmd.opt_debug
|
91
92
|
if Tb::Cmd.opt_git_log_debug_output
|
92
|
-
|
93
|
+
# File.realdirpath is required before Ruby 2.0.
|
94
|
+
command.last[:out] = File.realdirpath(Tb::Cmd.opt_git_log_debug_output)
|
93
95
|
system(*command)
|
94
96
|
File.open(Tb::Cmd.opt_git_log_debug_output) {|f|
|
95
97
|
yield f
|
@@ -131,22 +133,34 @@ end
|
|
131
133
|
|
132
134
|
def (Tb::Cmd).git_log_parse_commit(commit_info, files)
|
133
135
|
commit_info = commit_info.split(/\n(?=[a-z])/)
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
warn "unexpected git-log output: #{file_line.inspect}"
|
139
|
-
next
|
140
|
-
end
|
136
|
+
files_raw = {}
|
137
|
+
files_numstat = {}
|
138
|
+
files.split(/\n/).each {|file_line|
|
139
|
+
if /\A:(\d+) (\d+) ([0-9a-f]+) ([0-9a-f]+) (\S+)\t(.+)\z/ =~ file_line
|
141
140
|
mode1, mode2, hash1, hash2, status, filename = $1, $2, $3, $4, $5, $6
|
142
141
|
filename = git_log_unescape_filename(filename)
|
143
|
-
|
142
|
+
files_raw[filename] = [mode1, mode2, hash1, hash2, status]
|
143
|
+
elsif /\A(\d+|-)\t(\d+|-)\t(.+)\z/ =~ file_line
|
144
|
+
add, del, filename = $1, $2, $3
|
145
|
+
add = add == '-' ? nil : add.to_i
|
146
|
+
del = del == '-' ? nil : del.to_i
|
147
|
+
filename = git_log_unescape_filename(filename)
|
148
|
+
files_numstat[filename] = [add, del]
|
149
|
+
else
|
150
|
+
warn "unexpected git-log output (raw/numstat): #{file_line.inspect}"
|
151
|
+
end
|
152
|
+
}
|
153
|
+
Tb.csv_stream_output(files_csv="") {|gen|
|
154
|
+
gen << %w[mode1 mode2 hash1 hash2 add del status filename]
|
155
|
+
files_raw.each {|filename, (mode1, mode2, hash1, hash2, status)|
|
156
|
+
add, del = files_numstat[filename]
|
157
|
+
gen << [mode1, mode2, hash1, hash2, add, del, status, filename]
|
144
158
|
}
|
145
159
|
}
|
146
160
|
h = {}
|
147
161
|
commit_info.each {|s|
|
148
162
|
if /:/ !~ s
|
149
|
-
warn "unexpected git-log output: #{s.inspect}"
|
163
|
+
warn "unexpected git-log output (header:value): #{s.inspect}"
|
150
164
|
next
|
151
165
|
end
|
152
166
|
k = $`
|
@@ -171,7 +185,7 @@ def (Tb::Cmd).git_log_each_commit(f)
|
|
171
185
|
chunk.chomp!("\x01commit-separator\x01\n")
|
172
186
|
next if chunk.empty? # beginning of the output
|
173
187
|
if /\nend-commit\n/ !~ chunk
|
174
|
-
warn "unexpected git-log output: #{chunk.inspect}"
|
188
|
+
warn "unexpected git-log output (end-commit): #{chunk.inspect}"
|
175
189
|
next
|
176
190
|
end
|
177
191
|
commit_info, files = $`, $'
|
data/lib/tb/cmd_melt.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Copyright (C) 2012 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions
|
5
|
+
# are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright
|
8
|
+
# notice, this list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above
|
10
|
+
# copyright notice, this list of conditions and the following
|
11
|
+
# disclaimer in the documentation and/or other materials provided
|
12
|
+
# with the distribution.
|
13
|
+
# 3. The name of the author may not be used to endorse or promote
|
14
|
+
# products derived from this software without specific prior
|
15
|
+
# written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
18
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
21
|
+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
23
|
+
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
25
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
26
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
27
|
+
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
Tb::Cmd.subcommands << 'melt'
|
30
|
+
|
31
|
+
Tb::Cmd.default_option[:opt_melt_recnum] = nil
|
32
|
+
Tb::Cmd.default_option[:opt_melt_regexps] = []
|
33
|
+
Tb::Cmd.default_option[:opt_melt_list] = []
|
34
|
+
Tb::Cmd.default_option[:opt_melt_variable_field] = 'variable'
|
35
|
+
Tb::Cmd.default_option[:opt_melt_value_field] = 'value'
|
36
|
+
|
37
|
+
def (Tb::Cmd).op_melt
|
38
|
+
op = OptionParser.new
|
39
|
+
op.banner = "Usage: tb melt KEY-FIELDS-LIST [OPTS] [TABLE ...]\n" +
|
40
|
+
"split value fields into records."
|
41
|
+
define_common_option(op, "hNo", "--no-pager")
|
42
|
+
op.def_option('--recnum[=FIELD]',
|
43
|
+
'add recnum field (default don\'t add)') {|field|
|
44
|
+
Tb::Cmd.opt_melt_recnum = field || 'recnum'
|
45
|
+
}
|
46
|
+
op.def_option('-R REGEXP',
|
47
|
+
'--melt-regexp REGEXP',
|
48
|
+
'regexp for melt fields') {|regexp|
|
49
|
+
Tb::Cmd.opt_melt_regexps << Regexp.compile(regexp)
|
50
|
+
}
|
51
|
+
op.def_option('--melt-fields FIELD,...',
|
52
|
+
'list of melt fields') {|fields|
|
53
|
+
Tb::Cmd.opt_melt_list.concat split_field_list_argument(fields)
|
54
|
+
}
|
55
|
+
op.def_option('--variable-field FIELD', 'variable field. (default: variable)') {|field|
|
56
|
+
Tb::Cmd.opt_melt_variable_field = field
|
57
|
+
}
|
58
|
+
op.def_option('--value-field FIELD', 'value field. (default: value)') {|field|
|
59
|
+
Tb::Cmd.opt_melt_value_field = field
|
60
|
+
}
|
61
|
+
op
|
62
|
+
end
|
63
|
+
|
64
|
+
def (Tb::Cmd).main_melt(argv)
|
65
|
+
op_melt.parse!(argv)
|
66
|
+
exit_if_help('melt')
|
67
|
+
err('no key-fields given.') if argv.empty?
|
68
|
+
key_fields = split_field_list_argument(argv.shift)
|
69
|
+
key_fields_hash = Hash[key_fields.map {|f| [f, true] }]
|
70
|
+
if Tb::Cmd.opt_melt_regexps.empty? && Tb::Cmd.opt_melt_list.empty?
|
71
|
+
melt_fields_pattern = //
|
72
|
+
else
|
73
|
+
list = Tb::Cmd.opt_melt_list + Tb::Cmd.opt_melt_regexps
|
74
|
+
melt_fields_pattern = /\A#{Regexp.union(list)}\z/
|
75
|
+
end
|
76
|
+
argv = ['-'] if argv.empty?
|
77
|
+
creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
|
78
|
+
er = Tb::Enumerator.new {|y|
|
79
|
+
header = []
|
80
|
+
header << Tb::Cmd.opt_melt_recnum if Tb::Cmd.opt_melt_recnum
|
81
|
+
header.concat key_fields
|
82
|
+
header << Tb::Cmd.opt_melt_variable_field
|
83
|
+
header << Tb::Cmd.opt_melt_value_field
|
84
|
+
y.set_header header
|
85
|
+
creader.each_with_index {|pairs, i|
|
86
|
+
recnum = i + 1
|
87
|
+
h0 = {}
|
88
|
+
h0[Tb::Cmd.opt_melt_recnum] = nil if Tb::Cmd.opt_melt_recnum
|
89
|
+
key_fields.each {|kf|
|
90
|
+
h0[kf] = pairs[kf]
|
91
|
+
}
|
92
|
+
pairs.each {|f, v|
|
93
|
+
next if key_fields_hash[f]
|
94
|
+
next if melt_fields_pattern !~ f
|
95
|
+
h = h0.dup
|
96
|
+
h[Tb::Cmd.opt_melt_recnum] = recnum if Tb::Cmd.opt_melt_recnum
|
97
|
+
h[Tb::Cmd.opt_melt_variable_field] = f
|
98
|
+
h[Tb::Cmd.opt_melt_value_field] = v
|
99
|
+
y.yield h
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
output_tbenum(er)
|
104
|
+
end
|
105
|
+
|
data/lib/tb/cmd_svn_log.rb
CHANGED
@@ -97,14 +97,23 @@ class Tb::Cmd::SVNLOGListener
|
|
97
97
|
end
|
98
98
|
@y.set_header @header
|
99
99
|
end
|
100
|
+
assoc = @log.to_a.reject {|f, v| !%w[rev author date msg].include?(f) }
|
101
|
+
if !assoc.assoc('author')
|
102
|
+
assoc << ['author', '(no author)']
|
103
|
+
end
|
104
|
+
if !assoc.assoc('date')
|
105
|
+
assoc << ['date', '(no date)']
|
106
|
+
end
|
107
|
+
if !assoc.assoc('msg')
|
108
|
+
assoc << ['msg', '']
|
109
|
+
end
|
100
110
|
if @log['paths']
|
101
111
|
@log['paths'].each {|h|
|
102
|
-
|
103
|
-
|
104
|
-
@y.yield Hash[
|
112
|
+
assoc2 = assoc.dup
|
113
|
+
assoc2.concat(h.to_a.reject {|f, v| !%w[kind action path].include?(f) })
|
114
|
+
@y.yield Hash[assoc2]
|
105
115
|
}
|
106
116
|
else
|
107
|
-
assoc = @log.to_a.reject {|f, v| !%w[rev author date msg].include?(f) }
|
108
117
|
@y.yield Hash[assoc]
|
109
118
|
end
|
110
119
|
@log = nil
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Copyright (C) 2012 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions
|
5
|
+
# are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright
|
8
|
+
# notice, this list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above
|
10
|
+
# copyright notice, this list of conditions and the following
|
11
|
+
# disclaimer in the documentation and/or other materials provided
|
12
|
+
# with the distribution.
|
13
|
+
# 3. The name of the author may not be used to endorse or promote
|
14
|
+
# products derived from this software without specific prior
|
15
|
+
# written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
18
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
21
|
+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
23
|
+
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
25
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
26
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
27
|
+
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
Tb::Cmd.subcommands << 'unmelt'
|
30
|
+
|
31
|
+
Tb::Cmd.default_option[:opt_unmelt_recnum] = nil
|
32
|
+
Tb::Cmd.default_option[:opt_unmelt_keys] = []
|
33
|
+
Tb::Cmd.default_option[:opt_unmelt_variable_field] = 'variable'
|
34
|
+
Tb::Cmd.default_option[:opt_unmelt_value_field] = 'value'
|
35
|
+
|
36
|
+
def (Tb::Cmd).op_unmelt
|
37
|
+
op = OptionParser.new
|
38
|
+
op.banner = "Usage: tb unmelt [OPTS] [TABLE ...]\n" +
|
39
|
+
"merge melted records into a record."
|
40
|
+
define_common_option(op, "hNo", "--no-pager")
|
41
|
+
op.def_option('--recnum[=FIELD]',
|
42
|
+
'use FIELD as an additional key and remove it from the result. (default: not specified)') {|field|
|
43
|
+
Tb::Cmd.opt_unmelt_recnum = field || 'recnum'
|
44
|
+
}
|
45
|
+
op.def_option('--keys FIELD,...', 'key fields. (default: all fields except variable and value)') {|fields|
|
46
|
+
Tb::Cmd.opt_unmelt_keys.concat split_field_list_argument(fields)
|
47
|
+
}
|
48
|
+
op.def_option('--variable-field FIELD', 'variable field. (default: variable)') {|field|
|
49
|
+
Tb::Cmd.opt_unmelt_variable_field = field
|
50
|
+
}
|
51
|
+
op.def_option('--value-field FIELD', 'value field. (default: value)') {|field|
|
52
|
+
Tb::Cmd.opt_unmelt_value_field = field
|
53
|
+
}
|
54
|
+
op
|
55
|
+
end
|
56
|
+
|
57
|
+
def (Tb::Cmd).main_unmelt(argv)
|
58
|
+
op_unmelt.parse!(argv)
|
59
|
+
exit_if_help('unmelt')
|
60
|
+
argv = ['-'] if argv.empty?
|
61
|
+
creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
|
62
|
+
if Tb::Cmd.opt_unmelt_keys.empty?
|
63
|
+
key_fields = nil
|
64
|
+
else
|
65
|
+
if Tb::Cmd.opt_unmelt_recnum
|
66
|
+
key_fields = [Tb::Cmd.opt_unmelt_recnum]
|
67
|
+
else
|
68
|
+
key_fields = []
|
69
|
+
end
|
70
|
+
key_fields += Tb::Cmd.opt_unmelt_keys
|
71
|
+
end
|
72
|
+
er = Tb::Enumerator.new {|y|
|
73
|
+
creader.chunk {|pairs|
|
74
|
+
keys = {}
|
75
|
+
if key_fields
|
76
|
+
key_fields.each {|k|
|
77
|
+
keys[k] = pairs[k]
|
78
|
+
}
|
79
|
+
else
|
80
|
+
pairs.each_key {|k|
|
81
|
+
next if k == Tb::Cmd.opt_unmelt_variable_field ||
|
82
|
+
k == Tb::Cmd.opt_unmelt_value_field
|
83
|
+
keys[k] = pairs[k]
|
84
|
+
}
|
85
|
+
end
|
86
|
+
keys
|
87
|
+
}.each {|keys, pairs_ary|
|
88
|
+
if Tb::Cmd.opt_unmelt_recnum
|
89
|
+
keys.delete Tb::Cmd.opt_unmelt_recnum
|
90
|
+
end
|
91
|
+
rec = keys.dup
|
92
|
+
pairs_ary.each {|pairs|
|
93
|
+
var = pairs[Tb::Cmd.opt_unmelt_variable_field]
|
94
|
+
val = pairs[Tb::Cmd.opt_unmelt_value_field]
|
95
|
+
if rec.has_key? var
|
96
|
+
y.yield rec
|
97
|
+
rec = keys.dup
|
98
|
+
end
|
99
|
+
rec[var] = val
|
100
|
+
}
|
101
|
+
y.yield rec
|
102
|
+
}
|
103
|
+
}
|
104
|
+
output_tbenum(er)
|
105
|
+
end
|
106
|
+
|
data/lib/tb/cmdtop.rb
CHANGED
data/lib/tb/enumerator.rb
CHANGED
@@ -55,6 +55,7 @@ class Tb::Enumerator < Enumerator
|
|
55
55
|
def self.new(&enumerator_proc)
|
56
56
|
super() {|y|
|
57
57
|
header_proc = Thread.current[:tb_enumerator_header_proc]
|
58
|
+
Thread.current[:tb_enumerator_header_proc] = nil
|
58
59
|
ty = Tb::Yielder.new(header_proc, y)
|
59
60
|
enumerator_proc.call(ty)
|
60
61
|
if !ty.header_proc_called
|
data/lib/tb/func.rb
CHANGED
@@ -77,24 +77,24 @@ module Tb::Func
|
|
77
77
|
def Count.aggregate(count) count end
|
78
78
|
|
79
79
|
module Sum; end
|
80
|
-
def Sum.start(value) Tb::Func.smart_numerize(value) end
|
80
|
+
def Sum.start(value) value.nil? ? 0 : Tb::Func.smart_numerize(value) end
|
81
81
|
def Sum.call(v1, v2) v1 + v2 end
|
82
82
|
def Sum.aggregate(sum) sum end
|
83
83
|
|
84
84
|
module Min; end
|
85
|
-
def Min.start(value) [value, Tb::Func.smart_cmp_value(value)]
|
86
|
-
def Min.call(vc1, vc2) (vc1.last <=> vc2.last) <= 0 ? vc1 : vc2 end
|
87
|
-
def Min.aggregate(vc) vc.first end
|
85
|
+
def Min.start(value) value.nil? ? nil : [value, Tb::Func.smart_cmp_value(value)] end
|
86
|
+
def Min.call(vc1, vc2) vc1.nil? ? vc2 : vc2.nil? ? vc1 : (vc1.last <=> vc2.last) <= 0 ? vc1 : vc2 end
|
87
|
+
def Min.aggregate(vc) vc.nil? ? nil : vc.first end
|
88
88
|
|
89
89
|
module Max; end
|
90
|
-
def Max.start(value) [value, Tb::Func.smart_cmp_value(value)]
|
91
|
-
def Max.call(vc1, vc2) (vc1.last <=> vc2.last) >= 0 ? vc1 : vc2 end
|
92
|
-
def Max.aggregate(vc) vc.first end
|
90
|
+
def Max.start(value) value.nil? ? nil : [value, Tb::Func.smart_cmp_value(value)] end
|
91
|
+
def Max.call(vc1, vc2) vc1.nil? ? vc2 : vc2.nil? ? vc1 : (vc1.last <=> vc2.last) >= 0 ? vc1 : vc2 end
|
92
|
+
def Max.aggregate(vc) vc.nil? ? nil : vc.first end
|
93
93
|
|
94
94
|
module Avg; end
|
95
|
-
def Avg.start(value) [Tb::Func.smart_numerize(value), 1] end
|
95
|
+
def Avg.start(value) value.nil? ? [0, 0] : [Tb::Func.smart_numerize(value), 1] end
|
96
96
|
def Avg.call(v1, v2) [v1[0] + v2[0], v1[1] + v2[1]] end
|
97
|
-
def Avg.aggregate(sum_count) sum_count[0] / sum_count[1].to_f end
|
97
|
+
def Avg.aggregate(sum_count) sum_count[1] == 0 ? nil : sum_count[0] / sum_count[1].to_f end
|
98
98
|
|
99
99
|
module First; end
|
100
100
|
def First.start(value) value end
|
@@ -107,12 +107,12 @@ module Tb::Func
|
|
107
107
|
def Last.aggregate(value) value end
|
108
108
|
|
109
109
|
module Values; end
|
110
|
-
def Values.start(value) [value] end
|
110
|
+
def Values.start(value) value.nil? ? [] : [value] end
|
111
111
|
def Values.call(a1, a2) a1.concat a2 end
|
112
112
|
def Values.aggregate(ary) ary.join(',') end
|
113
113
|
|
114
114
|
module UniqueValues; end
|
115
|
-
def UniqueValues.start(value) {value => true} end
|
115
|
+
def UniqueValues.start(value) value.nil? ? {} : {value => true} end
|
116
116
|
def UniqueValues.call(h1, h2) h1.update h2 end
|
117
117
|
def UniqueValues.aggregate(hash) hash.keys.join(',') end
|
118
118
|
|
data/test/test_cmd_git_log.rb
CHANGED
@@ -159,4 +159,32 @@ class TestTbCmdGitLog < Test::Unit::TestCase
|
|
159
159
|
assert(!log.empty?)
|
160
160
|
end
|
161
161
|
|
162
|
+
def test_binary
|
163
|
+
system("git init -q")
|
164
|
+
File.open("foo", "w") {|f| f.print "\0\xff" }
|
165
|
+
system("git add foo")
|
166
|
+
system("git commit -q -m msg foo")
|
167
|
+
Tb::Cmd.main_git_log(['-o', o="o.csv"])
|
168
|
+
result = File.read(o)
|
169
|
+
tb = Tb.parse_csv(result)
|
170
|
+
assert_equal(1, tb.size)
|
171
|
+
assert_match(/,,,A,foo\n/, tb.get_record(0)["files"])
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_subdir
|
175
|
+
system("git init -q")
|
176
|
+
File.open("foo", "w") {|f| f.print "foo" }
|
177
|
+
system("git add foo")
|
178
|
+
system("git commit -q -m msg foo")
|
179
|
+
Dir.mkdir("bar")
|
180
|
+
File.open("bar/baz", "w") {|f| f.print "baz" }
|
181
|
+
system("git add bar")
|
182
|
+
system("git commit -q -m msg bar")
|
183
|
+
Tb::Cmd.main_git_log(['-o', o="o.csv", "bar"])
|
184
|
+
result = File.read(o)
|
185
|
+
tb = Tb.parse_csv(result)
|
186
|
+
assert_equal(1, tb.size)
|
187
|
+
assert_not_match(/foo\n/, tb.get_record(0)["files"])
|
188
|
+
end
|
189
|
+
|
162
190
|
end
|
data/test/test_cmd_group.rb
CHANGED
@@ -178,4 +178,121 @@ class TestTbCmdGroup < Test::Unit::TestCase
|
|
178
178
|
assert(!exc.success?)
|
179
179
|
end
|
180
180
|
|
181
|
+
def test_sum_nil
|
182
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
183
|
+
k,v
|
184
|
+
a,2
|
185
|
+
b,3
|
186
|
+
a,
|
187
|
+
b,4
|
188
|
+
End
|
189
|
+
Tb::Cmd.main_group(['-o', o="o.csv", 'k', '-a', 'sum(v)', i])
|
190
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
191
|
+
k,sum(v)
|
192
|
+
a,2
|
193
|
+
b,7
|
194
|
+
End
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_avg_nil
|
198
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
199
|
+
k,v
|
200
|
+
a,2
|
201
|
+
b,3
|
202
|
+
c,
|
203
|
+
a,
|
204
|
+
b,4
|
205
|
+
c,
|
206
|
+
End
|
207
|
+
Tb::Cmd.main_group(['-o', o="o.csv", 'k', '-a', 'avg(v)', i])
|
208
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
209
|
+
k,avg(v)
|
210
|
+
a,2.0
|
211
|
+
b,3.5
|
212
|
+
c,
|
213
|
+
End
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_min_nil
|
217
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
218
|
+
k,v
|
219
|
+
a,9
|
220
|
+
b,9
|
221
|
+
c,
|
222
|
+
a,2
|
223
|
+
b,
|
224
|
+
c,
|
225
|
+
a,
|
226
|
+
b,4
|
227
|
+
c,
|
228
|
+
End
|
229
|
+
Tb::Cmd.main_group(['-o', o="o.csv", 'k', '-a', 'min(v)', i])
|
230
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
231
|
+
k,min(v)
|
232
|
+
a,2
|
233
|
+
b,4
|
234
|
+
c,
|
235
|
+
End
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_max_nil
|
239
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
240
|
+
k,v
|
241
|
+
a,1
|
242
|
+
b,1
|
243
|
+
c,
|
244
|
+
a,2
|
245
|
+
b,
|
246
|
+
c,
|
247
|
+
a,
|
248
|
+
b,4
|
249
|
+
c,
|
250
|
+
End
|
251
|
+
Tb::Cmd.main_group(['-o', o="o.csv", 'k', '-a', 'max(v)', i])
|
252
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
253
|
+
k,max(v)
|
254
|
+
a,2
|
255
|
+
b,4
|
256
|
+
c,
|
257
|
+
End
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_values_nil
|
261
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
262
|
+
k,v
|
263
|
+
a,2
|
264
|
+
b,
|
265
|
+
c,
|
266
|
+
a,
|
267
|
+
b,4
|
268
|
+
c,
|
269
|
+
End
|
270
|
+
Tb::Cmd.main_group(['-o', o="o.csv", 'k', '-a', 'values(v)', i])
|
271
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
272
|
+
k,values(v)
|
273
|
+
a,2
|
274
|
+
b,4
|
275
|
+
c,""
|
276
|
+
End
|
277
|
+
end
|
278
|
+
|
279
|
+
def test_uniquevalues_nil
|
280
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
281
|
+
k,v
|
282
|
+
a,2
|
283
|
+
b,
|
284
|
+
c,
|
285
|
+
a,
|
286
|
+
b,4
|
287
|
+
c,
|
288
|
+
End
|
289
|
+
Tb::Cmd.main_group(['-o', o="o.csv", 'k', '-a', 'uniquevalues(v)', i])
|
290
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
291
|
+
k,uniquevalues(v)
|
292
|
+
a,2
|
293
|
+
b,4
|
294
|
+
c,""
|
295
|
+
End
|
296
|
+
end
|
297
|
+
|
181
298
|
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'tb/cmdtop'
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
class TestTbCmdMelt < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
Tb::Cmd.reset_option
|
8
|
+
@curdir = Dir.pwd
|
9
|
+
@tmpdir = Dir.mktmpdir
|
10
|
+
Dir.chdir @tmpdir
|
11
|
+
end
|
12
|
+
def teardown
|
13
|
+
Tb::Cmd.reset_option
|
14
|
+
Dir.chdir @curdir
|
15
|
+
FileUtils.rmtree @tmpdir
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_basic
|
19
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
20
|
+
a,b,c,d
|
21
|
+
0,1,2,3
|
22
|
+
4,5,6,7
|
23
|
+
8,9,a,b
|
24
|
+
c,d,e,f
|
25
|
+
End
|
26
|
+
Tb::Cmd.main_melt(['-o', o="o.csv", 'a,b', i])
|
27
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
28
|
+
a,b,variable,value
|
29
|
+
0,1,c,2
|
30
|
+
0,1,d,3
|
31
|
+
4,5,c,6
|
32
|
+
4,5,d,7
|
33
|
+
8,9,c,a
|
34
|
+
8,9,d,b
|
35
|
+
c,d,c,e
|
36
|
+
c,d,d,f
|
37
|
+
End
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_recnum
|
41
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
42
|
+
a,b,c,d
|
43
|
+
0,1,2,3
|
44
|
+
4,5,6,7
|
45
|
+
8,9,a,b
|
46
|
+
c,d,e,f
|
47
|
+
End
|
48
|
+
Tb::Cmd.main_melt(['-o', o="o.csv", 'a,b', '--recnum', i])
|
49
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
50
|
+
recnum,a,b,variable,value
|
51
|
+
1,0,1,c,2
|
52
|
+
1,0,1,d,3
|
53
|
+
2,4,5,c,6
|
54
|
+
2,4,5,d,7
|
55
|
+
3,8,9,c,a
|
56
|
+
3,8,9,d,b
|
57
|
+
4,c,d,c,e
|
58
|
+
4,c,d,d,f
|
59
|
+
End
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_recnum_value
|
63
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
64
|
+
a,b,c,d
|
65
|
+
0,1,2,3
|
66
|
+
4,5,6,7
|
67
|
+
8,9,a,b
|
68
|
+
c,d,e,f
|
69
|
+
End
|
70
|
+
Tb::Cmd.main_melt(['-o', o="o.csv", 'a,b', '--recnum=rec', i])
|
71
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
72
|
+
rec,a,b,variable,value
|
73
|
+
1,0,1,c,2
|
74
|
+
1,0,1,d,3
|
75
|
+
2,4,5,c,6
|
76
|
+
2,4,5,d,7
|
77
|
+
3,8,9,c,a
|
78
|
+
3,8,9,d,b
|
79
|
+
4,c,d,c,e
|
80
|
+
4,c,d,d,f
|
81
|
+
End
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_variable_field
|
85
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
86
|
+
a,b,c,d
|
87
|
+
0,1,2,3
|
88
|
+
4,5,6,7
|
89
|
+
8,9,a,b
|
90
|
+
c,d,e,f
|
91
|
+
End
|
92
|
+
Tb::Cmd.main_melt(['-o', o="o.csv", 'a,b', '--variable-field=foo', i])
|
93
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
94
|
+
a,b,foo,value
|
95
|
+
0,1,c,2
|
96
|
+
0,1,d,3
|
97
|
+
4,5,c,6
|
98
|
+
4,5,d,7
|
99
|
+
8,9,c,a
|
100
|
+
8,9,d,b
|
101
|
+
c,d,c,e
|
102
|
+
c,d,d,f
|
103
|
+
End
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_value_field
|
107
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
108
|
+
a,b,c,d
|
109
|
+
0,1,2,3
|
110
|
+
4,5,6,7
|
111
|
+
8,9,a,b
|
112
|
+
c,d,e,f
|
113
|
+
End
|
114
|
+
Tb::Cmd.main_melt(['-o', o="o.csv", 'a,b', '--value-field=bar', i])
|
115
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
116
|
+
a,b,variable,bar
|
117
|
+
0,1,c,2
|
118
|
+
0,1,d,3
|
119
|
+
4,5,c,6
|
120
|
+
4,5,d,7
|
121
|
+
8,9,c,a
|
122
|
+
8,9,d,b
|
123
|
+
c,d,c,e
|
124
|
+
c,d,d,f
|
125
|
+
End
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_melt_regexp
|
129
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
130
|
+
a,b,c,d
|
131
|
+
0,1,2,3
|
132
|
+
4,5,6,7
|
133
|
+
8,9,a,b
|
134
|
+
c,d,e,f
|
135
|
+
End
|
136
|
+
Tb::Cmd.main_melt(['-o', o="o.csv", 'a', '--melt-regexp=[bd]', i])
|
137
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
138
|
+
a,variable,value
|
139
|
+
0,b,1
|
140
|
+
0,d,3
|
141
|
+
4,b,5
|
142
|
+
4,d,7
|
143
|
+
8,b,9
|
144
|
+
8,d,b
|
145
|
+
c,b,d
|
146
|
+
c,d,f
|
147
|
+
End
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_melt_list
|
151
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
152
|
+
a,b,c,d
|
153
|
+
0,1,2,3
|
154
|
+
4,5,6,7
|
155
|
+
8,9,a,b
|
156
|
+
c,d,e,f
|
157
|
+
End
|
158
|
+
Tb::Cmd.main_melt(['-o', o="o.csv", 'a', '--melt-fields=b,d', i])
|
159
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
160
|
+
a,variable,value
|
161
|
+
0,b,1
|
162
|
+
0,d,3
|
163
|
+
4,b,5
|
164
|
+
4,d,7
|
165
|
+
8,b,9
|
166
|
+
8,d,b
|
167
|
+
c,b,d
|
168
|
+
c,d,f
|
169
|
+
End
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
data/test/test_cmd_svn_log.rb
CHANGED
@@ -84,4 +84,25 @@ class TestTbCmdSvnLog < Test::Unit::TestCase
|
|
84
84
|
assert_equal(1, tb.size)
|
85
85
|
assert_match(/baz/, tb.get_record(0)["msg"])
|
86
86
|
end
|
87
|
+
|
88
|
+
def test_no_props
|
89
|
+
system("svnadmin create repo")
|
90
|
+
File.open("repo/hooks/pre-revprop-change", "w", 0755) {|f| f.print "#!/bin/sh\nexit 0\0" }
|
91
|
+
system("svn co -q file://#{@tmpdir}/repo .")
|
92
|
+
File.open("foo", "w") {|f| f.puts "bar" }
|
93
|
+
system("svn add -q foo")
|
94
|
+
system("svn commit -q -m baz foo")
|
95
|
+
system("svn update -q") # update the revision of the directory.
|
96
|
+
system("svn propdel -q svn:author --revprop -r 1 .")
|
97
|
+
system("svn propdel -q svn:date --revprop -r 1 .")
|
98
|
+
system("svn propdel -q svn:log --revprop -r 1 .")
|
99
|
+
###
|
100
|
+
Tb::Cmd.main_svn_log(['-o', o="o.csv"])
|
101
|
+
result = File.read(o)
|
102
|
+
tb = Tb.parse_csv(result)
|
103
|
+
assert_equal(1, tb.size)
|
104
|
+
assert_equal('(no author)', tb.get_record(0)["author"])
|
105
|
+
assert_equal('(no date)', tb.get_record(0)["date"])
|
106
|
+
assert_equal('', tb.get_record(0)["msg"])
|
107
|
+
end
|
87
108
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'tb/cmdtop'
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
class TestTbCmdUnmelt < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
Tb::Cmd.reset_option
|
8
|
+
@curdir = Dir.pwd
|
9
|
+
@tmpdir = Dir.mktmpdir
|
10
|
+
Dir.chdir @tmpdir
|
11
|
+
end
|
12
|
+
def teardown
|
13
|
+
Tb::Cmd.reset_option
|
14
|
+
Dir.chdir @curdir
|
15
|
+
FileUtils.rmtree @tmpdir
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_basic
|
19
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
20
|
+
a,b,variable,value
|
21
|
+
0,1,x,3
|
22
|
+
0,1,y,7
|
23
|
+
4,5,x,b
|
24
|
+
4,5,y,f
|
25
|
+
End
|
26
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", i])
|
27
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
28
|
+
a,b,x,y
|
29
|
+
0,1,3,7
|
30
|
+
4,5,b,f
|
31
|
+
End
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_json
|
35
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
36
|
+
a,b,variable,value
|
37
|
+
0,1,x,3
|
38
|
+
0,1,y,7
|
39
|
+
4,5,x,b
|
40
|
+
4,5,y,f
|
41
|
+
End
|
42
|
+
Tb::Cmd.main_unmelt(['-o', o="o.json", i])
|
43
|
+
assert_equal(<<-"End".gsub(/\s+/, ''), File.read(o).gsub(/\s+/, ''))
|
44
|
+
[
|
45
|
+
{"a":"0", "b":"1", "x":"3", "y":"7"},
|
46
|
+
{"a":"4", "b":"5", "x":"b", "y":"f"}
|
47
|
+
]
|
48
|
+
End
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_duplicated_variable
|
52
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
53
|
+
a,b,variable,value
|
54
|
+
0,1,x,3
|
55
|
+
0,1,x,4
|
56
|
+
End
|
57
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", i])
|
58
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
59
|
+
a,b,x
|
60
|
+
0,1,3
|
61
|
+
0,1,4
|
62
|
+
End
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_keys
|
66
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
67
|
+
a,b,variable,value
|
68
|
+
0,1,x,3
|
69
|
+
0,1,y,7
|
70
|
+
4,5,x,b
|
71
|
+
4,5,y,f
|
72
|
+
End
|
73
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", '--keys=a', i])
|
74
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
75
|
+
a,x,y
|
76
|
+
0,3,7
|
77
|
+
4,b,f
|
78
|
+
End
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_recnum_noneffective
|
82
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
83
|
+
recnum,a,b,variable,value
|
84
|
+
1,0,1,x,3
|
85
|
+
1,0,1,y,7
|
86
|
+
2,4,5,x,b
|
87
|
+
2,4,5,y,f
|
88
|
+
End
|
89
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", '--keys=a,b', '--recnum', i])
|
90
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
91
|
+
a,b,x,y
|
92
|
+
0,1,3,7
|
93
|
+
4,5,b,f
|
94
|
+
End
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_recnum_effective
|
98
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
99
|
+
recnum,a,b,variable,value
|
100
|
+
1,0,1,x,3
|
101
|
+
2,0,1,y,f
|
102
|
+
End
|
103
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", '--keys=a,b', '--recnum', i])
|
104
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
105
|
+
a,b,x,y
|
106
|
+
0,1,3
|
107
|
+
0,1,,f
|
108
|
+
End
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_recnum_value
|
112
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
113
|
+
r,a,b,variable,value
|
114
|
+
1,0,1,x,3
|
115
|
+
2,0,1,y,f
|
116
|
+
End
|
117
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", '--keys=a,b', '--recnum=r', i])
|
118
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
119
|
+
a,b,x,y
|
120
|
+
0,1,3
|
121
|
+
0,1,,f
|
122
|
+
End
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_variable_field
|
126
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
127
|
+
a,b,foo,value
|
128
|
+
0,1,x,3
|
129
|
+
0,1,y,7
|
130
|
+
4,5,x,b
|
131
|
+
4,5,y,f
|
132
|
+
End
|
133
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", '--variable-field=foo', i])
|
134
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
135
|
+
a,b,x,y
|
136
|
+
0,1,3,7
|
137
|
+
4,5,b,f
|
138
|
+
End
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_value_field
|
142
|
+
File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
|
143
|
+
a,b,variable,bar
|
144
|
+
0,1,x,3
|
145
|
+
0,1,y,7
|
146
|
+
4,5,x,b
|
147
|
+
4,5,y,f
|
148
|
+
End
|
149
|
+
Tb::Cmd.main_unmelt(['-o', o="o.csv", '--value-field=bar', i])
|
150
|
+
assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
|
151
|
+
a,b,x,y
|
152
|
+
0,1,3,7
|
153
|
+
4,5,b,f
|
154
|
+
End
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.5'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-03-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'tb is a manipulation tool for table: CSV, TSV, JSON, etc.
|
15
15
|
|
@@ -20,6 +20,8 @@ description: ! 'tb is a manipulation tool for table: CSV, TSV, JSON, etc.
|
|
20
20
|
|
21
21
|
SQL like operations (join, group, etc.),
|
22
22
|
|
23
|
+
other table operations (gsub, rename, cross, melt, unmelt, etc.),
|
24
|
+
|
23
25
|
information extractions (git-log, svn-log, tar-tvf),
|
24
26
|
|
25
27
|
and more.
|
@@ -48,6 +50,7 @@ files:
|
|
48
50
|
- lib/tb/cmd_help.rb
|
49
51
|
- lib/tb/cmd_join.rb
|
50
52
|
- lib/tb/cmd_ls.rb
|
53
|
+
- lib/tb/cmd_melt.rb
|
51
54
|
- lib/tb/cmd_mheader.rb
|
52
55
|
- lib/tb/cmd_nest.rb
|
53
56
|
- lib/tb/cmd_newfield.rb
|
@@ -62,6 +65,7 @@ files:
|
|
62
65
|
- lib/tb/cmd_to_pp.rb
|
63
66
|
- lib/tb/cmd_to_tsv.rb
|
64
67
|
- lib/tb/cmd_to_yaml.rb
|
68
|
+
- lib/tb/cmd_unmelt.rb
|
65
69
|
- lib/tb/cmd_unnest.rb
|
66
70
|
- lib/tb/cmdmain.rb
|
67
71
|
- lib/tb/cmdtop.rb
|
@@ -109,6 +113,7 @@ files:
|
|
109
113
|
- test/test_cmd_help.rb
|
110
114
|
- test/test_cmd_join.rb
|
111
115
|
- test/test_cmd_ls.rb
|
116
|
+
- test/test_cmd_melt.rb
|
112
117
|
- test/test_cmd_mheader.rb
|
113
118
|
- test/test_cmd_nest.rb
|
114
119
|
- test/test_cmd_newfield.rb
|
@@ -123,6 +128,7 @@ files:
|
|
123
128
|
- test/test_cmd_to_pp.rb
|
124
129
|
- test/test_cmd_to_tsv.rb
|
125
130
|
- test/test_cmd_to_yaml.rb
|
131
|
+
- test/test_cmd_unmelt.rb
|
126
132
|
- test/test_cmd_unnest.rb
|
127
133
|
- test/test_cmdtty.rb
|
128
134
|
- test/test_cmdutil.rb
|
@@ -181,6 +187,7 @@ test_files:
|
|
181
187
|
- test/test_cmd_help.rb
|
182
188
|
- test/test_cmd_join.rb
|
183
189
|
- test/test_cmd_ls.rb
|
190
|
+
- test/test_cmd_melt.rb
|
184
191
|
- test/test_cmd_mheader.rb
|
185
192
|
- test/test_cmd_nest.rb
|
186
193
|
- test/test_cmd_newfield.rb
|
@@ -195,6 +202,7 @@ test_files:
|
|
195
202
|
- test/test_cmd_to_pp.rb
|
196
203
|
- test/test_cmd_to_tsv.rb
|
197
204
|
- test/test_cmd_to_yaml.rb
|
205
|
+
- test/test_cmd_unmelt.rb
|
198
206
|
- test/test_cmd_unnest.rb
|
199
207
|
- test/test_cmdtty.rb
|
200
208
|
- test/test_cmdutil.rb
|