xls 0.1.1 → 0.1.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.
- 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
|