rexcel 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.
@@ -1,16 +1,37 @@
1
1
  #encoding: cp1252
2
+ =begin rdoc
3
+ Create an excel file.
4
+
5
+ Creates
6
+ * test.xml
7
+ * test_xml.xls
8
+ * test.xls
9
+ * test.xlsx
10
+ =end
2
11
  $:.unshift('../lib') if $0 == __FILE__
3
12
  require 'rexcel'
4
13
  Excel::LOGGER.level = Log4r::DEBUG
5
14
 
6
15
  wb = Excel::Workbook.new()
7
16
 
8
- wb << [1,2,3,4,5,6,7]
9
- wb << [:a, :b, :c, :d, :e, :f]
10
- wb << ['', '�', '�', '�', '�', '�', '�']
11
- wb << ['�', '�', '�', '�', '�', '�', '�'].map{|x| x.encode('utf-8')}
12
-
13
-
17
+ #Sheet one with numbers, letters and umlauts.
18
+ wb << ['Integer', 1,2,3,4,5,6,7]
19
+ wb << ['Float', 1.1,2.2,3.3,4.4,5.5,6.6,7.7]
20
+ wb << %w{Letters a b c d e f}
21
+ begin
22
+ wb << [:Symbols, :a, :b, :c, :d, :e, :f]
23
+ rescue
24
+ puts "Warning: Adding symbols have problems"
25
+ puts "Please report the problem with the following informations:"
26
+ puts "ruby #{RUBY_VERSION}"
27
+ puts "#{Excel::Excel.instance.xl.Name} #{Excel::Excel.instance.xl.version}"
28
+ puts Excel::Excel.instance.xl.OperatingSystem
29
+ puts "Thanks"
30
+ end
31
+ wb << ['Umlauts', '�', '�', '�', '�', '�', '�', '�']
32
+ wb << ['Umlauts/UTF8', '�', '�', '�', '�', '�', '�', '�'].map{|x| x.encode('utf-8')}
33
+
34
+ #Sheet 2: Some text formatting.
14
35
  wb << ws = Excel::Worksheet.new('Formats')
15
36
  ws << row = Excel::Row.new
16
37
  row << cell = Excel::Cell.new(1)
@@ -27,6 +48,7 @@ row << cell = Excel::Cell.new('grau text', :style => style)
27
48
 
28
49
 
29
50
  wb.save('test.xml')
51
+ #Create xls based on xml.
30
52
  wb.save('test_xml.xls', 'test.xml')
31
53
  wb.save('test.xls')
32
54
  wb.save('test.xlsx')
@@ -2,6 +2,7 @@ $:.unshift('../lib') if $0 == __FILE__
2
2
  require 'rexcel'
3
3
  Excel::LOGGER.level = Log4r::DEBUG
4
4
 
5
+ wb = Excel::Workbook.new()
5
6
  wb << ws = Excel::Worksheet.new('Formats')
6
7
  ws << row = Excel::Row.new
7
8
  row << cell = Excel::Cell.new(1)
@@ -0,0 +1,52 @@
1
+ #encoding: cp1252
2
+ =begin rdoc
3
+ Create a sqlite-DB and copy the data from table to Excel.
4
+
5
+ This test needs a SQLITE-Installation
6
+ =end
7
+ $:.unshift('../lib') if $0 == __FILE__
8
+ require 'rexcel'
9
+ Excel::LOGGER.level = Log4r::DEBUG
10
+
11
+ require 'sequel'
12
+
13
+ begin
14
+ DB = Sequel.sqlite()
15
+ rescue Sequel::AdapterNotFound
16
+ puts "No sqlite found - test not possible"
17
+ exit
18
+ end
19
+ #
20
+ #Fill DB with data
21
+ #
22
+ DB.create_table :testtable do
23
+ column :column_1, :integer
24
+ column :column_2, :integer
25
+ column :column_3, :integer
26
+ end
27
+ 1.upto(100){|i|
28
+ DB[:testtable].insert( i, i * 2, i ** 2 )
29
+ }
30
+
31
+
32
+ #
33
+ #Create Excel from DB
34
+ #
35
+ WB = Excel::Workbook.new()
36
+ WB << WS = Excel::Worksheet.new('Formats')
37
+ WB << style = Excel::Style.new(:column, :color => 14)
38
+
39
+ DB[:testtable].columns.each{|col|
40
+ WS << Excel::Column.new(col, :style => style)
41
+ }
42
+ x = WS.add_title_row
43
+ p x
44
+ DB[:testtable].all{| dataset|
45
+ WS << dataset
46
+ }
47
+
48
+
49
+ WB.save('test_sequel.xml')
50
+ WB.save('test_sequel_xml.xls', 'test_sequel.xml')
51
+ #~ WB.save('test_sequel.xls')
52
+ #~ WB.save('test_sequel.xlsx')
@@ -3,65 +3,10 @@
3
3
  :title:Create Excel-documents with ruby
