roo 0.7.0 → 0.8.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 +4 -0
- data/Manifest.txt +2 -0
- data/README.txt +0 -1
- data/Rakefile +12 -2
- data/examples/write_me.rb +33 -0
- data/lib/roo.rb +1 -0
- data/lib/roo/excel.rb +53 -94
- data/lib/roo/generic_spreadsheet.rb +303 -0
- data/lib/roo/google.rb +667 -152
- data/lib/roo/openoffice.rb +13 -259
- data/lib/roo/version.rb +1 -1
- data/test/test_roo.rb +898 -453
- data/website/index.html +74 -11
- data/website/index.txt +52 -9
- metadata +6 -4
data/History.txt
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
== 0.8.0 2007-12-15
|
2
|
+
* 2 enhancements:
|
3
|
+
* Google online spreadsheets were implemented
|
4
|
+
* some methods common to more than one class were factored out to the GenericSpreadsheet (virtual) class
|
1
5
|
== 0.7.0 2007-11-23
|
2
6
|
* 6 enhancements:
|
3
7
|
* Openoffice/Excel: the most methods can be called with an option 'sheet'
|
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'roo', 'version')
|
|
14
14
|
|
15
15
|
AUTHOR = 'Thomas Preymesser' # can also be an array of Authors
|
16
16
|
EMAIL = "thopre@gmail.com"
|
17
|
-
DESCRIPTION = "roo can access the contents of OpenOffice-
|
17
|
+
DESCRIPTION = "roo can access the contents of OpenOffice-, Excel- or Google-Spreadsheets"
|
18
18
|
GEM_NAME = 'roo' # what ppl will type to install your gem
|
19
19
|
|
20
20
|
@config_file = "~/.rubyforge/user-config.yml"
|
@@ -107,7 +107,7 @@ desc 'Generate and upload website files'
|
|
107
107
|
task :website => [:website_generate, :website_upload]
|
108
108
|
|
109
109
|
desc 'Release the website and new gem version'
|
110
|
-
task :deploy => [:check_version, :website, :release] do
|
110
|
+
task :deploy => [:check_log_params, :check_version, :website, :release ] do
|
111
111
|
puts "Remember to create SVN tag:"
|
112
112
|
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
|
113
113
|
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
|
@@ -115,6 +115,16 @@ task :deploy => [:check_version, :website, :release] do
|
|
115
115
|
puts "Tagging release #{CHANGES}"
|
116
116
|
end
|
117
117
|
|
118
|
+
desc 'Check to ensure the LOG_* constant in Test are set off'
|
119
|
+
task :check_log_params do
|
120
|
+
require 'test/test_roo'
|
121
|
+
if DISPLAY_LOG
|
122
|
+
raise 'please turn off the DISPLAY_LOG constant for deployment!'
|
123
|
+
end
|
124
|
+
if DB_LOG
|
125
|
+
raise 'please turn off the DB_LOG constant for deployment!'
|
126
|
+
end
|
127
|
+
end
|
118
128
|
desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
|
119
129
|
task :local_deploy => [:website_generate, :install_gem]
|
120
130
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'roo'
|
3
|
+
|
4
|
+
#-- create a new spreadsheet within your google-spreadsheets and paste
|
5
|
+
#-- the 'key' parameter in the spreadsheet URL
|
6
|
+
MAXTRIES = 1000
|
7
|
+
print "what's your name? "
|
8
|
+
my_name = gets.chomp
|
9
|
+
print "where do you live? "
|
10
|
+
my_location = gets.chomp
|
11
|
+
print "your message? (if left blank, only your name and location will be inserted) "
|
12
|
+
my_message = gets.chomp
|
13
|
+
spreadsheet = Google.new('ptu6bbahNZpY0N0RrxQbWdw')
|
14
|
+
spreadsheet.default_sheet = 'Sheet1'
|
15
|
+
success = false
|
16
|
+
MAXTRIES.times do
|
17
|
+
col = rand(10)+1
|
18
|
+
row = rand(10)+1
|
19
|
+
if spreadsheet.empty?(row,col)
|
20
|
+
if my_message.empty?
|
21
|
+
text = Time.now.to_s+" "+"Greetings from #{my_name} (#{my_location})"
|
22
|
+
else
|
23
|
+
text = Time.now.to_s+" "+"#{my_message} from #{my_name} (#{my_location})"
|
24
|
+
end
|
25
|
+
spreadsheet.set_value(row,col,text)
|
26
|
+
puts "message written to row #{row}, column #{col}"
|
27
|
+
success = true
|
28
|
+
break
|
29
|
+
end
|
30
|
+
puts "Row #{row}, column #{col} already occupied, trying again..."
|
31
|
+
end
|
32
|
+
puts "no empty cell found within #{MAXTRIES} tries" if !success
|
33
|
+
|
data/lib/roo.rb
CHANGED
data/lib/roo/excel.rb
CHANGED
@@ -1,24 +1,25 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
gem 'parseexcel', '>= 0.5.2'
|
3
3
|
require 'parseexcel'
|
4
|
+
CHARGUESS=false
|
5
|
+
require 'charguess' if CHARGUESS
|
4
6
|
|
5
7
|
module Spreadsheet
|
6
8
|
module ParseExcel
|
7
9
|
class Worksheet
|
8
|
-
|
9
|
-
|
10
|
+
include Enumerable
|
11
|
+
attr_reader :min_row, :max_row, :min_col, :max_col
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
16
|
|
15
|
-
|
16
|
-
# from the Openoffice class in the same way.
|
17
|
-
# Parameter packed: :zip - File is a zip-file
|
18
|
-
class Excel < Openoffice
|
17
|
+
class Excel < GenericSpreadsheet #Openoffice
|
19
18
|
|
20
19
|
EXCEL_NO_FORMULAS = 'formulas are not supported for excel spreadsheets'
|
21
20
|
|
21
|
+
# Creates a new Excel spreadsheet object.
|
22
|
+
# Parameter packed: :zip - File is a zip-file
|
22
23
|
def initialize(filename, packed = nil)
|
23
24
|
@tmpdir = "oo_"+$$.to_s
|
24
25
|
unless File.exists?(@tmpdir)
|
@@ -26,7 +27,7 @@ class Excel < Openoffice
|
|
26
27
|
end
|
27
28
|
filename = open_from_uri(filename) if filename[0,7] == "http://"
|
28
29
|
filename = unzip(filename) if packed and packed == :zip
|
29
|
-
if filename
|
30
|
+
if File.extname(filename) != ".xls"
|
30
31
|
warn "are you sure, this is an excel file?"
|
31
32
|
end
|
32
33
|
@filename = filename
|
@@ -41,7 +42,7 @@ class Excel < Openoffice
|
|
41
42
|
end
|
42
43
|
# @first_row = @last_row = @first_column = @last_column = nil
|
43
44
|
#if ENV["roo_local"] != "thomas-p"
|
44
|
-
|
45
|
+
FileUtils::rm_r(@tmpdir)
|
45
46
|
#end
|
46
47
|
@first_row = Hash.new
|
47
48
|
@last_row = Hash.new
|
@@ -54,9 +55,19 @@ class Excel < Openoffice
|
|
54
55
|
result = []
|
55
56
|
0.upto(@workbook.sheet_count - 1) do |i|
|
56
57
|
# TODO: is there a better way to do conversion?
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
if CHARGUESS
|
59
|
+
encoding = CharGuess::guess(@workbook.worksheet(i).name)
|
60
|
+
encoding = 'unicode' unless encoding
|
61
|
+
|
62
|
+
|
63
|
+
result << Iconv.new('utf-8',encoding).iconv(
|
64
|
+
@workbook.worksheet(i).name
|
65
|
+
)
|
66
|
+
else
|
67
|
+
result << Iconv.new('utf-8','unicode').iconv(
|
68
|
+
@workbook.worksheet(i).name
|
69
|
+
)
|
70
|
+
end
|
60
71
|
end
|
61
72
|
return result
|
62
73
|
end
|
@@ -64,14 +75,14 @@ class Excel < Openoffice
|
|
64
75
|
# sets the working sheet (1,2,3,..)
|
65
76
|
def default_sheet=(n)
|
66
77
|
if n.kind_of?(Fixnum)
|
67
|
-
|
78
|
+
#
|
68
79
|
elsif n.kind_of?(String)
|
69
80
|
raise RangeError if ! self.sheets.include?(n)
|
70
81
|
# parseexcel supports now the name of a sheet
|
71
|
-
@default_sheet = n
|
72
82
|
else
|
73
83
|
raise TypeError, "what are you trying to set as default sheet?"
|
74
84
|
end
|
85
|
+
@default_sheet = n
|
75
86
|
@first_row[n] = @last_row[n] = @first_column[n] = @last_column[n] = nil
|
76
87
|
@cells_read[n] = false
|
77
88
|
end
|
@@ -91,9 +102,9 @@ class Excel < Openoffice
|
|
91
102
|
cell = row_par.at(col-1)
|
92
103
|
return nil unless cell
|
93
104
|
case cell.type
|
94
|
-
|
95
|
-
|
96
|
-
|
105
|
+
when :numeric then return cell.to_f
|
106
|
+
when :text then return cell.to_s('utf-8')
|
107
|
+
when :date then return cell.date
|
97
108
|
else
|
98
109
|
return nil # cell.to_s('utf-8')
|
99
110
|
end
|
@@ -142,9 +153,9 @@ class Excel < Openoffice
|
|
142
153
|
worksheet.row(rownumber-1).each {|cell|
|
143
154
|
if cell
|
144
155
|
case cell.type
|
145
|
-
|
146
|
-
|
147
|
-
|
156
|
+
when :numeric then result << cell.to_i
|
157
|
+
when :text then result << cell.to_s('utf-8')
|
158
|
+
when :date then result << cell.date
|
148
159
|
else
|
149
160
|
result << cell.to_s('utf-8')
|
150
161
|
end
|
@@ -169,12 +180,12 @@ class Excel < Openoffice
|
|
169
180
|
worksheet.each(skip) { |row_par|
|
170
181
|
if defined? row_par.at(columnnumber-1)
|
171
182
|
cell = row_par.at(columnnumber-1)
|
172
|
-
|
183
|
+
#if defined? cell = row_par.at(columnnumber-1)
|
173
184
|
if cell
|
174
185
|
case cell.type
|
175
|
-
|
176
|
-
|
177
|
-
|
186
|
+
when :numeric then result << cell.to_i
|
187
|
+
when :text then result << cell.to_s('utf-8')
|
188
|
+
when :date then result << cell.date
|
178
189
|
else
|
179
190
|
result << cell.to_s('utf-8')
|
180
191
|
end
|
@@ -219,40 +230,23 @@ class Excel < Openoffice
|
|
219
230
|
fr, lr, fc, lc = get_firsts_lasts(sheet)
|
220
231
|
lr
|
221
232
|
end
|
222
|
-
|
223
|
-
# true if a cell is empty
|
224
|
-
def empty?(row, col, sheet=nil)
|
225
|
-
sheet = @default_sheet unless sheet
|
226
|
-
row,col = normalize(row,col)
|
227
|
-
return true if row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet)
|
228
|
-
return true unless cell(row, col, sheet)
|
229
|
-
return true if celltype(row, col, sheet) == "string" && cell(row, col, sheet) == ""
|
230
|
-
false
|
231
|
-
end
|
232
|
-
|
233
|
-
# first non-empty column as a letter
|
234
|
-
def first_column_as_letter(sheet=nil)
|
235
|
-
sheet = @default_sheet unless sheet
|
236
|
-
Openoffice.number_to_letter(first_column(sheet))
|
237
|
-
end
|
238
233
|
|
239
|
-
#
|
240
|
-
def last_column_as_letter(sheet=nil)
|
241
|
-
sheet = @default_sheet unless sheet
|
242
|
-
Openoffice.number_to_letter(last_column(sheet))
|
243
|
-
end
|
244
|
-
|
234
|
+
# returns NO formula in excel spreadsheets
|
245
235
|
def formula(row,col,sheet=nil)
|
246
236
|
raise EXCEL_NO_FORMULAS
|
247
237
|
end
|
238
|
+
|
239
|
+
# raises an exception because formulas are not supported for excel files
|
248
240
|
def formula?(row,col,sheet=nil)
|
249
241
|
raise EXCEL_NO_FORMULAS
|
250
242
|
end
|
243
|
+
|
244
|
+
# returns NO formulas in excel spreadsheets
|
251
245
|
def formulas(sheet=nil)
|
252
246
|
raise EXCEL_NO_FORMULAS
|
253
247
|
end
|
254
248
|
|
255
|
-
private
|
249
|
+
private
|
256
250
|
|
257
251
|
# check if default_sheet was set
|
258
252
|
def default_sheet_check
|
@@ -264,42 +258,7 @@ private
|
|
264
258
|
sheet = @default_sheet unless sheet
|
265
259
|
fr = fc = 999_999
|
266
260
|
lr = lc = -999_999
|
267
|
-
#TODO: worksheet = @workbook.worksheet(sheet_no(@default_sheet))
|
268
261
|
worksheet = @workbook.worksheet(sheet_no(sheet))
|
269
|
-
if false #=============================================================
|
270
|
-
if @filename == "test/false_encoding.xls"
|
271
|
-
#assert_instance_of(Spreadsheet::ParseExcel::Worksheet, worksheet)
|
272
|
-
p worksheet.class
|
273
|
-
|
274
|
-
p worksheet.methods
|
275
|
-
p '### min_row: '+worksheet.min_row.to_s
|
276
|
-
p '### max_row: '+worksheet.max_row.to_s
|
277
|
-
p '### min_col: '+worksheet.min_col.to_s
|
278
|
-
p '### max_col: '+worksheet.max_col.to_s
|
279
|
-
end
|
280
|
-
if @filename == "test/false_encoding.xls"
|
281
|
-
p worksheet
|
282
|
-
end
|
283
|
-
skip = 0
|
284
|
-
line = 1
|
285
|
-
worksheet.each(skip) { |row_par|
|
286
|
-
if row_par
|
287
|
-
if @filename == "test/false_encoding.xls"
|
288
|
-
p row_par
|
289
|
-
end
|
290
|
-
row_par.each_with_index {|cell,i|
|
291
|
-
# nicht beruecksichtigen, wenn nil und vorher noch nichts war
|
292
|
-
if cell
|
293
|
-
fc = [fc, i+1].min
|
294
|
-
lc = [lc, i+1].max
|
295
|
-
fr = [fr, line].min
|
296
|
-
lr = [lr, line].max
|
297
|
-
end
|
298
|
-
}
|
299
|
-
end
|
300
|
-
line += 1
|
301
|
-
}
|
302
|
-
end #=============================================================
|
303
262
|
fr = worksheet.min_row + 1
|
304
263
|
lr = worksheet.max_row + 1
|
305
264
|
fc = worksheet.min_col + 1
|
@@ -319,19 +278,19 @@ p worksheet.class
|
|
319
278
|
return fr, lr, fc, lc
|
320
279
|
end
|
321
280
|
|
322
|
-
|
323
281
|
# converts name of a sheet to index (0,1,2,..)
|
324
282
|
def sheet_no(name)
|
325
283
|
return name-1 if name.kind_of?(Fixnum)
|
326
284
|
0.upto(@workbook.sheet_count - 1) do |i|
|
327
285
|
# TODO: is there a better way to do conversion?
|
328
286
|
return i if name == Iconv.new('utf-8','unicode').iconv(
|
329
|
-
|
330
|
-
|
287
|
+
@workbook.worksheet(i).name
|
288
|
+
)
|
331
289
|
end
|
332
290
|
raise StandardError, "sheet '#{name}' not found"
|
333
291
|
end
|
334
292
|
|
293
|
+
# writes csv output to stdout or file
|
335
294
|
def write_csv_content(file=nil,sheet=nil)
|
336
295
|
file = STDOUT unless file
|
337
296
|
sheet = @default_sheet unless sheet
|
@@ -345,15 +304,15 @@ p worksheet.class
|
|
345
304
|
empty = true
|
346
305
|
else
|
347
306
|
case cell.type
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
307
|
+
when :numeric
|
308
|
+
onecelltype = :float
|
309
|
+
onecell = cell.to_f
|
310
|
+
when :text
|
311
|
+
onecelltype = :string
|
312
|
+
onecell = cell.to_s('utf-8')
|
313
|
+
when :date
|
314
|
+
onecelltype = :date
|
315
|
+
onecell = cell.date
|
357
316
|
else
|
358
317
|
onecelltype = nil
|
359
318
|
onecell = nil
|
@@ -369,7 +328,7 @@ p worksheet.class
|
|
369
328
|
content = false
|
370
329
|
row.each {|elem|
|
371
330
|
if elem != ''
|
372
|
-
|
331
|
+
#if elem.class == String and elem.size > 0
|
373
332
|
content = true
|
374
333
|
end
|
375
334
|
}
|
@@ -0,0 +1,303 @@
|
|
1
|
+
# Base class for all other types of spreadsheets
|
2
|
+
class GenericSpreadsheet
|
3
|
+
|
4
|
+
attr_reader :default_sheet
|
5
|
+
|
6
|
+
# converts cell coordinate to numeric values of row,col
|
7
|
+
def normalize(row,col)
|
8
|
+
if row.class == String
|
9
|
+
if col.class == Fixnum
|
10
|
+
# ('A',1):
|
11
|
+
# ('B', 5) -> (5, 2)
|
12
|
+
row, col = col, row
|
13
|
+
else
|
14
|
+
raise ArgumentError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
if col.class == String
|
18
|
+
col = GenericSpreadsheet.letter_to_number(col)
|
19
|
+
end
|
20
|
+
return row,col
|
21
|
+
end
|
22
|
+
|
23
|
+
# true if cell is empty
|
24
|
+
def empty?(row, col, sheet=nil)
|
25
|
+
#def excel_empty?(row, col, sheet=nil)
|
26
|
+
sheet = @default_sheet unless sheet
|
27
|
+
read_cells(sheet) unless @cells_read[sheet] or self.class == Excel
|
28
|
+
row,col = normalize(row,col)
|
29
|
+
return true unless cell(row, col, sheet)
|
30
|
+
return true if celltype(row, col, sheet) == :string && cell(row, col, sheet).empty?
|
31
|
+
#false
|
32
|
+
#end
|
33
|
+
|
34
|
+
# true if a cell is empty
|
35
|
+
#def oo_empty?(row, col, sheet=nil)
|
36
|
+
#sheet = @default_sheet unless sheet
|
37
|
+
#read_cells(sheet) unless @cells_read[sheet]
|
38
|
+
#row,col = normalize(row,col)
|
39
|
+
return true if row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet)
|
40
|
+
#return true unless cell(row, col, sheet)
|
41
|
+
# return true if celltype(row, col, sheet) == "string" && cell(row, col, sheet) == ""
|
42
|
+
#return true if celltype(row, col, sheet) == :string && cell(row, col, sheet) == ""
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# reopens and read a spreadsheet document
|
48
|
+
def reload
|
49
|
+
ds = @default_sheet
|
50
|
+
initialize(@filename) if self.class == Openoffice or
|
51
|
+
self.class == Excel
|
52
|
+
initialize(@spreadsheetkey,@user,@password) if self.class == Google
|
53
|
+
self.default_sheet = ds
|
54
|
+
#@first_row = @last_row = @first_column = @last_column = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns information of the spreadsheet document and all sheets within
|
58
|
+
# this document.
|
59
|
+
def info
|
60
|
+
# $log.debug(self.class.to_s+"#info started")
|
61
|
+
result = "File: #{@filename}\n"+
|
62
|
+
"Number of sheets: #{sheets.size}\n"+
|
63
|
+
"Sheets: #{sheets.map{|sheet| sheet+", "}.to_s[0..-3]}\n"
|
64
|
+
n = 1
|
65
|
+
# $log.debug(sheets.inspect)
|
66
|
+
sheets.each {|sheet|
|
67
|
+
# $log.debug("Info fuer Sheet=#{sheet}")
|
68
|
+
self.default_sheet = sheet
|
69
|
+
# $log.debug("nach default_sheet=")
|
70
|
+
result << "Sheet " + n.to_s + ":\n"
|
71
|
+
result << " First row: #{first_row}\n"
|
72
|
+
# $log.debug("nach first_row")
|
73
|
+
result << " Last row: #{last_row}\n"
|
74
|
+
# $log.debug("nach last_row")
|
75
|
+
result << " First column: #{GenericSpreadsheet.number_to_letter(first_column)}\n"
|
76
|
+
# $log.debug("nach first_column")
|
77
|
+
result << " Last column: #{GenericSpreadsheet.number_to_letter(last_column)}"
|
78
|
+
# $log.debug("nach last_column")
|
79
|
+
result << "\n" if sheet != sheets.last
|
80
|
+
n += 1
|
81
|
+
}
|
82
|
+
# $log.debug(self.class.to_s+"#info ended")
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
# returns a rectangular area (default: all cells) as yaml-output
|
87
|
+
# you can add additional attributes with the prefix parameter like:
|
88
|
+
# oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
|
89
|
+
def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
|
90
|
+
sheet = @default_sheet unless sheet
|
91
|
+
result = "--- \n"
|
92
|
+
(from_row||first_row(sheet)).upto(to_row||last_row(sheet)) do |row|
|
93
|
+
(from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
|
94
|
+
unless empty?(row,col,sheet)
|
95
|
+
result << "cell_#{row}_#{col}: \n"
|
96
|
+
prefix.each {|k,v|
|
97
|
+
result << " #{k}: #{v} \n"
|
98
|
+
}
|
99
|
+
result << " row: #{row} \n"
|
100
|
+
result << " col: #{col} \n"
|
101
|
+
result << " celltype: #{self.celltype(row,col,sheet)} \n"
|
102
|
+
result << " value: #{self.cell(row,col,sheet)} \n"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
# recursively removes the current temporary directory
|
110
|
+
# this is only needed if you work with zipped files or files via the web
|
111
|
+
def remove_tmp
|
112
|
+
if File.exists?(@tmpdir)
|
113
|
+
FileUtils::rm_r(@tmpdir)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# first non-empty column as a letter
|
118
|
+
def first_column_as_letter(sheet=nil)
|
119
|
+
GenericSpreadsheet.number_to_letter(first_column(sheet))
|
120
|
+
end
|
121
|
+
|
122
|
+
# last non-empty column as a letter
|
123
|
+
def last_column_as_letter(sheet=nil)
|
124
|
+
GenericSpreadsheet.number_to_letter(last_column(sheet))
|
125
|
+
end
|
126
|
+
|
127
|
+
def open_from_uri(uri)
|
128
|
+
require 'open-uri' ;
|
129
|
+
tempfilename = File.join(@tmpdir, File.basename(uri))
|
130
|
+
f = File.open(tempfilename,"wb")
|
131
|
+
begin
|
132
|
+
open(uri) do |net|
|
133
|
+
f.write(net.read)
|
134
|
+
end
|
135
|
+
rescue
|
136
|
+
raise "could not open #{uri}"
|
137
|
+
end
|
138
|
+
f.close
|
139
|
+
File.join(@tmpdir, File.basename(uri))
|
140
|
+
end
|
141
|
+
|
142
|
+
# returns the number of the last non-empty row
|
143
|
+
def last_row(sheet=nil)
|
144
|
+
sheet = @default_sheet unless sheet
|
145
|
+
read_cells(sheet) unless @cells_read[sheet]
|
146
|
+
if @last_row[sheet]
|
147
|
+
return @last_row[sheet]
|
148
|
+
end
|
149
|
+
impossible_value = 0
|
150
|
+
result = impossible_value
|
151
|
+
@cell[sheet].each_pair {|key,value|
|
152
|
+
y,x = key.split(',')
|
153
|
+
y = y.to_i
|
154
|
+
result = [result, y].max if value
|
155
|
+
}
|
156
|
+
result = nil if result == impossible_value
|
157
|
+
@last_row[sheet] = result
|
158
|
+
result
|
159
|
+
end
|
160
|
+
|
161
|
+
# returns the number of the last non-empty column
|
162
|
+
def last_column(sheet=nil)
|
163
|
+
# $log.debug("#{self.class.to_s}#last_column(#{sheet})")
|
164
|
+
sheet = @default_sheet unless sheet
|
165
|
+
read_cells(sheet) unless @cells_read[sheet]
|
166
|
+
if @last_column[sheet]
|
167
|
+
# $log.debug("last_column of sheet #{sheet} already set")
|
168
|
+
return @last_column[sheet]
|
169
|
+
end
|
170
|
+
# $log.debug("last_column of sheet #{sheet} not yet set")
|
171
|
+
impossible_value = 0
|
172
|
+
result = impossible_value
|
173
|
+
@cell[sheet].each_pair {|key,value|
|
174
|
+
y,x = key.split(',')
|
175
|
+
x = x.to_i
|
176
|
+
result = [result, x].max if value
|
177
|
+
}
|
178
|
+
result = nil if result == impossible_value
|
179
|
+
@last_column[sheet] = result
|
180
|
+
result
|
181
|
+
end
|
182
|
+
|
183
|
+
# returns the number of the first non-empty row
|
184
|
+
def first_row(sheet=nil)
|
185
|
+
if sheet == nil
|
186
|
+
sheet = @default_sheet
|
187
|
+
end
|
188
|
+
read_cells(sheet) unless @cells_read[sheet]
|
189
|
+
if @first_row[sheet]
|
190
|
+
return @first_row[sheet]
|
191
|
+
end
|
192
|
+
impossible_value = 999_999 # more than a spreadsheet can hold
|
193
|
+
result = impossible_value
|
194
|
+
@cell[sheet].each_pair {|key,value|
|
195
|
+
y,x = key.split(',')
|
196
|
+
y = y.to_i
|
197
|
+
result = [result, y].min if value
|
198
|
+
}
|
199
|
+
result = nil if result == impossible_value
|
200
|
+
@first_row[sheet] = result
|
201
|
+
result
|
202
|
+
end
|
203
|
+
|
204
|
+
# returns the number of the first non-empty column
|
205
|
+
def first_column(sheet=nil)
|
206
|
+
if sheet == nil
|
207
|
+
sheet = @default_sheet
|
208
|
+
end
|
209
|
+
read_cells(sheet) unless @cells_read[sheet]
|
210
|
+
if @first_column[sheet]
|
211
|
+
return @first_column[sheet]
|
212
|
+
end
|
213
|
+
impossible_value = 999_999 # more than a spreadsheet can hold
|
214
|
+
result = impossible_value
|
215
|
+
@cell[sheet].each_pair {|key,value|
|
216
|
+
y,x = key.split(',')
|
217
|
+
x = x.to_i
|
218
|
+
result = [result, x].min if value
|
219
|
+
}
|
220
|
+
result = nil if result == impossible_value
|
221
|
+
@first_column[sheet] = result
|
222
|
+
result
|
223
|
+
end
|
224
|
+
|
225
|
+
# convert a number to something like this: 'AB'
|
226
|
+
def GenericSpreadsheet.number_to_letter(n)
|
227
|
+
letters=""
|
228
|
+
while n > 0
|
229
|
+
num = n%26
|
230
|
+
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num-1,1] + letters
|
231
|
+
n = n.div(26)
|
232
|
+
end
|
233
|
+
letters
|
234
|
+
end
|
235
|
+
|
236
|
+
# convert letters like 'AB' to a number
|
237
|
+
def GenericSpreadsheet.letter_to_number(letters)
|
238
|
+
result = 0
|
239
|
+
while letters && letters.length > 0
|
240
|
+
character = letters[0,1].upcase
|
241
|
+
num = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(character)
|
242
|
+
raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
|
243
|
+
num += 1
|
244
|
+
result = result * 26 + num
|
245
|
+
letters = letters[1..-1]
|
246
|
+
end
|
247
|
+
result
|
248
|
+
end
|
249
|
+
|
250
|
+
protected
|
251
|
+
|
252
|
+
def unzip(filename)
|
253
|
+
ret = nil
|
254
|
+
Zip::ZipFile.open(filename) do |zip|
|
255
|
+
ret = process_zipfile_packed zip
|
256
|
+
end
|
257
|
+
ret
|
258
|
+
end
|
259
|
+
|
260
|
+
# helper method
|
261
|
+
def after(d)
|
262
|
+
if DateTime.now > d
|
263
|
+
yield
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# helper method
|
268
|
+
def before(d)
|
269
|
+
if DateTime.now <= d
|
270
|
+
yield
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
private
|
275
|
+
|
276
|
+
def initialize
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
def process_zipfile_packed(zip, path='')
|
281
|
+
ret=nil
|
282
|
+
if zip.file.file? path
|
283
|
+
# extract and return filename
|
284
|
+
@tmpdir = "oo_"+$$.to_s
|
285
|
+
unless File.exists?(@tmpdir)
|
286
|
+
FileUtils::mkdir(@tmpdir)
|
287
|
+
end
|
288
|
+
file = File.open(File.join(@tmpdir, path),"wb")
|
289
|
+
file.write(zip.read(path))
|
290
|
+
file.close
|
291
|
+
return File.join(@tmpdir, path)
|
292
|
+
else
|
293
|
+
unless path.empty?
|
294
|
+
path += '/'
|
295
|
+
end
|
296
|
+
zip.dir.foreach(path) do |filename|
|
297
|
+
ret = process_zipfile_packed(zip, path + filename)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
ret
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|