xls 0.1.0 → 0.1.1

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 CHANGED
@@ -11,7 +11,41 @@ program :description, 'A command line utility for working with data in Excel.'
11
11
 
12
12
 
13
13
  ################################################################################
14
- # Processing
14
+ #
15
+ # Enumeration
16
+ #
17
+ ################################################################################
18
+
19
+ command :"enumerate" do |c|
20
+ c.syntax = 'xls enumerate FILE'
21
+ c.description = 'Executes Ruby code on each cell of a workbook.'
22
+ c.option('--selection SELECTION', 'The Excel style selection to work within.')
23
+ c.option('-e CODE', 'The code to execute for each cell.')
24
+ c.when_called do|args, options|
25
+ # Open input file.
26
+ abort("Input file required") if args.length == 0
27
+ workbook = Spreadsheet.open(args.first)
28
+
29
+ # Convert Ruby source to procs.
30
+ abort("Enumerator code required") if options.e.nil?
31
+ procs = options.e.is_a?(String) ? [options.e] : options.e
32
+ procs.map! {|source| eval("lambda { |cell, col_index, row_index| #{source} }")}
33
+
34
+ # Run enumerator.
35
+ enumerator = Xls::Enumerator.new()
36
+ enumerator.selection = Xls::Selection.parse(options.selection.upcase) unless options.selection.nil?
37
+ enumerator.procs = procs
38
+ enumerator.process(workbook)
39
+ end
40
+ end
41
+
42
+
43
+
44
+
45
+ ################################################################################
46
+ #
47
+ # Transformation
48
+ #
15
49
  ################################################################################
16
50
 
17
51
  command :columnize do |c|
@@ -21,15 +55,16 @@ command :columnize do |c|
21
55
  c.option('--fixed-columns COLUMNS', 'The columns that should stay fixed.')
22
56
  c.when_called do|args, options|
23
57
  # Open input file.
58
+ abort("Output file required") if options.output.nil?
24
59
  abort("Input file required") if args.length == 0
25
- workbook = Spreadsheet.open(args.first)
60
+ input = Spreadsheet.open(args.first)
26
61
 
27
62
  # Run columnizer.
28
63
  columnizer = Xls::Columnizer.new()
29
64
  columnizer.fixed_columns = options.fixed_columns.to_s.split(",")
30
- columnizer.execute(workbook)
65
+ output = columnizer.process(input)
31
66
 
32
- # Write output.
33
- workbook.write(options.output || $stdout)
67
+ # Write output to file.
68
+ output.write(options.output)
34
69
  end
35
70
  end
data/lib/xls.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'spreadsheet'
2
+ require 'tempfile'
2
3
 
3
4
  require 'xls/columnizer'
5
+ require 'xls/enumerator'
6
+ require 'xls/selection'
4
7
  require 'xls/version'
5
8
 
6
9
  class Xls