4
4
  Build Excel documents via win32ole.
5
5
 
6
- ==Example 1 (Simple Spreadsheet):
7
- require 'excel'
8
-
9
- wb = Excel::Workbook.new()
10
-
11
- wb << [1,2,3,4,5,6,7]
12
- wb << [:a, :b, :c, :d, :e, :f]
13
-
14
- #Save as excel spreadsheets
15
- wb.save('test.xls') #excel97_2003_format
16
- wb.save('test.xlsx') #Excel 2007
17
-
18
- wb.save('test.xml') #Microsoft Office Word 2003 XML Format
19
- wb.save('test.xls', 'test.xml')#Build xls from xlm
20
-
21
- ==Example 2 (Access to Worksheets, Rows and Cells):
22
- require 'excel'
23
-
24
- wb = Excel::Workbook.new()
25
-
26
- wb << ws = Excel::Worksheet.new('My Spreadsheet')
27
- ws << row = Excel::Row.new()
28
- row << Excel::Cell.new(1)
29
- row << Excel::Cell.new(2)
30
- row << Excel::Cell.new(4)
31
-
32
- #Save as excel spreadsheets
33
- wb.save('test.xls') #excel97_2003_format
34
- wb.save('test.xlsx') #Excel 2007
35
-
36
- wb.save('test.xml') #Microsoft Office Word 2003 XML Format
37
- wb.save('test.xls', 'test.xml')#Build xls from xlm
38
-
39
- ==Example 3 (With Format options):
40
- require 'excel'
41
-
42
- wb = Excel::Workbook.new()
43
- wb << style = Excel::Style.new('fett', :bold => true)
44
-
45
- wb << ws = Excel::Worksheet.new('My Spreadsheet')
46
- ws << row = Excel::Row.new()
47
- row << Excel::Cell.new(1)
48
- row << Excel::Cell.new(2)
49
- row << Excel::Cell.new(999, :style => style)
50
-
51
- #Save as excel spreadsheets
52
- wb.save('test.xls') #excel97_2003_format
53
- wb.save('test.xlsx') #Excel 2007
54
-
55
- wb.save('test.xml') #Microsoft Office Word 2003 XML Format
56
- wb.save('test.xls', 'test.xml')#Build xls from xlm
57
-
58
-
59
- 'Big' spreadsheets needs a long time to be build.
60
- (20000 lines need 2 hours)
61
-
62
- When you build the xml, and then the xls from xlm, the wor is done in seconds.
63
-
64
6
 
7
+ This gem was tested with
8
+ * Windows XP + MS Excel 2007 (Version 12)
9
+ * Windows 7 + MS Excel 2007 (Version 12)
65
10
  =end
66
11
  require 'win32ole'
67
12
  require 'log4r'
@@ -71,7 +16,7 @@ require 'singleton'
71
16
  Frame for Excel-tools.
72
17
  =end
73
18
  module Excel
74
- VERSION = '0.1.1'
19
+ VERSION = '0.1.2'
75
20
 
76
21
  LOGGER = Log4r::Logger.new( name )#, Log4r::DEBUG, :trunc => true )
77
22
  LOGGER.outputters << Log4r::StdoutOutputter.new('std', :level => Log4r::WARN )
@@ -125,11 +70,16 @@ Exceptionclass for empty Workbook, Worksheet, Row
125
70
  end #module Excel
126
71
 
127
72
  =begin
128
- makes problems in unit test
73
+ Close excel instance
74
+
75
+ The double at_exit is needed.
76
+ Else you get problems in unit test
129
77
  =end
