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.
@@ -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'
@@ -6,8 +6,10 @@ Rakefile
6
6
  base64include.rb
7
7
  examples/roo_soap_server.rb
8
8
  examples/roo_soap_client.rb
9
+ examples/write_me.rb
9
10
  lib/roo.rb
10
11
  lib/roo/version.rb
12
+ lib/roo/generic_spreadsheet.rb
11
13
  lib/roo/openoffice.rb
12
14
  lib/roo/excel.rb
13
15
  lib/roo/google.rb
data/README.txt CHANGED
@@ -3,4 +3,3 @@ README for roo
3
3
 
4
4
  see http://roo.rubyforge.org for examples
5
5
 
6
- PLEASE DON'T USE THE Google class - its only experimental.
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-Spreadsheets and Excel-Spreadsheets"
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
@@ -3,6 +3,7 @@ end
3
3
 
4
4
  require 'roo/version'
5
5
  # require 'roo/spreadsheetparser' TODO:
6
+ require 'roo/generic_spreadsheet'
6
7
  require 'roo/openoffice'
7
8
  require 'roo/excel'
8
9
  require 'roo/google'
@@ -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
- include Enumerable
9
- attr_reader :min_row, :max_row, :min_col, :max_col
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
- # Class Excel is derived from class Openoffice. It implements almost all methods
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[-4..-1] != ".xls"
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
- FileUtils::rm_r(@tmpdir)
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
- result << Iconv.new('utf-8','unicode').iconv(
58
- @workbook.worksheet(i).name
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
- @default_sheet = n #-1
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
- when :numeric then return cell.to_f
95
- when :text then return cell.to_s('utf-8')
96
- when :date then return cell.date
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
- when :numeric then result << cell.to_i
146
- when :text then result << cell.to_s('utf-8')
147
- when :date then result << cell.date
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
- #if defined? cell = row_par.at(columnnumber-1)
183
+ #if defined? cell = row_par.at(columnnumber-1)
173
184
  if cell
174
185
  case cell.type
175
- when :numeric then result << cell.to_i
176
- when :text then result << cell.to_s('utf-8')
177
- when :date then result << cell.date
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
- # last non-empty column as a letter
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
- @workbook.worksheet(i).name
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
- when :numeric
349
- onecelltype = :float
350
- onecell = cell.to_f
351
- when :text
352
- onecelltype = :string
353
- onecell = cell.to_s('utf-8')
354
- when :date
355
- onecelltype = :date
356
- onecell = cell.date
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
- #if elem.class == String and elem.size > 0
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