@@ -0,0 +1,61 @@
1
+ class Xls
2
+ class Enumerator
3
+ ############################################################################
4
+ #
5
+ # Constructor
6
+ #
7
+ ############################################################################
8
+
9
+ def initialize(options={})
10
+ self.selection = options[:selection]
11
+ self.procs = options[:procs] || []
12
+ end
13
+
14
+
15
+ ############################################################################
16
+ #
17
+ # Attributes
18
+ #
19
+ ############################################################################
20
+
21
+ # The selection to enumerate over.
22
+ attr_accessor :selection
23
+
24
+ # A list of procs to run on each cell.
25
+ attr_accessor :procs
26
+
27
+
28
+ ############################################################################
29
+ #
30
+ # Methods
31
+ #
32
+ ############################################################################
33
+
34
+ # Executes a set of procs on each cell in a selection.
35
+ #
36
+ # @param [Workbook] input The input workbook.
37
+ def process(input)
38
+ # Loop over each worksheet.
39
+ (0...input.sheet_count).each do |sheet_index|
40
+ sheet = input.worksheet(sheet_index)
41
+
42
+ # Loop over each row.
43
+ sheet.each do |row|
44
+ next unless selection.nil? || selection.rows.nil? || selection.rows.cover?(row.idx)
45
+
46
+ # Loop over each cell.
47
+ row.each_with_index do |cell, col_index|
48
+ next unless selection.nil? || selection.columns.nil? || selection.columns.cover?(col_index)
49
+
50
+ # Run each proc.
51
+ procs.each do |proc|
52
+ proc.call(cell.to_s, col_index, row.idx)
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ return nil
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,99 @@
1
+ class Xls
2
+ class Selection
3
+ ############################################################################
4
+ #
5
+ # Error Classes
6
+ #
7
+ ############################################################################
8
+
9
+ class SelectionFormatError < StandardError; end
10
+
11
+
12
+ ############################################################################
13
+ #
14
+ # Static Methods
15
+ #
16
+ ############################################################################
17
+
18
+ # Parses an Excel style [COLUMN][ROW]:[COLUMN][ROW] format into a
19
+ # selection.
20
+ #
21
+ # @param [String] str The Excel-style selection.
22
+ #
23
+ # @return [Selection] A selection object.
24
+ def self.parse(str)
25
+ m, tl_col, tl_row, br_col, br_row = *str.to_s.match(/^([A-Z]+)?(\d+)?(?::([A-Z]+)?(\d+)?)?$/)
26
+ raise SelectionFormatError.new("Invalid selection: #{str}") if m.nil?
27
+
28
+ # Default bottom-right for single cell selection.
29
+ br_col = tl_col if br_col.nil?
30
+ br_row = tl_row if br_row.nil?
31
+
32
+ # Convert column letters to numbers.
33
+ columns = nil
34
+ if !tl_col.nil? && !br_col.nil?
35
+ tl_col = col_to_index(tl_col)
36
+ br_col = col_to_index(br_col)
37
+ tl_col, br_col = [tl_col, br_col].min, [tl_col, br_col].max
38
+ columns = (tl_col..br_col)
39
+ end
40
+
41
+ # Convert rows to zero-based indices.
42
+ rows = nil
43
+ if !tl_row.nil? && !br_row.nil?
44
+ tl_row = tl_row.to_i - 1
45
+ br_row = br_row.to_i - 1
46
+ tl_row, br_row = [tl_row, br_row].min, [tl_row, br_row].max
47
+ rows = (tl_row..br_row)
48
+ end
49
+
50
+ # Return a selection object.
51
+ return Xls::Selection.new(columns, rows)
52
+ end
53
+
54
+ # Converts column letters to integer indices.
55
+ def self.col_to_index(letters)
56
+ value = 0
57
+ letters.upcase.split('').each_with_index do |letter, index|
58
+ value = value + ((letter.ord - "A".ord) * (26 ** index))
59
+ end
60
+ return value
61
+ end
62
+
63
+
64
+ ############################################################################
65
+ #
66
+ # Constructor
67
+ #
68
+ ############################################################################
69
+
70
+ def initialize(columns, rows)
71
+ self.columns = columns
72
+ self.rows = rows
73
+ end
74
+
75
+
76
+ ############################################################################
77
+ #
78
+ # Attributes
79
+ #
80
+ ############################################################################
81
+
82
+ # A range of row indices that the selection covers.
83
+ attr_accessor :rows
84
+
85
+ # A range of column indices that the selection covers.
86
+ attr_accessor :columns
87
+
88
+ # An array of indicies (top-level column, top-left row, bottom-right
89
+ # column, bottom-right row).
90
+ def indices
91
+ return [
92
+ columns.nil? ? nil : columns.begin,
93
+ rows.nil? ? nil : rows.begin,
94
+ columns.nil? ? nil : columns.end,
95
+ rows.nil? ? nil : rows.end
96
+ ]
97
+ end
98
+ end
99
+ end
@@ -1,3 +1,3 @@
1
1
  class Xls
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ class TestEnumerator < MiniTest::Unit::TestCase
4
+ def setup
5
+ @enumerator = Xls::Enumerator.new
6
+ end
7
+
8
+ ######################################
9
+ # Execute
10
+ ######################################
11
+
12
+ def test_enumerate
13
+ my_arr = []
14
+ input = Spreadsheet.open('fixtures/enumerator/basic.xls')
15
+ @enumerator.procs = [
16
+ lambda {|cell, col, row| my_arr << cell.to_s},
17
+ lambda {|cell, col, row| my_arr << '0'},
18
+ ]
19
+ @enumerator.process(input)
20
+ assert_equal ["a", "0", "b", "0", "c", "0", "d", "0", "e", "0", "f", "0", "g", "0", "h", "0", "i", "0"], my_arr
21
+ end
22
+
23
+ def test_enumerate_with_selection
24
+ my_arr = []
25
+ input = Spreadsheet.open('fixtures/enumerator/basic.xls')
26
+ @enumerator.procs = [lambda {|cell, col, row| my_arr << cell.to_s}]
27
+ @enumerator.selection = Xls::Selection.parse("B2:C3")
28
+ @enumerator.process(input)
29
+ assert_equal ['e', 'f', 'h', 'i'], my_arr
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ class TestSelection < MiniTest::Unit::TestCase
4
+ ######################################
5
+ # Parsing
6
+ ######################################
7
+
8
+ def test_parse_top_left_cell
9
+ assert_equal [0, 0, 0, 0], Xls::Selection.parse("A1").indices
10
+ assert_equal [0, 0, 0, 0], Xls::Selection.parse("A1:A1").indices
11
+ end
12
+
13
+ def test_parse_single_cell
14
+ assert_equal [2, 7, 2, 7], Xls::Selection.parse("C8").indices
15
+ end
16
+
17
+ def test_parse_range
18
+ assert_equal [2, 7, 4, 9], Xls::Selection.parse("C8:E10").indices
19
+ end
20
+
21
+ def test_parse_inverse_range
22
+ assert_equal [2, 7, 4, 9], Xls::Selection.parse("E10:C8").indices
23
+ assert_equal [2, 7, 4, 9], Xls::Selection.parse("C10:E8").indices
24
+ end
25
+
26
+ def test_parse_columns_only
27
+ assert_equal [2, nil, 2, nil], Xls::Selection.parse("C").indices
28
+ assert_equal [2, nil, 4, nil], Xls::Selection.parse("C:E").indices
29
+ end
30
+
31
+ def test_parse_rows_only
32
+ assert_equal [nil, 2, nil, 2], Xls::Selection.parse("3").indices
33
+ assert_equal [nil, 4, nil, 7], Xls::Selection.parse("5:8").indices
34
+ end
35
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xls
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -132,10 +132,14 @@ extensions: []
132
132
  extra_rdoc_files: []
133
133
  files:
134
134
  - lib/xls/columnizer.rb
135
+ - lib/xls/enumerator.rb
136
+ - lib/xls/selection.rb
135
137
  - lib/xls/version.rb
136
138
  - lib/xls.rb
137
139
  - README.md
138
140
  - test/columnizer_test.rb
141
+ - test/enumerator_test.rb
142
+ - test/selection_test.rb
139
143
  - test/test_helper.rb
140
144
  - bin/xls
141
145
  homepage: http://github.com/benbjohnson/xls
@@ -164,4 +168,6 @@ specification_version: 3
164
168
  summary: A command line utilty for working with data in Excel.
165
169
  test_files:
166
170
  - test/columnizer_test.rb
171
+ - test/enumerator_test.rb
172
+ - test/selection_test.rb
167
173
  - test/test_helper.rb