roo-immersion 1.0.0
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/History.txt +225 -0
- data/README.markdown +60 -0
- data/examples/roo_soap_client.rb +53 -0
- data/examples/roo_soap_server.rb +29 -0
- data/examples/write_me.rb +33 -0
- data/lib/roo.rb +32 -0
- data/lib/roo/excel.rb +468 -0
- data/lib/roo/excel2003xml.rb +394 -0
- data/lib/roo/excelx.rb +601 -0
- data/lib/roo/generic_spreadsheet.rb +628 -0
- data/lib/roo/google.rb +379 -0
- data/lib/roo/openoffice.rb +451 -0
- data/lib/roo/roo_rails_helper.rb +82 -0
- data/lib/roo/version.rb +9 -0
- data/test/1900_base.xls +0 -0
- data/test/1904_base.xls +0 -0
- data/test/Bibelbund.csv +3741 -0
- data/test/Bibelbund.ods +0 -0
- data/test/Bibelbund.xls +0 -0
- data/test/Bibelbund.xlsx +0 -0
- data/test/Bibelbund.xml +62518 -0
- data/test/Bibelbund1.ods +0 -0
- data/test/bad_excel_date.xls +0 -0
- data/test/bbu.ods +0 -0
- data/test/bbu.xls +0 -0
- data/test/bbu.xlsx +0 -0
- data/test/bbu.xml +152 -0
- data/test/bode-v1.ods.zip +0 -0
- data/test/bode-v1.xls.zip +0 -0
- data/test/boolean.ods +0 -0
- data/test/boolean.xls +0 -0
- data/test/boolean.xlsx +0 -0
- data/test/boolean.xml +112 -0
- data/test/borders.ods +0 -0
- data/test/borders.xls +0 -0
- data/test/borders.xlsx +0 -0
- data/test/borders.xml +144 -0
- data/test/bug-row-column-fixnum-float.xls +0 -0
- data/test/bug-row-column-fixnum-float.xml +127 -0
- data/test/datetime.ods +0 -0
- data/test/datetime.xls +0 -0
- data/test/datetime.xlsx +0 -0
- data/test/datetime.xml +142 -0
- data/test/datetime_floatconv.xls +0 -0
- data/test/datetime_floatconv.xml +148 -0
- data/test/emptysheets.ods +0 -0
- data/test/emptysheets.xls +0 -0
- data/test/emptysheets.xml +105 -0
- data/test/excel2003.xml +21140 -0
- data/test/false_encoding.xls +0 -0
- data/test/false_encoding.xml +132 -0
- data/test/formula.ods +0 -0
- data/test/formula.xls +0 -0
- data/test/formula.xlsx +0 -0
- data/test/formula.xml +134 -0
- data/test/formula_parse_error.xls +0 -0
- data/test/formula_parse_error.xml +1833 -0
- data/test/html-escape.ods +0 -0
- data/test/no_spreadsheet_file.txt +1 -0
- data/test/numbers1.csv +18 -0
- data/test/numbers1.ods +0 -0
- data/test/numbers1.xls +0 -0
- data/test/numbers1.xlsx +0 -0
- data/test/numbers1.xml +312 -0
- data/test/only_one_sheet.ods +0 -0
- data/test/only_one_sheet.xls +0 -0
- data/test/only_one_sheet.xlsx +0 -0
- data/test/only_one_sheet.xml +67 -0
- data/test/paragraph.ods +0 -0
- data/test/paragraph.xls +0 -0
- data/test/paragraph.xlsx +0 -0
- data/test/paragraph.xml +127 -0
- data/test/ric.ods +0 -0
- data/test/simple_spreadsheet.ods +0 -0
- data/test/simple_spreadsheet.xls +0 -0
- data/test/simple_spreadsheet.xlsx +0 -0
- data/test/simple_spreadsheet.xml +225 -0
- data/test/simple_spreadsheet_from_italo.ods +0 -0
- data/test/simple_spreadsheet_from_italo.xls +0 -0
- data/test/simple_spreadsheet_from_italo.xml +242 -0
- data/test/skipped_tests.rb +789 -0
- data/test/style.ods +0 -0
- data/test/style.xls +0 -0
- data/test/style.xlsx +0 -0
- data/test/style.xml +154 -0
- data/test/test_helper.rb +19 -0
- data/test/test_roo.rb +1834 -0
- data/test/time-test.csv +2 -0
- data/test/time-test.ods +0 -0
- data/test/time-test.xls +0 -0
- data/test/time-test.xlsx +0 -0
- data/test/time-test.xml +131 -0
- data/test/whitespace.ods +0 -0
- data/test/whitespace.xls +0 -0
- data/test/whitespace.xlsx +0 -0
- data/test/whitespace.xml +184 -0
- metadata +231 -0
@@ -0,0 +1,628 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
# Base class for all other types of spreadsheets
|
5
|
+
class GenericSpreadsheet
|
6
|
+
|
7
|
+
attr_reader :default_sheet
|
8
|
+
|
9
|
+
# sets the line with attribute names (default: 1)
|
10
|
+
attr_accessor :header_line
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
end
|
14
|
+
|
15
|
+
# set the working sheet in the document
|
16
|
+
def default_sheet=(sheet)
|
17
|
+
if sheet.kind_of? Fixnum
|
18
|
+
if sheet >= 0 and sheet <= sheets.length
|
19
|
+
sheet = self.sheets[sheet-1]
|
20
|
+
else
|
21
|
+
raise RangeError
|
22
|
+
end
|
23
|
+
elsif sheet.kind_of?(String)
|
24
|
+
raise RangeError if ! self.sheets.include?(sheet)
|
25
|
+
else
|
26
|
+
raise TypeError, "what are you trying to set as default sheet?"
|
27
|
+
end
|
28
|
+
@default_sheet = sheet
|
29
|
+
check_default_sheet
|
30
|
+
@first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil
|
31
|
+
@cells_read[sheet] = false
|
32
|
+
end
|
33
|
+
|
34
|
+
# first non-empty column as a letter
|
35
|
+
def first_column_as_letter(sheet=nil)
|
36
|
+
GenericSpreadsheet.number_to_letter(first_column(sheet))
|
37
|
+
end
|
38
|
+
|
39
|
+
# last non-empty column as a letter
|
40
|
+
def last_column_as_letter(sheet=nil)
|
41
|
+
GenericSpreadsheet.number_to_letter(last_column(sheet))
|
42
|
+
end
|
43
|
+
|
44
|
+
# returns the number of the first non-empty row
|
45
|
+
def first_row(sheet=nil)
|
46
|
+
if sheet == nil
|
47
|
+
sheet = @default_sheet
|
48
|
+
end
|
49
|
+
read_cells(sheet) unless @cells_read[sheet]
|
50
|
+
if @first_row[sheet]
|
51
|
+
return @first_row[sheet]
|
52
|
+
end
|
53
|
+
impossible_value = 999_999 # more than a spreadsheet can hold
|
54
|
+
result = impossible_value
|
55
|
+
@cell[sheet].each_pair {|key,value|
|
56
|
+
y,x = key # _to_string(key).split(',')
|
57
|
+
y = y.to_i
|
58
|
+
result = [result, y].min if value
|
59
|
+
} if @cell[sheet]
|
60
|
+
result = nil if result == impossible_value
|
61
|
+
@first_row[sheet] = result
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
# returns the number of the last non-empty row
|
66
|
+
def last_row(sheet=nil)
|
67
|
+
sheet = @default_sheet unless sheet
|
68
|
+
read_cells(sheet) unless @cells_read[sheet]
|
69
|
+
if @last_row[sheet]
|
70
|
+
return @last_row[sheet]
|
71
|
+
end
|
72
|
+
impossible_value = 0
|
73
|
+
result = impossible_value
|
74
|
+
@cell[sheet].each_pair {|key,value|
|
75
|
+
y,x = key # _to_string(key).split(',')
|
76
|
+
y = y.to_i
|
77
|
+
result = [result, y].max if value
|
78
|
+
} if @cell[sheet]
|
79
|
+
result = nil if result == impossible_value
|
80
|
+
@last_row[sheet] = result
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
# returns the number of the first non-empty column
|
85
|
+
def first_column(sheet=nil)
|
86
|
+
if sheet == nil
|
87
|
+
sheet = @default_sheet
|
88
|
+
end
|
89
|
+
read_cells(sheet) unless @cells_read[sheet]
|
90
|
+
if @first_column[sheet]
|
91
|
+
return @first_column[sheet]
|
92
|
+
end
|
93
|
+
impossible_value = 999_999 # more than a spreadsheet can hold
|
94
|
+
result = impossible_value
|
95
|
+
@cell[sheet].each_pair {|key,value|
|
96
|
+
y,x = key # _to_string(key).split(',')
|
97
|
+
x = x # .to_i
|
98
|
+
result = [result, x].min if value
|
99
|
+
} if @cell[sheet]
|
100
|
+
result = nil if result == impossible_value
|
101
|
+
@first_column[sheet] = result
|
102
|
+
result
|
103
|
+
end
|
104
|
+
|
105
|
+
# returns the number of the last non-empty column
|
106
|
+
def last_column(sheet=nil)
|
107
|
+
sheet = @default_sheet unless sheet
|
108
|
+
read_cells(sheet) unless @cells_read[sheet]
|
109
|
+
if @last_column[sheet]
|
110
|
+
return @last_column[sheet]
|
111
|
+
end
|
112
|
+
impossible_value = 0
|
113
|
+
result = impossible_value
|
114
|
+
@cell[sheet].each_pair {|key,value|
|
115
|
+
y,x = key # _to_string(key).split(',')
|
116
|
+
x = x.to_i
|
117
|
+
result = [result, x].max if value
|
118
|
+
} if @cell[sheet]
|
119
|
+
result = nil if result == impossible_value
|
120
|
+
@last_column[sheet] = result
|
121
|
+
result
|
122
|
+
end
|
123
|
+
|
124
|
+
# returns a rectangular area (default: all cells) as yaml-output
|
125
|
+
# you can add additional attributes with the prefix parameter like:
|
126
|
+
# oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
|
127
|
+
def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
|
128
|
+
sheet = @default_sheet unless sheet
|
129
|
+
result = "--- \n"
|
130
|
+
(from_row||first_row(sheet)).upto(to_row||last_row(sheet)) do |row|
|
131
|
+
(from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
|
132
|
+
unless empty?(row,col,sheet)
|
133
|
+
result << "cell_#{row}_#{col}: \n"
|
134
|
+
prefix.each {|k,v|
|
135
|
+
result << " #{k}: #{v} \n"
|
136
|
+
}
|
137
|
+
result << " row: #{row} \n"
|
138
|
+
result << " col: #{col} \n"
|
139
|
+
result << " celltype: #{self.celltype(row,col,sheet)} \n"
|
140
|
+
if self.celltype(row,col,sheet) == :time
|
141
|
+
result << " value: #{GenericSpreadsheet.integer_to_timestring( self.cell(row,col,sheet))} \n"
|
142
|
+
else
|
143
|
+
result << " value: #{self.cell(row,col,sheet)} \n"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
result
|
149
|
+
end
|
150
|
+
|
151
|
+
# write the current spreadsheet to stdout or into a file
|
152
|
+
def to_csv(filename=nil,sheet=nil)
|
153
|
+
sheet = @default_sheet unless sheet
|
154
|
+
if filename
|
155
|
+
file = File.open(filename,"w") # do |file|
|
156
|
+
write_csv_content(file,sheet)
|
157
|
+
file.close
|
158
|
+
else
|
159
|
+
write_csv_content(STDOUT,sheet)
|
160
|
+
end
|
161
|
+
true
|
162
|
+
end
|
163
|
+
|
164
|
+
# find a row either by row number or a condition
|
165
|
+
# Caution: this works only within the default sheet -> set default_sheet before you call this method
|
166
|
+
# (experimental. see examples in the test_roo.rb file)
|
167
|
+
def find(*args) # :nodoc
|
168
|
+
result_array = false
|
169
|
+
args.each {|arg,val|
|
170
|
+
if arg.class == Hash
|
171
|
+
arg.each { |hkey,hval|
|
172
|
+
if hkey == :array and hval == true
|
173
|
+
result_array = true
|
174
|
+
end
|
175
|
+
}
|
176
|
+
end
|
177
|
+
}
|
178
|
+
column_with = {}
|
179
|
+
1.upto(last_column) do |col|
|
180
|
+
column_with[cell(@header_line,col)] = col
|
181
|
+
end
|
182
|
+
result = Array.new
|
183
|
+
#-- id
|
184
|
+
if args[0].class == Fixnum
|
185
|
+
rownum = args[0]
|
186
|
+
if @header_line
|
187
|
+
tmp = {}
|
188
|
+
else
|
189
|
+
tmp = []
|
190
|
+
end
|
191
|
+
1.upto(self.row(rownum).size) {|j|
|
192
|
+
x = ''
|
193
|
+
column_with.each { |key,val|
|
194
|
+
if val == j
|
195
|
+
x = key
|
196
|
+
end
|
197
|
+
}
|
198
|
+
if @header_line
|
199
|
+
tmp[x] = cell(rownum,j)
|
200
|
+
else
|
201
|
+
tmp[j-1] = cell(rownum,j)
|
202
|
+
end
|
203
|
+
|
204
|
+
}
|
205
|
+
if @header_line
|
206
|
+
result = [ tmp ]
|
207
|
+
else
|
208
|
+
result = tmp
|
209
|
+
end
|
210
|
+
#-- :all
|
211
|
+
elsif args[0] == :all
|
212
|
+
if args[1].class == Hash
|
213
|
+
args[1].each {|key,val|
|
214
|
+
if key == :conditions
|
215
|
+
column_with = {}
|
216
|
+
1.upto(last_column) do |col|
|
217
|
+
column_with[cell(@header_line,col)] = col
|
218
|
+
end
|
219
|
+
conditions = val
|
220
|
+
first_row.upto(last_row) do |i|
|
221
|
+
# are all conditions met?
|
222
|
+
found = 1
|
223
|
+
conditions.each { |key,val|
|
224
|
+
if cell(i,column_with[key]) == val
|
225
|
+
found *= 1
|
226
|
+
else
|
227
|
+
found *= 0
|
228
|
+
end
|
229
|
+
}
|
230
|
+
if found > 0
|
231
|
+
tmp = {}
|
232
|
+
1.upto(self.row(i).size) {|j|
|
233
|
+
x = ''
|
234
|
+
column_with.each { |key,val|
|
235
|
+
if val == j
|
236
|
+
x = key
|
237
|
+
end
|
238
|
+
}
|
239
|
+
tmp[x] = cell(i,j)
|
240
|
+
}
|
241
|
+
if result_array
|
242
|
+
result << self.row(i)
|
243
|
+
else
|
244
|
+
result << tmp
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end # :conditions
|
249
|
+
}
|
250
|
+
end
|
251
|
+
end
|
252
|
+
result
|
253
|
+
end
|
254
|
+
|
255
|
+
# returns all values in this row as an array
|
256
|
+
# row numbers are 1,2,3,... like in the spreadsheet
|
257
|
+
def row(rownumber,sheet=nil)
|
258
|
+
sheet = @default_sheet unless sheet
|
259
|
+
read_cells(sheet) unless @cells_read[sheet]
|
260
|
+
result = []
|
261
|
+
first_column(sheet).upto(last_column(sheet)) do |col|
|
262
|
+
result << cell(rownumber,col,sheet)
|
263
|
+
end
|
264
|
+
result
|
265
|
+
end
|
266
|
+
|
267
|
+
# returns all values in this column as an array
|
268
|
+
# column numbers are 1,2,3,... like in the spreadsheet
|
269
|
+
def column(columnnumber,sheet=nil)
|
270
|
+
if columnnumber.class == String
|
271
|
+
columnnumber = Excel.letter_to_number(columnnumber)
|
272
|
+
end
|
273
|
+
sheet = @default_sheet unless sheet
|
274
|
+
read_cells(sheet) unless @cells_read[sheet]
|
275
|
+
result = []
|
276
|
+
first_row(sheet).upto(last_row(sheet)) do |row|
|
277
|
+
result << cell(row,columnnumber,sheet)
|
278
|
+
end
|
279
|
+
result
|
280
|
+
end
|
281
|
+
|
282
|
+
# reopens and read a spreadsheet document
|
283
|
+
def reload
|
284
|
+
ds = @default_sheet
|
285
|
+
initialize(@filename) if self.class == Openoffice or
|
286
|
+
self.class == Excel
|
287
|
+
initialize(@spreadsheetkey,@user,@password) if self.class == Google
|
288
|
+
self.default_sheet = ds
|
289
|
+
#@first_row = @last_row = @first_column = @last_column = nil
|
290
|
+
end
|
291
|
+
|
292
|
+
# true if cell is empty
|
293
|
+
def empty?(row, col, sheet=nil)
|
294
|
+
sheet = @default_sheet unless sheet
|
295
|
+
read_cells(sheet) unless @cells_read[sheet] or self.class == Excel
|
296
|
+
row,col = normalize(row,col)
|
297
|
+
return true unless cell(row, col, sheet)
|
298
|
+
return true if celltype(row, col, sheet) == :string && cell(row, col, sheet).empty?
|
299
|
+
return true if row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet)
|
300
|
+
false
|
301
|
+
end
|
302
|
+
|
303
|
+
# recursively removes the current temporary directory
|
304
|
+
# this is only needed if you work with zipped files or files via the web
|
305
|
+
def remove_tmp
|
306
|
+
if File.exists?(@tmpdir)
|
307
|
+
FileUtils::rm_r(@tmpdir)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
# Returns information of the spreadsheet document and all sheets within
|
312
|
+
# this document.
|
313
|
+
def info
|
314
|
+
result = "File: #{File.basename(@filename)}\n"+
|
315
|
+
"Number of sheets: #{sheets.size}\n"+
|
316
|
+
"Sheets: #{sheets.map{|sheet| sheet+", "}.to_s[0..-3]}\n"
|
317
|
+
n = 1
|
318
|
+
sheets.each {|sheet|
|
319
|
+
self.default_sheet = sheet
|
320
|
+
result << "Sheet " + n.to_s + ":\n"
|
321
|
+
unless first_row
|
322
|
+
result << " - empty -"
|
323
|
+
else
|
324
|
+
result << " First row: #{first_row}\n"
|
325
|
+
result << " Last row: #{last_row}\n"
|
326
|
+
result << " First column: #{GenericSpreadsheet.number_to_letter(first_column)}\n"
|
327
|
+
result << " Last column: #{GenericSpreadsheet.number_to_letter(last_column)}"
|
328
|
+
end
|
329
|
+
result << "\n" if sheet != sheets.last
|
330
|
+
n += 1
|
331
|
+
}
|
332
|
+
result
|
333
|
+
end
|
334
|
+
|
335
|
+
def to_xml
|
336
|
+
xml_document = ''
|
337
|
+
xml = Builder::XmlMarkup.new(:target => xml_document, :indent => 2)
|
338
|
+
xml.instruct! :xml, :version =>"1.0", :encoding => "utf-8"
|
339
|
+
xml.spreadsheet {
|
340
|
+
self.sheets.each do |sheet|
|
341
|
+
self.default_sheet = sheet
|
342
|
+
xml.sheet(:name => sheet) { |x|
|
343
|
+
if first_row and last_row and first_column and last_column
|
344
|
+
# sonst gibt es Fehler bei leeren Blaettern
|
345
|
+
first_row.upto(last_row) do |row|
|
346
|
+
first_column.upto(last_column) do |col|
|
347
|
+
unless empty?(row,col)
|
348
|
+
x.cell(cell(row,col),
|
349
|
+
:row =>row,
|
350
|
+
:column => col,
|
351
|
+
:type => celltype(row,col))
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
}
|
357
|
+
end
|
358
|
+
}
|
359
|
+
xml_document
|
360
|
+
end
|
361
|
+
|
362
|
+
protected
|
363
|
+
|
364
|
+
def file_type_check(filename, ext, name)
|
365
|
+
new_expression = {
|
366
|
+
'.ods' => 'Openoffice.new',
|
367
|
+
'.xls' => 'Excel.new',
|
368
|
+
'.xlsx' => 'Excelx.new',
|
369
|
+
'.xml' => 'Excel2003.new'
|
370
|
+
}
|
371
|
+
case ext
|
372
|
+
when '.ods', '.xls', '.xlsx', '.xml'
|
373
|
+
correct_class = "use #{new_expression[ext]} to handle #{ext} spreadsheet files"
|
374
|
+
else
|
375
|
+
raise "unknown file type: #{ext}"
|
376
|
+
end
|
377
|
+
if File.extname(filename).downcase != ext
|
378
|
+
case @file_warning
|
379
|
+
when :error
|
380
|
+
warn correct_class
|
381
|
+
raise TypeError, "#{filename} is not #{name} file"
|
382
|
+
when :warning
|
383
|
+
warn "are you sure, this is #{name} spreadsheet file?"
|
384
|
+
warn correct_class
|
385
|
+
when :ignore
|
386
|
+
# ignore
|
387
|
+
else
|
388
|
+
raise "#{@file_warning} illegal state of file_warning"
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
# konvertiert einen Key in der Form "12,45" (=row,column) in
|
394
|
+
# ein Array mit numerischen Werten ([12,45])
|
395
|
+
# Diese Methode ist eine temp. Loesung, um zu erforschen, ob der
|
396
|
+
# Zugriff mit numerischen Keys schneller ist.
|
397
|
+
def key_to_num(str)
|
398
|
+
r,c = str.split(',')
|
399
|
+
r = r.to_i
|
400
|
+
c = c.to_i
|
401
|
+
[r,c]
|
402
|
+
end
|
403
|
+
|
404
|
+
# siehe: key_to_num
|
405
|
+
def key_to_string(arr)
|
406
|
+
"#{arr[0]},#{arr[1]}"
|
407
|
+
end
|
408
|
+
|
409
|
+
private
|
410
|
+
|
411
|
+
# converts cell coordinate to numeric values of row,col
|
412
|
+
def normalize(row,col)
|
413
|
+
if row.class == String
|
414
|
+
if col.class == Fixnum
|
415
|
+
# ('A',1):
|
416
|
+
# ('B', 5) -> (5, 2)
|
417
|
+
row, col = col, row
|
418
|
+
else
|
419
|
+
raise ArgumentError
|
420
|
+
end
|
421
|
+
end
|
422
|
+
if col.class == String
|
423
|
+
col = GenericSpreadsheet.letter_to_number(col)
|
424
|
+
end
|
425
|
+
return row,col
|
426
|
+
end
|
427
|
+
|
428
|
+
# def open_from_uri(uri)
|
429
|
+
# require 'open-uri' ;
|
430
|
+
# tempfilename = File.join(@tmpdir, File.basename(uri))
|
431
|
+
# f = File.open(tempfilename,"wb")
|
432
|
+
# begin
|
433
|
+
# open(uri) do |net|
|
434
|
+
# f.write(net.read)
|
435
|
+
# end
|
436
|
+
# rescue
|
437
|
+
# raise "could not open #{uri}"
|
438
|
+
# end
|
439
|
+
# f.close
|
440
|
+
# File.join(@tmpdir, File.basename(uri))
|
441
|
+
# end
|
442
|
+
|
443
|
+
# OpenURI::HTTPError
|
444
|
+
# def open_from_uri(uri)
|
445
|
+
# require 'open-uri'
|
446
|
+
# #existiert URL?
|
447
|
+
# r = Net::HTTP.get_response(URI.parse(uri))
|
448
|
+
# raise "URL nicht verfuegbar" unless r.is_a? Net::HTTPOK
|
449
|
+
# tempfilename = File.join(@tmpdir, File.basename(uri))
|
450
|
+
# f = File.open(tempfilename,"wb")
|
451
|
+
# open(uri) do |net|
|
452
|
+
# f.write(net.read)
|
453
|
+
# end
|
454
|
+
# # rescue
|
455
|
+
# # raise "could not open #{uri}"
|
456
|
+
# # end
|
457
|
+
# f.close
|
458
|
+
# File.join(@tmpdir, File.basename(uri))
|
459
|
+
# end
|
460
|
+
|
461
|
+
def open_from_uri(uri)
|
462
|
+
require 'open-uri'
|
463
|
+
response = ''
|
464
|
+
begin
|
465
|
+
open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") { |net|
|
466
|
+
response = net.read
|
467
|
+
tempfilename = File.join(@tmpdir, File.basename(uri))
|
468
|
+
f = File.open(tempfilename,"wb")
|
469
|
+
f.write(response)
|
470
|
+
f.close
|
471
|
+
}
|
472
|
+
rescue OpenURI::HTTPError
|
473
|
+
raise "could not open #{uri}"
|
474
|
+
end
|
475
|
+
File.join(@tmpdir, File.basename(uri))
|
476
|
+
end
|
477
|
+
|
478
|
+
def open_from_stream(stream)
|
479
|
+
tempfilename = File.join(@tmpdir, "spreadsheet")
|
480
|
+
f = File.open(tempfilename,"wb")
|
481
|
+
f.write(stream[7..-1])
|
482
|
+
f.close
|
483
|
+
File.join(@tmpdir, "spreadsheet")
|
484
|
+
end
|
485
|
+
|
486
|
+
# convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...)
|
487
|
+
def self.number_to_letter(n)
|
488
|
+
letters=""
|
489
|
+
while n > 0
|
490
|
+
num = n%26
|
491
|
+
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num-1,1] + letters
|
492
|
+
n = n.div(26)
|
493
|
+
end
|
494
|
+
letters
|
495
|
+
end
|
496
|
+
|
497
|
+
# convert letters like 'AB' to a number ('A' => 1, 'B' => 2, ...)
|
498
|
+
def self.letter_to_number(letters)
|
499
|
+
result = 0
|
500
|
+
while letters && letters.length > 0
|
501
|
+
character = letters[0,1].upcase
|
502
|
+
num = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(character)
|
503
|
+
raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
|
504
|
+
num += 1
|
505
|
+
result = result * 26 + num
|
506
|
+
letters = letters[1..-1]
|
507
|
+
end
|
508
|
+
result
|
509
|
+
end
|
510
|
+
|
511
|
+
def unzip(filename)
|
512
|
+
ret = nil
|
513
|
+
Zip::ZipFile.open(filename) do |zip|
|
514
|
+
ret = process_zipfile_packed zip
|
515
|
+
end
|
516
|
+
ret
|
517
|
+
end
|
518
|
+
|
519
|
+
# check if default_sheet was set and exists in sheets-array
|
520
|
+
def check_default_sheet
|
521
|
+
sheet_found = false
|
522
|
+
raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
|
523
|
+
if sheets.index(@default_sheet)
|
524
|
+
sheet_found = true
|
525
|
+
end
|
526
|
+
if ! sheet_found
|
527
|
+
raise RangeError, "sheet '#{@default_sheet}' not found"
|
528
|
+
end
|
529
|
+
#raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
|
530
|
+
end
|
531
|
+
|
532
|
+
def process_zipfile_packed(zip, path='')
|
533
|
+
ret=nil
|
534
|
+
if zip.file.file? path
|
535
|
+
# extract and return filename
|
536
|
+
@tmpdir = "oo_"+$$.to_s
|
537
|
+
unless File.exists?(@tmpdir)
|
538
|
+
FileUtils::mkdir(@tmpdir)
|
539
|
+
end
|
540
|
+
file = File.open(File.join(@tmpdir, path),"wb")
|
541
|
+
file.write(zip.read(path))
|
542
|
+
file.close
|
543
|
+
return File.join(@tmpdir, path)
|
544
|
+
else
|
545
|
+
unless path.empty?
|
546
|
+
path += '/'
|
547
|
+
end
|
548
|
+
zip.dir.foreach(path) do |filename|
|
549
|
+
ret = process_zipfile_packed(zip, path + filename)
|
550
|
+
end
|
551
|
+
end
|
552
|
+
ret
|
553
|
+
end
|
554
|
+
|
555
|
+
def write_csv_content(file=nil,sheet=nil)
|
556
|
+
file = STDOUT unless file
|
557
|
+
if first_row(sheet) # sheet is not empty
|
558
|
+
# first_row(sheet).upto(last_row(sheet)) do |row|
|
559
|
+
1.upto(last_row(sheet)) do |row|
|
560
|
+
1.upto(last_column(sheet)) do |col|
|
561
|
+
file.print(",") if col > 1
|
562
|
+
onecell = cell(row,col,sheet)
|
563
|
+
onecelltype = celltype(row,col,sheet)
|
564
|
+
file.print one_cell_output(onecelltype,onecell,empty?(row,col,sheet))
|
565
|
+
end
|
566
|
+
file.print("\n")
|
567
|
+
end # sheet not empty
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
def one_cell_output(onecelltype,onecell,empty)
|
572
|
+
str = ""
|
573
|
+
if empty
|
574
|
+
str += ''
|
575
|
+
else
|
576
|
+
case onecelltype
|
577
|
+
when :string
|
578
|
+
if onecell == ""
|
579
|
+
str << ''
|
580
|
+
else
|
581
|
+
onecell.gsub!(/"/,'""')
|
582
|
+
str << ('"'+onecell+'"')
|
583
|
+
end
|
584
|
+
when :float,:percentage
|
585
|
+
if onecell == onecell.to_i
|
586
|
+
str << onecell.to_i.to_s
|
587
|
+
else
|
588
|
+
str << onecell.to_s
|
589
|
+
end
|
590
|
+
when :formula
|
591
|
+
if onecell.class == String
|
592
|
+
if onecell == ""
|
593
|
+
str << ''
|
594
|
+
else
|
595
|
+
onecell.gsub!(/"/,'""')
|
596
|
+
str << '"'+onecell+'"'
|
597
|
+
end
|
598
|
+
elsif onecell.class == Float
|
599
|
+
if onecell == onecell.to_i
|
600
|
+
str << onecell.to_i.to_s
|
601
|
+
else
|
602
|
+
str << onecell.to_s
|
603
|
+
end
|
604
|
+
else
|
605
|
+
raise "unhandled onecell-class "+onecell.class.to_s
|
606
|
+
end
|
607
|
+
when :date
|
608
|
+
str << onecell.to_s
|
609
|
+
when :time
|
610
|
+
str << GenericSpreadsheet.integer_to_timestring(onecell)
|
611
|
+
else
|
612
|
+
raise "unhandled celltype "+onecelltype.to_s
|
613
|
+
end
|
614
|
+
end
|
615
|
+
str
|
616
|
+
end
|
617
|
+
|
618
|
+
# converts an integer value to a time string like '02:05:06'
|
619
|
+
def self.integer_to_timestring(content)
|
620
|
+
return content if String === content
|
621
|
+
h = (content/3600.0).floor
|
622
|
+
content = content - h*3600
|
623
|
+
m = (content/60.0).floor
|
624
|
+
content = content - m*60
|
625
|
+
s = content
|
626
|
+
sprintf("%02d:%02d:%02d",h,m,s)
|
627
|
+
end
|
628
|
+
end
|