130
- #~ at_exit {
131
- #~ Excel::Excel.instance.close
132
- #~ }
78
+ at_exit {
79
+ at_exit {
80
+ Excel::Excel.instance.close
81
+ }
82
+ }
133
83
 
134
84
 
135
85
  require 'rexcel/workbook'
@@ -32,7 +32,10 @@ Options:
32
32
  case key
33
33
  when :log
34
34
  when :string
35
- @type = 'String' if value
35
+ if value
36
+ @type = 'String'
37
+ content = content.to_s
38
+ end
36
39
  when :style
37
40
  @style = value
38
41
  raise ArgumentError, "Style is no Excel::Style" unless @style.is_a?(Style)
@@ -128,6 +131,36 @@ Receives a OLE-cell and optional a row.
128
131
  cell
129
132
  end #to_xls
130
133
 
134
+ =begin rdoc
135
+ Fill an CSV-Field.
136
+
137
+ The output is set depending on cell content.
138
+ The csv-value is optimized to be used by Excel.
139
+
140
+ Examples for conversion:
141
+ * 1.1 -> ='1.1' (1.1 would become 1st January)
142
+
143
+ =end
144
+ def to_csv()
145
+ cellcontent = @content
146
+ case @type
147
+ when 'String' #Suppress automatic conversion from Excel
148
+ cellcontent = "\"#{@content}\"" if @content =~ /^\A[\s\d]/
149
+ when 'Number'
150
+ #~ cellcontent = "%.2f" % cellcontent if cellcontent.is_a?(Float)
151
+ #http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm#CSVAndExcel
152
+ cellcontent = "=\"#{cellcontent}\"" if cellcontent.is_a?(Float)
153
+ when 'DateTime'
154
+ @log.warn("Cell type DateTime not tested")
155
+ #avoid interpretation in excel, e.g. dates. does not work with numbers.
156
+ cellcontent = " #{cellcontent}"
157
+ when nil #no conversion
158
+ cellcontent = @content
159
+ else
160
+ raise ArgumentError, "#{self.class}##{__method__}: Undefined CSV type #{@type}"
161
+ end
162
+ cellcontent
163
+ end #to_csv
131
164
 
132
165
  end #class Cell
133
166
  end #module Excel
@@ -2,50 +2,50 @@ module Excel
2
2
  =begin rdoc
3
3
  A Row in the spreadsheet.
4
4
  =end
5
- class Row
5
+ class Row
6
6
  =begin rdoc
7
7
  Define a new row.
8
8
  =end
9
- def initialize( options = {})
10
- @log = options[:log] || LOGGER
11
- @columns = []
12
- options.each{|key,value|
13
- case key
14
- when :log
15
- when :style
16
- @style = value
17
- raise ArgumentError, "Style is no Excel::Style" unless @style.is_a?(Style)
18
- else
19
- @log.warn("Excel::Row: undefined option #{option}")
20
- end
21
- }
22
- end
23
- #Array with columns of this row
24
- attr_reader :columns
25
- #Style for the row. Is inherited to cells.
26
- attr_reader :style
9
+ def initialize( options = {})
10
+ @log = options[:log] || LOGGER
11
+ @columns = []
12
+ options.each{|key,value|
13
+ case key
14
+ when :log
15
+ when :style
16
+ @style = value
17
+ raise ArgumentError, "Style is no Excel::Style" unless @style.is_a?(Style)
18
+ else
19
+ @log.warn("Excel::Row: undefined option #{option}")
20
+ end
21
+ }
22
+ end
23
+ #Array with columns of this row
24
+ attr_reader :columns
25
+ #Style for the row. Is inherited to cells.
26
+ attr_reader :style
27
27
  =begin rdoc
28
28
  Add content to the Row.
29
29
  =end
