xls 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/xls +29 -20
- data/lib/xls/enumerator.rb +32 -9
- data/lib/xls/version.rb +1 -1
- data/test/enumerator_test.rb +35 -16
- data/test/test_helper.rb +2 -2
- metadata +1 -1
data/bin/xls
CHANGED
@@ -16,29 +16,38 @@ program :description, 'A command line utility for working with data in Excel.'
|
|
16
16
|
#
|
17
17
|
################################################################################
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
def build_enumerator_command(action, description)
|
20
|
+
command action do |c|
|
21
|
+
c.syntax = "xls #{action.to_s} FILE"
|
22
|
+
c.description = description
|
23
|
+
c.option('-s SELECTION', 'The Excel style selection to work within.')
|
24
|
+
c.option('-e CODE', 'The code to execute for each cell.')
|
25
|
+
c.option('--use-headers', 'Whether to use the first row as a header row.')
|
26
|
+
c.option('--output FILE', 'The path to the output file.') unless action == :each
|
27
|
+
c.when_called do|args, options|
|
28
|
+
abort("Input file required") if args.length == 0
|
29
|
+
abort("Output file required") if action != :each && options.e.nil?
|
30
|
+
abort("Execution code required") if options.e.nil?
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
# Run enumerator.
|
33
|
+
workbook = Spreadsheet.open(args.first)
|
34
|
+
enumerator = Xls::Enumerator.new()
|
35
|
+
enumerator.action = action
|
36
|
+
enumerator.use_headers = options.use_headers
|
37
|
+
enumerator.selection = Xls::Selection.parse(options.s.upcase) unless options.s.nil?
|
38
|
+
enumerator.proc = options.e
|
39
|
+
enumerator.process(workbook)
|
40
|
+
|
41
|
+
# Write output to file.
|
42
|
+
workbook.write(options.output) if action != :each
|
43
|
+
end
|
39
44
|
end
|
40
45
|
end
|
41
46
|
|
47
|
+
build_enumerator_command(:each, 'Executes Ruby code on each cell of a workbook.')
|
48
|
+
build_enumerator_command(:map, 'Maps output of Ruby code on each cell.')
|
49
|
+
build_enumerator_command(:select, 'Keeps each cell where the code evaluates true.')
|
50
|
+
build_enumerator_command(:reject, 'Keeps each cell where the code evaluates false.')
|
42
51
|
|
43
52
|
|
44
53
|
|
@@ -50,7 +59,7 @@ end
|
|
50
59
|
|
51
60
|
command :columnize do |c|
|
52
61
|
c.syntax = 'xls columnize FILE'
|
53
|
-
c.description = 'Converts
|
62
|
+
c.description = 'Converts table into 2-column rows table.'
|
54
63
|
c.option('--output FILE', 'The path to the output file.')
|
55
64
|
c.option('--fixed-columns COLUMNS', 'The columns that should stay fixed.')
|
56
65
|
c.when_called do|args, options|
|
data/lib/xls/enumerator.rb
CHANGED
@@ -8,7 +8,7 @@ class Xls
|
|
8
8
|
|
9
9
|
def initialize(options={})
|
10
10
|
self.selection = options[:selection]
|
11
|
-
self.
|
11
|
+
self.proc = options[:proc]
|
12
12
|
end
|
13
13
|
|
14
14
|
|
@@ -18,12 +18,18 @@ class Xls
|
|
18
18
|
#
|
19
19
|
############################################################################
|
20
20
|
|
21
|
+
# The action to perform on each cell of data (:none, :map, :select, :reject).
|
22
|
+
attr_accessor :action
|
23
|
+
|
21
24
|
# The selection to enumerate over.
|
22
25
|
attr_accessor :selection
|
23
26
|
|
24
|
-
#
|
25
|
-
attr_accessor :
|
26
|
-
|
27
|
+
# The proc to run on each cell.
|
28
|
+
attr_accessor :proc
|
29
|
+
|
30
|
+
# A flag stating if headers should be used.
|
31
|
+
attr_accessor :use_headers
|
32
|
+
|
27
33
|
|
28
34
|
############################################################################
|
29
35
|
#
|
@@ -31,25 +37,32 @@ class Xls
|
|
31
37
|
#
|
32
38
|
############################################################################
|
33
39
|
|
34
|
-
# Executes a
|
40
|
+
# Executes a proc on each cell in a selection.
|
35
41
|
#
|
36
42
|
# @param [Workbook] input The input workbook.
|
37
43
|
def process(input)
|
38
44
|
# Loop over each worksheet.
|
39
45
|
(0...input.sheet_count).each do |sheet_index|
|
40
46
|
sheet = input.worksheet(sheet_index)
|
47
|
+
headers = sheet.row(0)
|
41
48
|
|
42
49
|
# Loop over each row.
|
43
|
-
sheet.each do |row|
|
50
|
+
sheet.each(use_headers ? 1 : 0) do |row|
|
44
51
|
next unless selection.nil? || selection.rows.nil? || selection.rows.cover?(row.idx)
|
45
52
|
|
46
53
|
# Loop over each cell.
|
47
54
|
row.each_with_index do |cell, col_index|
|
48
55
|
next unless selection.nil? || selection.columns.nil? || selection.columns.cover?(col_index)
|
49
56
|
|
50
|
-
# Run
|
51
|
-
|
52
|
-
|
57
|
+
# Run proc and retrieve the results.
|
58
|
+
header = (use_headers ? headers[col_index].to_s : '')
|
59
|
+
result = cell_binding(cell.to_s, col_index, row.idx, header).eval(proc)
|
60
|
+
|
61
|
+
# Perform an action if one is specified.
|
62
|
+
case action
|
63
|
+
when :map then row[col_index] = result.to_s
|
64
|
+
when :select then row[col_index] = '' unless result
|
65
|
+
when :reject then row[col_index] = '' if result
|
53
66
|
end
|
54
67
|
end
|
55
68
|
end
|
@@ -57,5 +70,15 @@ class Xls
|
|
57
70
|
|
58
71
|
return nil
|
59
72
|
end
|
73
|
+
|
74
|
+
|
75
|
+
####################################
|
76
|
+
# Bindings
|
77
|
+
####################################
|
78
|
+
|
79
|
+
# Returns the context in which cell procs are run.
|
80
|
+
def cell_binding(text, col, row, header)
|
81
|
+
return binding
|
82
|
+
end
|
60
83
|
end
|
61
84
|
end
|
data/lib/xls/version.rb
CHANGED
data/test/enumerator_test.rb
CHANGED
@@ -9,23 +9,42 @@ class TestEnumerator < MiniTest::Unit::TestCase
|
|
9
9
|
# Execute
|
10
10
|
######################################
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
@enumerator.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
def test_map_enumerate
|
13
|
+
workbook = Spreadsheet.open('fixtures/enumerator/basic.xls')
|
14
|
+
@enumerator.action = :map
|
15
|
+
@enumerator.proc = "text.to_s + '!'"
|
16
|
+
@enumerator.process(workbook)
|
17
|
+
assert_worksheet [
|
18
|
+
['a!', 'b!', 'c!'],
|
19
|
+
['d!', 'e!', 'f!'],
|
20
|
+
['g!', 'h!', 'i!'],
|
21
|
+
],
|
22
|
+
workbook.worksheet(0)
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
@enumerator.
|
27
|
-
@enumerator.
|
28
|
-
|
29
|
-
|
25
|
+
def test_select_enumerate
|
26
|
+
workbook = Spreadsheet.open('fixtures/enumerator/basic.xls')
|
27
|
+
@enumerator.action = :select
|
28
|
+
@enumerator.proc = "text.to_s.ord % 2 == 0"
|
29
|
+
@enumerator.process(workbook)
|
30
|
+
assert_worksheet [
|
31
|
+
['', 'b', ''],
|
32
|
+
['d', '', 'f'],
|
33
|
+
['', 'h', ''],
|
34
|
+
],
|
35
|
+
workbook.worksheet(0)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_reject_enumerate
|
39
|
+
workbook = Spreadsheet.open('fixtures/enumerator/basic.xls')
|
40
|
+
@enumerator.action = :reject
|
41
|
+
@enumerator.proc = "text.to_s.ord % 2 == 0"
|
42
|
+
@enumerator.process(workbook)
|
43
|
+
assert_worksheet [
|
44
|
+
['a', '', 'c'],
|
45
|
+
['', 'e', ''],
|
46
|
+
['g', '', 'i'],
|
47
|
+
],
|
48
|
+
workbook.worksheet(0)
|
30
49
|
end
|
31
50
|
end
|
data/test/test_helper.rb
CHANGED
@@ -10,8 +10,8 @@ class MiniTest::Unit::TestCase
|
|
10
10
|
worksheet.each do |row|
|
11
11
|
act << row.map {|cell| cell.to_s.strip }
|
12
12
|
end
|
13
|
-
exp = exp.map {|row| row.map {|cell| '%-10s' % cell.to_s}.join('').
|
14
|
-
act = act.map {|row| row.map {|cell| '%-10s' % cell.to_s}.join('').
|
13
|
+
exp = exp.map {|row| row.map {|cell| '%-10s' % cell.to_s}.join('').rstrip}.join("\n")
|
14
|
+
act = act.map {|row| row.map {|cell| '%-10s' % cell.to_s}.join('').rstrip}.join("\n")
|
15
15
|
assert_equal(exp, act, msg)
|
16
16
|
end
|
17
17
|
end
|