roo 1.10.1 → 1.10.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +38 -0
- data/History.txt +4 -4
- data/License.txt +20 -0
- data/Manifest.txt +68 -0
- data/README.markdown +109 -0
- data/Rakefile +5 -4
- data/bin/roo +0 -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 +20 -61
- data/lib/roo/csv.rb +13 -11
- data/lib/roo/excel.rb +108 -219
- data/lib/roo/excel2003xml.rb +312 -0
- data/lib/roo/excelx.rb +205 -341
- data/lib/roo/generic_spreadsheet.rb +371 -268
- data/lib/roo/google.rb +64 -54
- data/lib/roo/openoffice.rb +101 -156
- data/lib/roo/roo_rails_helper.rb +5 -5
- data/lib/roo/worksheet.rb +18 -0
- data/roo.gemspec +43 -0
- data/scripts/txt2html +67 -0
- data/test/all_ss.rb +8 -10
- data/test/{1900_base.xls → files/1900_base.xls} +0 -0
- data/test/{1904_base.xls → files/1904_base.xls} +0 -0
- data/test/{Bibelbund.csv → files/Bibelbund.csv} +0 -0
- data/test/{Bibelbund.ods → files/Bibelbund.ods} +0 -0
- data/test/{Bibelbund.xls → files/Bibelbund.xls} +0 -0
- data/test/{Bibelbund.xlsx → files/Bibelbund.xlsx} +0 -0
- data/test/files/Bibelbund.xml +62518 -0
- data/test/{Bibelbund1.ods → files/Bibelbund1.ods} +0 -0
- data/test/{Pfand_from_windows_phone.xlsx → files/Pfand_from_windows_phone.xlsx} +0 -0
- data/test/files/bad_excel_date.xls +0 -0
- data/test/{bbu.ods → files/bbu.ods} +0 -0
- data/test/{bbu.xls → files/bbu.xls} +0 -0
- data/test/{bbu.xlsx → files/bbu.xlsx} +0 -0
- data/test/files/bbu.xml +152 -0
- data/test/{bode-v1.ods.zip → files/bode-v1.ods.zip} +0 -0
- data/test/{bode-v1.xls.zip → files/bode-v1.xls.zip} +0 -0
- data/test/{boolean.ods → files/boolean.ods} +0 -0
- data/test/{boolean.xls → files/boolean.xls} +0 -0
- data/test/{boolean.xlsx → files/boolean.xlsx} +0 -0
- data/test/files/boolean.xml +112 -0
- data/test/{borders.ods → files/borders.ods} +0 -0
- data/test/{borders.xls → files/borders.xls} +0 -0
- data/test/{borders.xlsx → files/borders.xlsx} +0 -0
- data/test/files/borders.xml +144 -0
- data/test/{bug-row-column-fixnum-float.xls → files/bug-row-column-fixnum-float.xls} +0 -0
- data/test/files/bug-row-column-fixnum-float.xml +127 -0
- data/test/{comments.ods → files/comments.ods} +0 -0
- data/test/{comments.xls → files/comments.xls} +0 -0
- data/test/{comments.xlsx → files/comments.xlsx} +0 -0
- data/test/{csvtypes.csv → files/csvtypes.csv} +0 -0
- data/test/{datetime.ods → files/datetime.ods} +0 -0
- data/test/{datetime.xls → files/datetime.xls} +0 -0
- data/test/{datetime.xlsx → files/datetime.xlsx} +0 -0
- data/test/files/datetime.xml +142 -0
- data/test/{datetime_floatconv.xls → files/datetime_floatconv.xls} +0 -0
- data/test/files/datetime_floatconv.xml +148 -0
- data/test/{dreimalvier.ods → files/dreimalvier.ods} +0 -0
- data/test/{emptysheets.ods → files/emptysheets.ods} +0 -0
- data/test/{emptysheets.xls → files/emptysheets.xls} +0 -0
- data/test/{emptysheets.xlsx → files/emptysheets.xlsx} +0 -0
- data/test/files/emptysheets.xml +105 -0
- data/test/files/excel2003.xml +21140 -0
- data/test/{false_encoding.xls → files/false_encoding.xls} +0 -0
- data/test/files/false_encoding.xml +132 -0
- data/test/{formula.ods → files/formula.ods} +0 -0
- data/test/{formula.xls → files/formula.xls} +0 -0
- data/test/{formula.xlsx → files/formula.xlsx} +0 -0
- data/test/files/formula.xml +134 -0
- data/test/files/formula_parse_error.xls +0 -0
- data/test/files/formula_parse_error.xml +1833 -0
- data/test/{formula_string_error.xlsx → files/formula_string_error.xlsx} +0 -0
- data/test/{html-escape.ods → files/html-escape.ods} +0 -0
- data/test/{matrix.ods → files/matrix.ods} +0 -0
- data/test/{matrix.xls → files/matrix.xls} +0 -0
- data/test/{named_cells.ods → files/named_cells.ods} +0 -0
- data/test/{named_cells.xls → files/named_cells.xls} +0 -0
- data/test/{named_cells.xlsx → files/named_cells.xlsx} +0 -0
- data/test/{no_spreadsheet_file.txt → files/no_spreadsheet_file.txt} +0 -0
- data/test/{numbers1.csv → files/numbers1.csv} +0 -0
- data/test/{numbers1.ods → files/numbers1.ods} +0 -0
- data/test/{numbers1.xls → files/numbers1.xls} +0 -0
- data/test/{numbers1.xlsx → files/numbers1.xlsx} +0 -0
- data/test/files/numbers1.xml +312 -0
- data/test/{only_one_sheet.ods → files/only_one_sheet.ods} +0 -0
- data/test/{only_one_sheet.xls → files/only_one_sheet.xls} +0 -0
- data/test/{only_one_sheet.xlsx → files/only_one_sheet.xlsx} +0 -0
- data/test/files/only_one_sheet.xml +67 -0
- data/test/{paragraph.ods → files/paragraph.ods} +0 -0
- data/test/{paragraph.xls → files/paragraph.xls} +0 -0
- data/test/{paragraph.xlsx → files/paragraph.xlsx} +0 -0
- data/test/files/paragraph.xml +127 -0
- data/test/{prova.xls → files/prova.xls} +0 -0
- data/test/{ric.ods → files/ric.ods} +0 -0
- data/test/{simple_spreadsheet.ods → files/simple_spreadsheet.ods} +0 -0
- data/test/{simple_spreadsheet.xls → files/simple_spreadsheet.xls} +0 -0
- data/test/{simple_spreadsheet.xlsx → files/simple_spreadsheet.xlsx} +0 -0
- data/test/files/simple_spreadsheet.xml +225 -0
- data/test/{simple_spreadsheet_from_italo.ods → files/simple_spreadsheet_from_italo.ods} +0 -0
- data/test/{simple_spreadsheet_from_italo.xls → files/simple_spreadsheet_from_italo.xls} +0 -0
- data/test/files/simple_spreadsheet_from_italo.xml +242 -0
- data/test/{so_datetime.csv → files/so_datetime.csv} +0 -0
- data/test/{style.ods → files/style.ods} +0 -0
- data/test/{style.xls → files/style.xls} +0 -0
- data/test/{style.xlsx → files/style.xlsx} +0 -0
- data/test/files/style.xml +154 -0
- data/test/{time-test.csv → files/time-test.csv} +0 -0
- data/test/{time-test.ods → files/time-test.ods} +0 -0
- data/test/{time-test.xls → files/time-test.xls} +0 -0
- data/test/{time-test.xlsx → files/time-test.xlsx} +0 -0
- data/test/files/time-test.xml +131 -0
- data/test/{type_excel.ods → files/type_excel.ods} +0 -0
- data/test/{type_excel.xlsx → files/type_excel.xlsx} +0 -0
- data/test/{type_excelx.ods → files/type_excelx.ods} +0 -0
- data/test/{type_excelx.xls → files/type_excelx.xls} +0 -0
- data/test/{type_openoffice.xls → files/type_openoffice.xls} +0 -0
- data/test/{type_openoffice.xlsx → files/type_openoffice.xlsx} +0 -0
- data/test/{whitespace.ods → files/whitespace.ods} +0 -0
- data/test/{whitespace.xls → files/whitespace.xls} +0 -0
- data/test/{whitespace.xlsx → files/whitespace.xlsx} +0 -0
- data/test/files/whitespace.xml +184 -0
- data/test/test_generic_spreadsheet.rb +257 -0
- data/test/test_helper.rb +167 -27
- data/test/test_roo.rb +1178 -930
- data/website/index.html +385 -0
- data/website/index.txt +423 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +130 -0
- data/website/template.rhtml +48 -0
- metadata +151 -121
- data/README.txt +0 -110
- data/lib/roo/.csv.rb.swp +0 -0
@@ -1,39 +1,23 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'stringio'
|
3
5
|
|
4
6
|
# Base class for all other types of spreadsheets
|
5
|
-
class GenericSpreadsheet
|
7
|
+
class Roo::GenericSpreadsheet
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
TEMP_PREFIX = "oo_"
|
6
11
|
|
7
|
-
attr_reader :default_sheet
|
12
|
+
attr_reader :default_sheet, :headers
|
8
13
|
|
9
14
|
# sets the line with attribute names (default: 1)
|
10
15
|
attr_accessor :header_line
|
11
16
|
|
12
17
|
protected
|
13
18
|
|
14
|
-
# Helper function for development
|
15
|
-
def fremdrechner? #nodoc
|
16
|
-
eigener = [
|
17
|
-
'C:\Users\thopre',
|
18
|
-
'c:/Users/thopre',
|
19
|
-
'/c/Users/thopre',
|
20
|
-
'/home/tp',
|
21
|
-
].include? ENV['HOME']
|
22
|
-
# if eigener
|
23
|
-
# puts "fremdrechner? ==> false"
|
24
|
-
# else
|
25
|
-
# puts "fremdrechner? ==> true"
|
26
|
-
# end
|
27
|
-
! eigener
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.next_tmpdir
|
31
|
-
tmpdir = "oo_"+$$.to_s+"_"+sprintf("%010d",rand(10_000_000_000))
|
32
|
-
tmpdir
|
33
|
-
end
|
34
|
-
|
35
19
|
def self.split_coordinate(str)
|
36
|
-
letter,number = GenericSpreadsheet.split_coord(str)
|
20
|
+
letter,number = Roo::GenericSpreadsheet.split_coord(str)
|
37
21
|
x = letter_to_number(letter)
|
38
22
|
y = number
|
39
23
|
return y, x
|
@@ -52,41 +36,47 @@ class GenericSpreadsheet
|
|
52
36
|
|
53
37
|
public
|
54
38
|
|
39
|
+
def initialize(filename, packed=nil, file_warning=:error, tmpdir=nil)
|
40
|
+
@cell = Hash.new{|h,k| h[k] = {}}
|
41
|
+
@cell_type = Hash.new{|h,k| h[k] = {}}
|
42
|
+
@cells_read = {}
|
43
|
+
|
44
|
+
@first_row = {}
|
45
|
+
@last_row = {}
|
46
|
+
@first_column = {}
|
47
|
+
@last_column = {}
|
48
|
+
|
49
|
+
@style = {}
|
50
|
+
@style_defaults = Hash.new { |h,k| h[k] = [] }
|
51
|
+
@style_definitions = {}
|
52
|
+
|
53
|
+
@default_sheet = self.sheets.first
|
54
|
+
@formula = {}
|
55
|
+
@header_line = 1
|
56
|
+
end
|
57
|
+
|
55
58
|
# sets the working sheet in the document
|
56
59
|
# 'sheet' can be a number (1 = first sheet) or the name of a sheet.
|
57
60
|
def default_sheet=(sheet)
|
58
|
-
|
59
|
-
if sheet > 0 and sheet <= sheets.length
|
60
|
-
sheet = self.sheets[sheet-1]
|
61
|
-
else
|
62
|
-
raise RangeError
|
63
|
-
end
|
64
|
-
elsif sheet.kind_of?(String)
|
65
|
-
raise RangeError if ! self.sheets.include?(sheet)
|
66
|
-
else
|
67
|
-
raise TypeError, "what are you trying to set as default sheet?"
|
68
|
-
end
|
61
|
+
validate_sheet!(sheet)
|
69
62
|
@default_sheet = sheet
|
70
|
-
check_default_sheet
|
71
63
|
@first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil
|
72
64
|
@cells_read[sheet] = false
|
73
65
|
end
|
74
66
|
|
75
67
|
# first non-empty column as a letter
|
76
68
|
def first_column_as_letter(sheet=nil)
|
77
|
-
GenericSpreadsheet.number_to_letter(first_column(sheet))
|
69
|
+
Roo::GenericSpreadsheet.number_to_letter(first_column(sheet))
|
78
70
|
end
|
79
71
|
|
80
72
|
# last non-empty column as a letter
|
81
73
|
def last_column_as_letter(sheet=nil)
|
82
|
-
GenericSpreadsheet.number_to_letter(last_column(sheet))
|
74
|
+
Roo::GenericSpreadsheet.number_to_letter(last_column(sheet))
|
83
75
|
end
|
84
76
|
|
85
77
|
# returns the number of the first non-empty row
|
86
78
|
def first_row(sheet=nil)
|
87
|
-
|
88
|
-
sheet = @default_sheet
|
89
|
-
end
|
79
|
+
sheet ||= @default_sheet
|
90
80
|
read_cells(sheet) unless @cells_read[sheet]
|
91
81
|
if @first_row[sheet]
|
92
82
|
return @first_row[sheet]
|
@@ -94,8 +84,7 @@ class GenericSpreadsheet
|
|
94
84
|
impossible_value = 999_999 # more than a spreadsheet can hold
|
95
85
|
result = impossible_value
|
96
86
|
@cell[sheet].each_pair {|key,value|
|
97
|
-
y
|
98
|
-
y = y.to_i
|
87
|
+
y = key.first.to_i # _to_string(key).split(',')
|
99
88
|
result = [result, y].min if value
|
100
89
|
} if @cell[sheet]
|
101
90
|
result = nil if result == impossible_value
|
@@ -105,7 +94,7 @@ class GenericSpreadsheet
|
|
105
94
|
|
106
95
|
# returns the number of the last non-empty row
|
107
96
|
def last_row(sheet=nil)
|
108
|
-
sheet
|
97
|
+
sheet ||= @default_sheet
|
109
98
|
read_cells(sheet) unless @cells_read[sheet]
|
110
99
|
if @last_row[sheet]
|
111
100
|
return @last_row[sheet]
|
@@ -113,8 +102,7 @@ class GenericSpreadsheet
|
|
113
102
|
impossible_value = 0
|
114
103
|
result = impossible_value
|
115
104
|
@cell[sheet].each_pair {|key,value|
|
116
|
-
y
|
117
|
-
y = y.to_i
|
105
|
+
y = key.first.to_i # _to_string(key).split(',')
|
118
106
|
result = [result, y].max if value
|
119
107
|
} if @cell[sheet]
|
120
108
|
result = nil if result == impossible_value
|
@@ -124,9 +112,7 @@ class GenericSpreadsheet
|
|
124
112
|
|
125
113
|
# returns the number of the first non-empty column
|
126
114
|
def first_column(sheet=nil)
|
127
|
-
|
128
|
-
sheet = @default_sheet
|
129
|
-
end
|
115
|
+
sheet ||= @default_sheet
|
130
116
|
read_cells(sheet) unless @cells_read[sheet]
|
131
117
|
if @first_column[sheet]
|
132
118
|
return @first_column[sheet]
|
@@ -134,8 +120,7 @@ class GenericSpreadsheet
|
|
134
120
|
impossible_value = 999_999 # more than a spreadsheet can hold
|
135
121
|
result = impossible_value
|
136
122
|
@cell[sheet].each_pair {|key,value|
|
137
|
-
|
138
|
-
x = x # .to_i
|
123
|
+
x = key.last.to_i # _to_string(key).split(',')
|
139
124
|
result = [result, x].min if value
|
140
125
|
} if @cell[sheet]
|
141
126
|
result = nil if result == impossible_value
|
@@ -145,7 +130,7 @@ class GenericSpreadsheet
|
|
145
130
|
|
146
131
|
# returns the number of the last non-empty column
|
147
132
|
def last_column(sheet=nil)
|
148
|
-
sheet
|
133
|
+
sheet ||= @default_sheet
|
149
134
|
read_cells(sheet) unless @cells_read[sheet]
|
150
135
|
if @last_column[sheet]
|
151
136
|
return @last_column[sheet]
|
@@ -153,8 +138,7 @@ class GenericSpreadsheet
|
|
153
138
|
impossible_value = 0
|
154
139
|
result = impossible_value
|
155
140
|
@cell[sheet].each_pair {|key,value|
|
156
|
-
|
157
|
-
x = x.to_i
|
141
|
+
x = key.last.to_i # _to_string(key).split(',')
|
158
142
|
result = [result, x].max if value
|
159
143
|
} if @cell[sheet]
|
160
144
|
result = nil if result == impossible_value
|
@@ -166,7 +150,7 @@ class GenericSpreadsheet
|
|
166
150
|
# you can add additional attributes with the prefix parameter like:
|
167
151
|
# oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
|
168
152
|
def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
|
169
|
-
sheet
|
153
|
+
sheet ||= @default_sheet
|
170
154
|
result = "--- \n"
|
171
155
|
return '' unless first_row # empty result if there is no first_row in a sheet
|
172
156
|
|
@@ -181,7 +165,7 @@ class GenericSpreadsheet
|
|
181
165
|
result << " col: #{col} \n"
|
182
166
|
result << " celltype: #{self.celltype(row,col,sheet)} \n"
|
183
167
|
if self.celltype(row,col,sheet) == :time
|
184
|
-
result << " value: #{GenericSpreadsheet.integer_to_timestring( self.cell(row,col,sheet))} \n"
|
168
|
+
result << " value: #{Roo::GenericSpreadsheet.integer_to_timestring( self.cell(row,col,sheet))} \n"
|
185
169
|
else
|
186
170
|
result << " value: #{self.cell(row,col,sheet)} \n"
|
187
171
|
end
|
@@ -193,152 +177,117 @@ class GenericSpreadsheet
|
|
193
177
|
|
194
178
|
# write the current spreadsheet to stdout or into a file
|
195
179
|
def to_csv(filename=nil,sheet=nil)
|
196
|
-
sheet
|
180
|
+
sheet ||= @default_sheet
|
197
181
|
if filename
|
198
|
-
|
199
|
-
|
200
|
-
|
182
|
+
File.open(filename,"w") do |file|
|
183
|
+
write_csv_content(file,sheet)
|
184
|
+
end
|
185
|
+
return true
|
201
186
|
else
|
202
|
-
|
187
|
+
sio = StringIO.new
|
188
|
+
write_csv_content(sio,sheet)
|
189
|
+
sio.rewind
|
190
|
+
return sio.read
|
203
191
|
end
|
204
|
-
true
|
205
192
|
end
|
206
193
|
|
207
194
|
# returns a matrix object from the whole sheet or a rectangular area of a sheet
|
208
195
|
def to_matrix(from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
|
209
|
-
|
210
|
-
arr = []
|
211
|
-
pos = 0
|
212
|
-
return Matrix.rows([]) unless first_row
|
196
|
+
require 'matrix'
|
213
197
|
|
214
|
-
|
215
|
-
|
216
|
-
(from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
|
198
|
+
sheet ||= @default_sheet
|
199
|
+
return Matrix.empty unless first_row
|
217
200
|
|
218
|
-
|
201
|
+
Matrix.rows((from_row||first_row(sheet)).upto(to_row||last_row(sheet)).map do |row|
|
202
|
+
(from_column||first_column(sheet)).upto(to_column||last_column(sheet)).map do |col|
|
203
|
+
cell(row,col)
|
219
204
|
end
|
220
|
-
|
221
|
-
pos += 1
|
222
|
-
end
|
223
|
-
Matrix.rows(arr)
|
205
|
+
end)
|
224
206
|
end
|
225
207
|
|
226
208
|
# find a row either by row number or a condition
|
227
209
|
# Caution: this works only within the default sheet -> set default_sheet before you call this method
|
228
210
|
# (experimental. see examples in the test_roo.rb file)
|
229
211
|
def find(*args) # :nodoc
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
result_array = true
|
236
|
-
end
|
237
|
-
}
|
238
|
-
end
|
239
|
-
}
|
240
|
-
column_with = {}
|
241
|
-
1.upto(last_column) do |col|
|
242
|
-
column_with[cell(@header_line,col)] = col
|
243
|
-
end
|
244
|
-
result = Array.new
|
212
|
+
options = (args.last.is_a?(Hash) ? args.pop : {})
|
213
|
+
result_array = options[:array]
|
214
|
+
header_for = Hash[1.upto(last_column).map do |col|
|
215
|
+
[col, cell(@header_line,col)]
|
216
|
+
end]
|
245
217
|
#-- id
|
246
218
|
if args[0].class == Fixnum
|
247
219
|
rownum = args[0]
|
248
220
|
if @header_line
|
249
|
-
|
221
|
+
[Hash[1.upto(self.row().size).map {|j|
|
222
|
+
[header_for.fetch(j), cell(rownum,j)]
|
223
|
+
}]]
|
250
224
|
else
|
251
|
-
|
252
|
-
|
253
|
-
1.upto(self.row(rownum).size) {|j|
|
254
|
-
x = ''
|
255
|
-
column_with.each { |key,val|
|
256
|
-
if val == j
|
257
|
-
x = key
|
258
|
-
end
|
225
|
+
self.row(rownum).size.times.map {|j|
|
226
|
+
cell(rownum,j + 1)
|
259
227
|
}
|
260
|
-
if @header_line
|
261
|
-
tmp[x] = cell(rownum,j)
|
262
|
-
else
|
263
|
-
tmp[j-1] = cell(rownum,j)
|
264
|
-
end
|
265
|
-
|
266
|
-
}
|
267
|
-
if @header_line
|
268
|
-
result = [ tmp ]
|
269
|
-
else
|
270
|
-
result = tmp
|
271
228
|
end
|
272
|
-
|
229
|
+
#-- :all
|
273
230
|
elsif args[0] == :all
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
if found > 0
|
293
|
-
tmp = {}
|
294
|
-
1.upto(self.row(i).size) {|j|
|
295
|
-
x = ''
|
296
|
-
column_with.each { |key,val|
|
297
|
-
if val == j
|
298
|
-
x = key
|
299
|
-
end
|
300
|
-
}
|
301
|
-
tmp[x] = cell(i,j)
|
302
|
-
}
|
303
|
-
if result_array
|
304
|
-
result << self.row(i)
|
305
|
-
else
|
306
|
-
result << tmp
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end # :conditions
|
311
|
-
}
|
231
|
+
rows = first_row.upto(last_row)
|
232
|
+
|
233
|
+
# are all conditions met?
|
234
|
+
if (conditions = options[:conditions]) && !conditions.empty?
|
235
|
+
column_with = header_for.invert
|
236
|
+
rows = rows.select do |i|
|
237
|
+
conditions.all? { |key,val| cell(i,column_with[key]) == val }
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
rows.map do |i|
|
242
|
+
if result_array
|
243
|
+
self.row(i)
|
244
|
+
else
|
245
|
+
Hash[1.upto(self.row(i).size).map do |j|
|
246
|
+
[header_for.fetch(j), cell(i,j)]
|
247
|
+
end]
|
248
|
+
end
|
312
249
|
end
|
313
250
|
end
|
314
|
-
result
|
315
251
|
end
|
316
252
|
|
317
253
|
# returns all values in this row as an array
|
318
254
|
# row numbers are 1,2,3,... like in the spreadsheet
|
319
255
|
def row(rownumber,sheet=nil)
|
320
|
-
sheet
|
256
|
+
sheet ||= @default_sheet
|
321
257
|
read_cells(sheet) unless @cells_read[sheet]
|
322
|
-
|
323
|
-
|
324
|
-
result << cell(rownumber,col,sheet)
|
258
|
+
first_column(sheet).upto(last_column(sheet)).map do |col|
|
259
|
+
cell(rownumber,col,sheet)
|
325
260
|
end
|
326
|
-
result
|
327
261
|
end
|
328
262
|
|
329
263
|
# returns all values in this column as an array
|
330
264
|
# column numbers are 1,2,3,... like in the spreadsheet
|
331
265
|
def column(columnnumber,sheet=nil)
|
332
266
|
if columnnumber.class == String
|
333
|
-
columnnumber = Excel.letter_to_number(columnnumber)
|
267
|
+
columnnumber = Roo::Excel.letter_to_number(columnnumber)
|
334
268
|
end
|
335
|
-
sheet
|
269
|
+
sheet ||= @default_sheet
|
336
270
|
read_cells(sheet) unless @cells_read[sheet]
|
337
|
-
|
338
|
-
|
339
|
-
result << cell(row,columnnumber,sheet)
|
271
|
+
first_row(sheet).upto(last_row(sheet)).map do |row|
|
272
|
+
cell(row,columnnumber,sheet)
|
340
273
|
end
|
341
|
-
|
274
|
+
end
|
275
|
+
|
276
|
+
# set a cell to a certain value
|
277
|
+
# (this will not be saved back to the spreadsheet file!)
|
278
|
+
def set(row,col,value,sheet=nil) #:nodoc:
|
279
|
+
sheet ||= @default_sheet
|
280
|
+
read_cells(sheet) unless @cells_read[sheet]
|
281
|
+
row, col = normalize(row,col)
|
282
|
+
cell_type = case value
|
283
|
+
when Fixnum then :float
|
284
|
+
when String, Float then :string
|
285
|
+
else
|
286
|
+
raise ArgumentError, "Type for #{value} not set"
|
287
|
+
end
|
288
|
+
|
289
|
+
set_value(row,col,value,sheet)
|
290
|
+
set_type(row,col,cell_type,sheet)
|
342
291
|
end
|
343
292
|
|
344
293
|
# reopens and read a spreadsheet document
|
@@ -356,21 +305,12 @@ class GenericSpreadsheet
|
|
356
305
|
|
357
306
|
# true if cell is empty
|
358
307
|
def empty?(row, col, sheet=nil)
|
359
|
-
sheet
|
360
|
-
read_cells(sheet) unless @cells_read[sheet] or self.class == Excel
|
308
|
+
sheet ||= @default_sheet
|
309
|
+
read_cells(sheet) unless @cells_read[sheet] or self.class == Roo::Excel
|
361
310
|
row,col = normalize(row,col)
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
false
|
366
|
-
end
|
367
|
-
|
368
|
-
# recursively removes the current temporary directory
|
369
|
-
# this is only needed if you work with zipped files or files via the web
|
370
|
-
def remove_tmp
|
371
|
-
if File.exists?(@tmpdir)
|
372
|
-
FileUtils::rm_r(@tmpdir)
|
373
|
-
end
|
311
|
+
contents = cell(row, col, sheet)
|
312
|
+
!contents || (celltype(row, col, sheet) == :string && contents.empty?) \
|
313
|
+
|| (row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet))
|
374
314
|
end
|
375
315
|
|
376
316
|
# returns information of the spreadsheet document and all sheets within
|
@@ -388,8 +328,8 @@ class GenericSpreadsheet
|
|
388
328
|
else
|
389
329
|
result << " First row: #{first_row}\n"
|
390
330
|
result << " Last row: #{last_row}\n"
|
391
|
-
result << " First column: #{GenericSpreadsheet.number_to_letter(first_column)}\n"
|
392
|
-
result << " Last column: #{GenericSpreadsheet.number_to_letter(last_column)}"
|
331
|
+
result << " First column: #{Roo::GenericSpreadsheet.number_to_letter(first_column)}\n"
|
332
|
+
result << " Last column: #{Roo::GenericSpreadsheet.number_to_letter(last_column)}"
|
393
333
|
end
|
394
334
|
result << "\n" if sheet != sheets.last
|
395
335
|
n += 1
|
@@ -399,7 +339,7 @@ class GenericSpreadsheet
|
|
399
339
|
|
400
340
|
# returns an XML representation of all sheets of a spreadsheet file
|
401
341
|
def to_xml
|
402
|
-
|
342
|
+
Nokogiri::XML::Builder.new do |xml|
|
403
343
|
xml.spreadsheet {
|
404
344
|
self.sheets.each do |sheet|
|
405
345
|
self.default_sheet = sheet
|
@@ -420,8 +360,7 @@ class GenericSpreadsheet
|
|
420
360
|
}
|
421
361
|
end
|
422
362
|
}
|
423
|
-
end
|
424
|
-
return builder.to_xml
|
363
|
+
end.to_xml
|
425
364
|
end
|
426
365
|
|
427
366
|
# when a method like spreadsheet.a42 is called
|
@@ -430,12 +369,12 @@ class GenericSpreadsheet
|
|
430
369
|
# #aa42 => #cell('aa',42)
|
431
370
|
# #aa42('Sheet1') => #cell('aa',42,'Sheet1')
|
432
371
|
if m =~ /^([a-z]+)(\d)$/
|
433
|
-
col = GenericSpreadsheet.letter_to_number($1)
|
372
|
+
col = Roo::GenericSpreadsheet.letter_to_number($1)
|
434
373
|
row = $2.to_i
|
435
|
-
if args.
|
436
|
-
|
374
|
+
if args.empty?
|
375
|
+
cell(row,col)
|
437
376
|
else
|
438
|
-
|
377
|
+
cell(row,col,args.first)
|
439
378
|
end
|
440
379
|
else
|
441
380
|
super
|
@@ -448,7 +387,7 @@ class GenericSpreadsheet
|
|
448
387
|
# [row, col, formula]
|
449
388
|
def formulas(sheet=nil)
|
450
389
|
theformulas = Array.new
|
451
|
-
sheet
|
390
|
+
sheet ||= @default_sheet
|
452
391
|
read_cells(sheet) unless @cells_read[sheet]
|
453
392
|
return theformulas unless first_row(sheet) # if there is no first row then
|
454
393
|
# there can't be formulas
|
@@ -463,14 +402,127 @@ class GenericSpreadsheet
|
|
463
402
|
end
|
464
403
|
=end
|
465
404
|
|
405
|
+
|
406
|
+
|
407
|
+
# FestivalBobcats fork changes begin here
|
408
|
+
|
409
|
+
|
410
|
+
|
411
|
+
# access different worksheets by calling spreadsheet.sheet(1)
|
412
|
+
# or spreadsheet.sheet('SHEETNAME')
|
413
|
+
def sheet(index,name=false)
|
414
|
+
@default_sheet = String === index ? index : self.sheets[index]
|
415
|
+
name ? [@default_sheet,self] : self
|
416
|
+
end
|
417
|
+
|
418
|
+
# iterate through all worksheets of a document
|
419
|
+
def each_with_pagename
|
420
|
+
self.sheets.each do |s|
|
421
|
+
yield sheet(s,true)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# by passing in headers as options, this method returns
|
426
|
+
# specific columns from your header assignment
|
427
|
+
# for example:
|
428
|
+
# xls.sheet('New Prices').parse(:upc => 'UPC', :price => 'Price') would return:
|
429
|
+
# [{:upc => 123456789012, :price => 35.42},..]
|
430
|
+
|
431
|
+
# the queries are matched with regex, so regex options can be passed in
|
432
|
+
# such as :price => '^(Cost|Price)'
|
433
|
+
# case insensitive by default
|
434
|
+
|
435
|
+
|
436
|
+
# by using the :header_search option, you can query for headers
|
437
|
+
# and return a hash of every row with the keys set to the header result
|
438
|
+
# for example:
|
439
|
+
# xls.sheet('New Prices').parse(:header_search => ['UPC*SKU','^Price*\sCost\s'])
|
440
|
+
|
441
|
+
# that example searches for a column titled either UPC or SKU and another
|
442
|
+
# column titled either Price or Cost (regex characters allowed)
|
443
|
+
# * is the wildcard character
|
444
|
+
|
445
|
+
# you can also pass in a :clean => true option to strip the sheet of
|
446
|
+
# odd unicode characters and white spaces around columns
|
447
|
+
|
448
|
+
def each(options={})
|
449
|
+
if options.empty?
|
450
|
+
1.upto(last_row) do |line|
|
451
|
+
yield row(line)
|
452
|
+
end
|
453
|
+
else
|
454
|
+
if options[:clean]
|
455
|
+
options.delete(:clean)
|
456
|
+
@cleaned ||= {}
|
457
|
+
@cleaned[@default_sheet] || clean_sheet(@default_sheet)
|
458
|
+
end
|
459
|
+
|
460
|
+
if options[:header_search]
|
461
|
+
@headers = nil
|
462
|
+
@header_line = row_with(options[:header_search])
|
463
|
+
elsif [:first_row,true].include?(options[:headers])
|
464
|
+
@headers = []
|
465
|
+
row(first_row).each_with_index {|x,i| @headers << [x,i + 1]}
|
466
|
+
else
|
467
|
+
set_headers(options)
|
468
|
+
end
|
469
|
+
|
470
|
+
headers = @headers ||
|
471
|
+
Hash[(first_column..last_column).map do |col|
|
472
|
+
[cell(@header_line,col), col]
|
473
|
+
end]
|
474
|
+
|
475
|
+
@header_line.upto(last_row) do |line|
|
476
|
+
yield(Hash[headers.map {|k,v| [k,cell(line,v)]}])
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
def parse(options={})
|
482
|
+
ary = []
|
483
|
+
if block_given?
|
484
|
+
each(options) {|row| ary << yield(row)}
|
485
|
+
else
|
486
|
+
each(options) {|row| ary << row}
|
487
|
+
end
|
488
|
+
ary
|
489
|
+
end
|
490
|
+
|
491
|
+
def row_with(query,return_headers=false)
|
492
|
+
query.map! {|x| Array(x.split('*'))}
|
493
|
+
line_no = 0
|
494
|
+
each do |row|
|
495
|
+
line_no += 1
|
496
|
+
# makes sure headers is the first part of wildcard search for priority
|
497
|
+
# ex. if UPC and SKU exist for UPC*SKU search, UPC takes the cake
|
498
|
+
headers = query.map do |q|
|
499
|
+
q.map {|i| row.grep(/#{i}/i)[0]}.compact[0]
|
500
|
+
end.compact
|
501
|
+
|
502
|
+
if headers.length == query.length
|
503
|
+
@header_line = line_no
|
504
|
+
return return_headers ? headers : line_no
|
505
|
+
elsif line_no > 100
|
506
|
+
raise "Couldn't find header row."
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
# this method lets you find the worksheet with the most data
|
512
|
+
def longest_sheet
|
513
|
+
sheet(@workbook.worksheets.inject {|m,o|
|
514
|
+
o.row_count > m.row_count ? o : m
|
515
|
+
}.name)
|
516
|
+
end
|
517
|
+
|
466
518
|
protected
|
467
519
|
|
468
|
-
def file_type_check(filename, ext, name, packed=nil)
|
520
|
+
def file_type_check(filename, ext, name, warning_level, packed=nil)
|
469
521
|
new_expression = {
|
470
|
-
'.ods' => 'Openoffice.new',
|
471
|
-
'.xls' => 'Excel.new',
|
472
|
-
'.xlsx' => 'Excelx.new',
|
473
|
-
'.csv' => 'Csv.new',
|
522
|
+
'.ods' => 'Roo::Openoffice.new',
|
523
|
+
'.xls' => 'Roo::Excel.new',
|
524
|
+
'.xlsx' => 'Roo::Excelx.new',
|
525
|
+
'.csv' => 'Roo::Csv.new',
|
474
526
|
}
|
475
527
|
if packed == :zip
|
476
528
|
# lalala.ods.zip => lalala.ods
|
@@ -480,12 +532,12 @@ class GenericSpreadsheet
|
|
480
532
|
end
|
481
533
|
case ext
|
482
534
|
when '.ods', '.xls', '.xlsx', '.csv'
|
483
|
-
correct_class = "use #{new_expression[ext]} to handle #{ext} spreadsheet files"
|
535
|
+
correct_class = "use #{new_expression[ext]} to handle #{ext} spreadsheet files. This has #{File.extname(filename).downcase}"
|
484
536
|
else
|
485
537
|
raise "unknown file type: #{ext}"
|
486
538
|
end
|
487
539
|
if File.extname(filename).downcase != ext
|
488
|
-
case
|
540
|
+
case warning_level
|
489
541
|
when :error
|
490
542
|
warn correct_class
|
491
543
|
raise TypeError, "#{filename} is not #{name} file"
|
@@ -495,7 +547,7 @@ class GenericSpreadsheet
|
|
495
547
|
when :ignore
|
496
548
|
# ignore
|
497
549
|
else
|
498
|
-
raise "#{
|
550
|
+
raise "#{warning_level} illegal state of file_warning"
|
499
551
|
end
|
500
552
|
end
|
501
553
|
end
|
@@ -506,9 +558,7 @@ class GenericSpreadsheet
|
|
506
558
|
# Zugriff mit numerischen Keys schneller ist.
|
507
559
|
def key_to_num(str)
|
508
560
|
r,c = str.split(',')
|
509
|
-
r
|
510
|
-
c = c.to_i
|
511
|
-
[r,c]
|
561
|
+
[r.to_i,c.to_i]
|
512
562
|
end
|
513
563
|
|
514
564
|
# see: key_to_num
|
@@ -518,6 +568,47 @@ class GenericSpreadsheet
|
|
518
568
|
|
519
569
|
private
|
520
570
|
|
571
|
+
def make_tmpdir(tmp_root = nil)
|
572
|
+
Dir.mktmpdir(TEMP_PREFIX, tmp_root || ENV['ROO_TMP']) do |tmpdir|
|
573
|
+
yield tmpdir
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
def clean_sheet(sheet)
|
578
|
+
read_cells(sheet) unless @cells_read[sheet]
|
579
|
+
@cell[sheet].each_pair do |coord,value|
|
580
|
+
if String === value
|
581
|
+
@cell[sheet][coord] = sanitize_value(value)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
@cleaned[sheet] = true
|
585
|
+
end
|
586
|
+
|
587
|
+
def sanitize_value(v)
|
588
|
+
v.strip.unpack('U*').select {|b| b < 127}.pack('U*')
|
589
|
+
end
|
590
|
+
|
591
|
+
def set_headers(hash={})
|
592
|
+
# try to find header row with all values or give an error
|
593
|
+
# then create new hash by indexing strings and keeping integers for header array
|
594
|
+
@headers = row_with(hash.values,true)
|
595
|
+
@headers = Hash[hash.keys.zip(@headers.map {|x| header_index(x)})]
|
596
|
+
end
|
597
|
+
|
598
|
+
def header_index(query)
|
599
|
+
row(@header_line).index(query) + first_column
|
600
|
+
end
|
601
|
+
|
602
|
+
def set_value(row,col,value,sheet=nil)
|
603
|
+
sheet ||= @default_sheet
|
604
|
+
@cell[sheet][[row,col]] = value
|
605
|
+
end
|
606
|
+
|
607
|
+
def set_type(row,col,type,sheet=nil)
|
608
|
+
sheet ||= @default_sheet
|
609
|
+
@cell_type[sheet][[row,col]] = type
|
610
|
+
end
|
611
|
+
|
521
612
|
# converts cell coordinate to numeric values of row,col
|
522
613
|
def normalize(row,col)
|
523
614
|
if row.class == String
|
@@ -530,43 +621,57 @@ class GenericSpreadsheet
|
|
530
621
|
end
|
531
622
|
end
|
532
623
|
if col.class == String
|
533
|
-
col = GenericSpreadsheet.letter_to_number(col)
|
624
|
+
col = Roo::GenericSpreadsheet.letter_to_number(col)
|
534
625
|
end
|
535
626
|
return row,col
|
536
627
|
end
|
537
628
|
|
538
|
-
def
|
629
|
+
def uri?(filename)
|
630
|
+
filename.start_with?("http://", "https://")
|
631
|
+
end
|
632
|
+
|
633
|
+
def open_from_uri(uri, tmpdir)
|
539
634
|
require 'open-uri'
|
540
635
|
response = ''
|
541
636
|
begin
|
542
637
|
open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") { |net|
|
543
638
|
response = net.read
|
544
|
-
tempfilename = File.join(
|
545
|
-
|
546
|
-
|
547
|
-
|
639
|
+
tempfilename = File.join(tmpdir, File.basename(uri))
|
640
|
+
File.open(tempfilename,"wb") do |file|
|
641
|
+
file.write(response)
|
642
|
+
end
|
548
643
|
}
|
549
644
|
rescue OpenURI::HTTPError
|
550
645
|
raise "could not open #{uri}"
|
551
646
|
end
|
552
|
-
File.join(
|
647
|
+
File.join(tmpdir, File.basename(uri))
|
553
648
|
end
|
554
|
-
|
555
|
-
def open_from_stream(stream)
|
556
|
-
tempfilename = File.join(
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
File.join(
|
649
|
+
|
650
|
+
def open_from_stream(stream, tmpdir)
|
651
|
+
tempfilename = File.join(tmpdir, "spreadsheet")
|
652
|
+
File.open(tempfilename,"wb") do |file|
|
653
|
+
file.write(stream[7..-1])
|
654
|
+
end
|
655
|
+
File.join(tmpdir, "spreadsheet")
|
561
656
|
end
|
562
657
|
|
658
|
+
LETTERS = %w{A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
|
659
|
+
|
563
660
|
# convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...)
|
564
661
|
def self.number_to_letter(n)
|
565
662
|
letters=""
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
663
|
+
if n > 26
|
664
|
+
while n % 26 == 0 && n != 0
|
665
|
+
letters << 'Z'
|
666
|
+
n = (n - 26) / 26
|
667
|
+
end
|
668
|
+
while n > 0
|
669
|
+
num = n%26
|
670
|
+
letters = LETTERS[num-1] + letters
|
671
|
+
n = (n / 26)
|
672
|
+
end
|
673
|
+
else
|
674
|
+
letters = LETTERS[n-1]
|
570
675
|
end
|
571
676
|
letters
|
572
677
|
end
|
@@ -576,7 +681,7 @@ class GenericSpreadsheet
|
|
576
681
|
result = 0
|
577
682
|
while letters && letters.length > 0
|
578
683
|
character = letters[0,1].upcase
|
579
|
-
num =
|
684
|
+
num = LETTERS.index(character)
|
580
685
|
raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
|
581
686
|
num += 1
|
582
687
|
result = result * 26 + num
|
@@ -585,106 +690,105 @@ class GenericSpreadsheet
|
|
585
690
|
result
|
586
691
|
end
|
587
692
|
|
588
|
-
def unzip(filename)
|
589
|
-
ret = nil
|
693
|
+
def unzip(filename, tmpdir)
|
590
694
|
Zip::ZipFile.open(filename) do |zip|
|
591
|
-
|
695
|
+
process_zipfile_packed(zip, tmpdir)
|
592
696
|
end
|
593
|
-
ret
|
594
697
|
end
|
595
698
|
|
596
699
|
# check if default_sheet was set and exists in sheets-array
|
597
|
-
def
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
700
|
+
def validate_sheet!(sheet)
|
701
|
+
case sheet
|
702
|
+
when nil
|
703
|
+
raise ArgumentError, "Error: sheet 'nil' not valid"
|
704
|
+
when Fixnum
|
705
|
+
self.sheets.fetch(sheet-1) do
|
706
|
+
raise RangeError, "sheet index #{sheet} not found"
|
707
|
+
end
|
708
|
+
when String
|
709
|
+
if !sheets.include? sheet
|
710
|
+
raise RangeError, "sheet '#{sheet}' not found"
|
711
|
+
end
|
712
|
+
else
|
713
|
+
raise TypeError, "not a valid sheet type: #{sheet.inspect}"
|
605
714
|
end
|
606
715
|
end
|
607
716
|
|
608
|
-
def process_zipfile_packed(zip, path='')
|
609
|
-
ret=nil
|
717
|
+
def process_zipfile_packed(zip, tmpdir, path='')
|
610
718
|
if zip.file.file? path
|
611
719
|
# extract and return filename
|
612
|
-
|
613
|
-
|
614
|
-
file.close
|
615
|
-
return File.join(@tmpdir, path)
|
616
|
-
else
|
617
|
-
unless path.empty?
|
618
|
-
path += '/'
|
720
|
+
File.open(File.join(tmpdir, path),"wb") do |file|
|
721
|
+
file.write(zip.read(path))
|
619
722
|
end
|
723
|
+
File.join(tmpdir, path)
|
724
|
+
else
|
725
|
+
ret=nil
|
726
|
+
path += '/' unless path.empty?
|
620
727
|
zip.dir.foreach(path) do |filename|
|
621
|
-
ret = process_zipfile_packed(zip, path + filename)
|
728
|
+
ret = process_zipfile_packed(zip, tmpdir, path + filename)
|
622
729
|
end
|
730
|
+
ret
|
623
731
|
end
|
624
|
-
ret
|
625
732
|
end
|
626
733
|
|
627
734
|
# Write all cells to the csv file. File can be a filename or nil. If the this
|
628
735
|
# parameter is nil the output goes to STDOUT
|
629
736
|
def write_csv_content(file=nil,sheet=nil)
|
630
|
-
file
|
737
|
+
file ||= STDOUT
|
631
738
|
if first_row(sheet) # sheet is not empty
|
632
739
|
1.upto(last_row(sheet)) do |row|
|
633
740
|
1.upto(last_column(sheet)) do |col|
|
634
741
|
file.print(",") if col > 1
|
635
|
-
|
636
|
-
onecelltype = celltype(row,col,sheet)
|
637
|
-
file.print one_cell_output(onecelltype,onecell,empty?(row,col,sheet))
|
742
|
+
file.print cell_to_csv(row,col,sheet)
|
638
743
|
end
|
639
744
|
file.print("\n")
|
640
745
|
end # sheet not empty
|
641
746
|
end
|
642
747
|
end
|
643
748
|
|
644
|
-
# The content of a cell in the csv output
|
645
|
-
def
|
646
|
-
|
647
|
-
|
648
|
-
str += ''
|
749
|
+
# The content of a cell in the csv output
|
750
|
+
def cell_to_csv(row, col, sheet)
|
751
|
+
if empty?(row,col,sheet)
|
752
|
+
''
|
649
753
|
else
|
650
|
-
|
754
|
+
onecell = cell(row,col,sheet)
|
755
|
+
|
756
|
+
case celltype(row,col,sheet)
|
651
757
|
when :string
|
652
758
|
unless onecell.empty?
|
653
|
-
|
654
|
-
str << ('"'+one+'"')
|
759
|
+
%{"#{onecell.gsub(/"/,'""')}"}
|
655
760
|
end
|
656
761
|
when :float, :percentage
|
657
762
|
if onecell == onecell.to_i
|
658
|
-
|
763
|
+
onecell.to_i.to_s
|
659
764
|
else
|
660
|
-
|
765
|
+
onecell.to_s
|
661
766
|
end
|
662
767
|
when :formula
|
663
|
-
|
768
|
+
case onecell
|
769
|
+
when String
|
664
770
|
unless onecell.empty?
|
665
|
-
|
666
|
-
str << '"'+one+'"'
|
771
|
+
%{"#{onecell.gsub(/"/,'""')}"}
|
667
772
|
end
|
668
|
-
|
773
|
+
when Float
|
669
774
|
if onecell == onecell.to_i
|
670
|
-
|
775
|
+
onecell.to_i.to_s
|
671
776
|
else
|
672
|
-
|
777
|
+
onecell.to_s
|
673
778
|
end
|
779
|
+
when DateTime
|
780
|
+
onecell.to_s
|
674
781
|
else
|
675
|
-
raise "unhandled onecell-class
|
782
|
+
raise "unhandled onecell-class #{onecell.class}"
|
676
783
|
end
|
677
|
-
when :date
|
678
|
-
|
784
|
+
when :date, :datetime
|
785
|
+
onecell.to_s
|
679
786
|
when :time
|
680
|
-
|
681
|
-
when :datetime
|
682
|
-
str << onecell.to_s
|
787
|
+
Roo::GenericSpreadsheet.integer_to_timestring(onecell)
|
683
788
|
else
|
684
|
-
raise "unhandled celltype "
|
685
|
-
end
|
789
|
+
raise "unhandled celltype #{celltype(row,col,sheet)}"
|
790
|
+
end || ""
|
686
791
|
end
|
687
|
-
str
|
688
792
|
end
|
689
793
|
|
690
794
|
# converts an integer value to a time string like '02:05:06'
|
@@ -696,5 +800,4 @@ class GenericSpreadsheet
|
|
696
800
|
s = content
|
697
801
|
sprintf("%02d:%02d:%02d",h,m,s)
|
698
802
|
end
|
699
|
-
|
700
803
|
end
|