30
- def << (insertion)
31
- case insertion
32
- when Cell
33
- @columns << insertion
34
- when Array
35
- insertion.each{|value|
36
- @columns << Cell.new(value)
37
- }
38
- when Hash
39
- @log.error("Excel::Row: Hashs not supported")
40
- raise ArgumentError, "Excel::Row#<<: Hashs not supported"
41
- #fixme: if connectuion to worksheet, use columns.
42
- when Row, Worksheet, Workbook
43
- raise ArgumentError, "Excel::Row#<<: #{insertion.class} not supported"
44
- else
45
- @columns << Cell.new(insertion)
46
- end
47
- self #for usage like " << (Excel::Row.new() << 'a')"
48
- end
30
+ def << (insertion)
31
+ case insertion
32
+ when Cell
33
+ @columns << insertion
34
+ when Array
35
+ insertion.each{|value|
36
+ @columns << Cell.new(value)
37
+ }
38
+ when Hash
39
+ @log.error("Excel::Row: Hashs not supported")
40
+ raise ArgumentError, "Excel::Row#<<: Hashs not supported"
41
+ #fixme: if connectuion to worksheet, use columns.
42
+ when Row, Worksheet, Workbook
43
+ raise ArgumentError, "Excel::Row#<<: #{insertion.class} not supported"
44
+ else
45
+ @columns << Cell.new(insertion)
46
+ end
47
+ self #for usage like " << (Excel::Row.new() << 'a')"
48
+ end
49
49
  =begin rdoc
50
50
  Build the xml a work sheet row,
51
51
 
@@ -53,23 +53,45 @@ ns must be a method-object to implement the namespace definitions.
53
53
 
54
54
  Format options (bold, italic, colors) are forwarded to cells.
55
55
  =end
56
- def to_xml(xmlbuilder, ns)
57
- raise EmptyError, "Row without content" if @columns.empty?
56
+ def to_xml(xmlbuilder, ns)
57
+ raise EmptyError, "Row without content" if @columns.empty?
58
+
59
+ #Build options
60
+ row_options = {}
61
+ if @style
62
+ row_options[ns.call('StyleID')] = @style.style_id
63
+ end
64
+
65
+ xmlbuilder[ns.call].Row( row_options ){
66
+ @columns.each{|column|
67
+ column.to_xml(xmlbuilder, ns, self)
68
+ }
69
+ }
70
+ end #to_xml
71
+ =begin rdoc
72
+ Build the row for csv.
73
+
74
+ Details on options see Rexcel::Worksheet#to_csv
75
+ =end
76
+ def to_csv(options = {})
77
+
78
+ @log.debug("Prepare csv-row")
79
+ #Set options defaults
80
+ options[:sep] ||= ";"
81
+
82
+ csv_row = []
83
+ self.columns.each_with_index{|col, colnum|
84
+ @log.debug("Prepare cell #{colnum} for csv-output") if @log.debug?
85
+ csv_row << col.to_csv()
86
+ raise ArgumentError, "Cell separator <#{options[:sep]}> is part of the data #{csv_row.last.inspect}" if csv_row.last.to_s.include?(options[:sep])
87
+
88
+ }#columns in row
89
+
90
+ csv_row.join(options[:sep])
91
+ end #build_excel_csv
58
92
 
59
- #Build options
60
- row_options = {}
61
- if @style
62
- row_options[ns.call('StyleID')] = @style.style_id
93
+ def inspect
94
+ "#<Excel::Row:#{object_id} #{columns.size} Columns>"
63
95
  end
64
-
65
- xmlbuilder[ns.call].Row( row_options ){
66
- @columns.each{|column|
67
- column.to_xml(xmlbuilder, ns, self)
68
- }
69
- }
70
- end #to_xml
71
- def inspect
72
- "#<Excel::Row:#{object_id} #{columns.size} Columns>"
73
- end
74
- end #class Row
96
+ end #class Row
75
97
  end #module Excel
@@ -68,69 +68,144 @@ If no actual worksheet is available, a new worksheet i created.
68
68
  =begin rdoc
69
69
  Build the workbook via OLE.
70
70
 
71
- If a xml-source is available, it is used to build the Excel Workbook.
71
+ The excel may be build in two ways.
72
72
 
