rexcel 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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