73
+ * from internal data (previous with Workbook#<< added data)
74
+ * from a xml file
75
+
76
+ ==Internal data
73
77
  If no xml-source is available, the internal data are taken to
74
78
  build the xls.
79
+
75
80
  This may take some time...
76
81
 
82
+ ==Source-reference
83
+ If a reference to a source is available,
84
+ this reference is used to build the Excel Workbook.
85
+
86
+ This source reference may be
87
+ * xml
88
+ * csv
89
+
90
+ You may first save the data as xml and then
91
+ use the xml to build a xls(x).
92
+
77
93
  For big files, it is recommended to use the way via xml.
78
94
 
79
95
  =end
80
- def prepare_xls(xml_source = nil)
96
+ def prepare_xls(source_reference = nil)
81
97
 
82
98
  wb = nil
83
- if xml_source
84
- if File.exist?(xml_source)
85
- @log.info("Use existing #{xml_source}")
86
- wb = Excel.instance.xl.Workbooks.OpenXML(File.expand_path(xml_source))
87
- else
88
- @log.fatal("#{__method__} #{xml_source} missing")
89
- raise ArgumentError, 'Source data missing'
90
- end
91
- return wb
92
- end
93
-
94
- @log.info("Create xls-Workbook")
95
- wb = Excel.instance.xl.Workbooks.Add #Includes 3 worksheets
96
- #Delete unused sheets.
97
- wb.ActiveSheet.delete
98
- wb.ActiveSheet.delete
99
-
100
- @log.info("Add styles to document") unless @styles.empty?
101
- @styles.each{|name, style|
102
- @log.debug("Add style #{name} to document")
103
- style.to_xls(wb)
104
- }
105
-
106
- first = true
107
- #wb.Worksheets.Add appends new worksheets at begin -> reverse for creation to get right sequence.
108
- @worksheets.reverse.each{|worksheet|
109
- if first
110
- worksheet.to_xls(wb.ActiveSheet)
111
- first = false
112
- else
113
- worksheet.to_xls(wb.Worksheets.Add)
114
- end
115
- }
99
+ case source_reference
100
+ when /xml$/
101
+ if File.exist?(source_reference)
102
+ @log.info("Use existing #{source_reference}")
103
+ wb = Excel.instance.xl.Workbooks.OpenXML(File.expand_path(source_reference))
104
+ else
105
+ @log.fatal("#{__method__} #{source_reference} missing")
106
+ raise ArgumentError, 'Source data missing'
107
+ end
108
+ when /csv$/
109
+ if File.exist?(source_reference)
110
+ @log.info("Use existing #{source_reference}")
111
+ wb = Excel.instance.xl.Workbooks.Open(File.expand_path(source_reference))
112
+ else
113
+ @log.fatal("#{__method__} #{source_reference} missing")
114
+ raise ArgumentError, 'Source data missing'
115
+ end
116
+
117
+ when nil
118
+ @log.info("Create xls-Workbook")
119
+ wb = Excel.instance.xl.Workbooks.Add #Includes 3 worksheets
120
+ #Delete unused sheets.
121
+ wb.ActiveSheet.delete
122
+ wb.ActiveSheet.delete
123
+
124
+ @log.info("Add styles to document") unless @styles.empty?
125
+ @styles.each{|name, style|
126
+ @log.debug("Add style #{name} to document")
127
+ style.to_xls(wb)
128
+ }
129
+
130
+ first = true
131
+ #wb.Worksheets.Add appends new worksheets at begin -> reverse for creation to get right sequence.
132
+ @worksheets.reverse.each{|worksheet|
133
+ if first
134
+ worksheet.to_xls(wb.ActiveSheet)
135
+ first = false
136
+ else
137
+ worksheet.to_xls(wb.Worksheets.Add)
138
+ end
139
+ }
140
+ end #case source_reference
116
141
  wb
117
142
  end
118
143
 
119
144
  =begin rdoc
120
145
  Save the workbook.
121
146
 
122
- Filename must end with
123
- * xls
124
- * xlsx
125
- * xlm (Microsoft Office Word 2003 XML Format)
147
+ The filename must end with
148
+ * .xls
149
+ * .xlsx
150
+ * .xlm (Microsoft Office Word 2003 XML Format)
151
+ * .csv
152
+
153
+ Examples:
154
+ WB = Excel::Workbook.new()
155
+ # fill WB
156
+ WB.save('testfile.xls')
157
+ WB.save('testfile.xlsx')
158
+ WB.save('testfile.xml')
126
159
 
127
- The optional XML-file is used to create the workbook.
160
+
161
+ ==Save with reference to xml file
162
+ You may create the Excel file based on a xml file.
128
163
 
129
164
  The way Ruby Workbook -> xml -> xls[x] is faster then the
130
165
  direct xls[x] generation.
166
+
167
+ Example:
168
+ WB = Excel::Workbook.new()
169
+ # fill WB
170
+ WB.save('testfile.xml')
171
+ WB.save('testfile_xml.xls', 'testfile.xml')
172
+
173
+ ==Save with reference to csv file
174
+ You may create the Excel file based on a csv file.
175
+
176
+ * This method is only possible for workbooks with only one worksheets.
177
+ * Attention! You may get problems with field conversion.
178
+ * Therefor the way Ruby Workbook -> xml -> xls[x] is recommended.
179
+
180
+ Example:
181
+
182
+ WB = Excel::Workbook.new()
183
+ # fill WB
184
+ WB.save('testfile.csv')
185
+ WB.save('testfile_xml.xls', 'testfile.csv')
186
+
187
+ ==Save with implicit reference files
188
+ Instead the explicit usage of xml/csv-file, the save command
189
+ can do it implicit
190
+
191
+ Example:
192
+ WB = Excel::Workbook.new()
193
+ # fill WB
194
+ WB.save('testfile_xml.xls', :via_xml)
195
+ WB.save('testfile_xml.xls', :via_csv)
196
+
131
197
  =end
132
- def save(path, xml_file = nil)
198
+ def save(path, source_reference = nil)
133
199
 
200
+ case source_reference
201
+ when :via_xml
202
+ source_reference = path.sub(/\.[^\.]+/, '.xml')
203
+ save(source_reference)
204
+ when :via_csv
205
+ source_reference = path.sub(/\.[^\.]+/, '.csv')
206
+ save(source_reference)
207
+ end
208
+
134
209
  @log.info("Save #{path}")
135
210
  expath = File.expand_path(path) #Excel needs absolute path
136
211
  expath.gsub!(/\//, '\\')# Save the workbook. / must be \
@@ -138,12 +213,12 @@ direct xls[x] generation.
138
213
  #different formats, see http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.xlfileformat.aspx
139
214
  case path
140
215
  when /xlsx$/
141
- wb = prepare_xls(xml_file)
216
+ wb = prepare_xls(source_reference)
142
217
  @log.info("Save #{path}")
143
218
  wb.SaveAs(expath, 51)
144
219
  wb.Close
145
220
  when /xls$/
146
- wb = prepare_xls(xml_file)
221
+ wb = prepare_xls(source_reference)
147
222
  @log.info("Save #{path}")
148
223
  wb.SaveAs(expath, -4143 ) #excel97_2003_format
149
224
  wb.Close
@@ -160,6 +235,10 @@ The namespace must be used before its definition (Tag Workbook)
160
235
  File.open(path, 'w'){|f|
161
236
  f << build_excel_xml(ns)
162
237
  }
238
+ when /csv$/
239
+ File.open(path, 'w'){|f|
240
+ f << build_excel_csv()
241
+ }
163
242
  else
164
243
  @log.fatal("Wrong filename, no xls/xlsx (#{path})")
165
244
  raise ArgumentError, "Wrong filename, no xls/xlsx (#{path})"
@@ -224,6 +303,25 @@ Descriptions and examples:
224
303
  ).strip.gsub(/<(\/?)Workbook/, '<\1%s:Workbook' % namespace )
225
304
 
226
305
  end #build_excel_xml
306
+ =begin rdoc
307
+ Build the csv for Excel.
308
+
309
+ Details on options see Rexcel::Worksheet#to_csv and Rexcel::Cell#to_csv.
310
+ =end
311
+ def build_excel_csv(options = {})
312
+
313
+ csv = []
314
+ count = 0 #counter for filled worksheets
315
+ @log.debug("Prepare csv")
316
+ @worksheets.each{|worksheet|
317
+ next if worksheet.rows.empty?
318
+ csv << worksheet.to_csv(options)
319
+ count += 1
320
+ } #Worksheets
321
+
322
+ raise ArgumentError, "csv not possible for multiple worksheets" if count > 1
323
+ csv.join()
324
+ end #build_excel_csv
227
325
 
228
326
  end #class Workbook
229
327
  end #module Excel