robust_excel_ole 0.3.7 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,8 @@
1
1
 
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
+ include Utilities
5
+
4
6
  module RobustExcelOle
5
7
 
6
8
  class Bookstore
@@ -11,6 +13,10 @@ module RobustExcelOle
11
13
  end
12
14
 
13
15
  # returns a book with the given filename, if it was open once
16
+ # @param [String] filename the file name
17
+ # @param [Hash] options the options
18
+ # @option option [Boolean] :prefer_writable
19
+ # @option option [Boolean] :prefer_excel
14
20
  # prefers open books to closed books, and among them, prefers more recently opened books
15
21
  # excludes hidden Excel instance
16
22
  # options: :prefer_writable returns the writable book, if it is open (default: true)
@@ -29,7 +35,7 @@ module RobustExcelOle
29
35
  begin
30
36
  @filename2books[filename_key].delete(wr_book)
31
37
  rescue
32
- t "Warning: deleting dead reference failed: file: #{filename.inspect}"
38
+ trace "Warning: deleting dead reference failed: file: #{filename.inspect}"
33
39
  end
34
40
  else
35
41
  book = wr_book.__getobj__
@@ -51,6 +57,7 @@ module RobustExcelOle
51
57
  end
52
58
 
53
59
  # stores a workbook
60
+ # @param [Book] book a given book
54
61
  def store(book)
55
62
  filename_key = RobustExcelOle::canonize(book.filename)
56
63
  if book.stored_filename
@@ -63,7 +70,7 @@ module RobustExcelOle
63
70
  end
64
71
 
65
72
  # creates and returns a separate Excel instance with Visible and DisplayAlerts equal false
66
- def hidden_excel
73
+ def hidden_excel # :nodoc: #
67
74
  unless (@hidden_excel_instance && @hidden_excel_instance.weakref_alive? && @hidden_excel_instance.__getobj__.alive?)
68
75
  @hidden_excel_instance = WeakRef.new(Excel.create)
69
76
  end
@@ -87,25 +94,25 @@ module RobustExcelOle
87
94
 
88
95
  private
89
96
 
90
- def try_hidden_excel
97
+ def try_hidden_excel # :nodoc: #
91
98
  @hidden_excel_instance.__getobj__ if (@hidden_excel_instance && @hidden_excel_instance.weakref_alive? && @hidden_excel_instance.__getobj__.alive?)
92
99
  end
93
100
 
94
101
  # prints the book store
95
- def print
96
- t "@filename2books:"
102
+ def print # :nodoc: #
103
+ trace "@filename2books:"
97
104
  if @filename2books
98
105
  @filename2books.each do |filename,books|
99
- t " filename: #{filename}"
100
- t " books:"
106
+ trace " filename: #{filename}"
107
+ trace " books:"
101
108
  if books.empty?
102
- t " []"
109
+ trace " []"
103
110
  else
104
111
  books.each do |book|
105
112
  if book.weakref_alive?
106
- t "#{book}"
113
+ trace "#{book}"
107
114
  else
108
- t "weakref not alive"
115
+ trace "weakref not alive"
109
116
  end
110
117
  end
111
118
  end
@@ -11,7 +11,7 @@ module RobustExcelOle
11
11
  @conv_to_win32_path =
12
12
  Win32API.new('cygwin1.dll', 'cygwin_conv_to_win32_path', 'PP', 'I')
13
13
 
14
- def cygpath(options, path)
14
+ def cygpath(options, path) # :nodoc: #
15
15
  absolute = shortname = false
16
16
  func = nil
17
17
  options.delete(" \t-").chars {|opt|
@@ -2,30 +2,40 @@
2
2
 
3
3
  require 'timeout'
4
4
 
5
- module RobustExcelOle
5
+ include Utilities
6
+
7
+ module RobustExcelOle
6
8
 
7
9
  class Excel
8
10
 
9
11
  @@hwnd2excel = {}
10
12
 
11
13
  # creates a new Excel instance
14
+ # @return [Excel] a new Excel instance
12
15
  def self.create
13
16
  new(:reuse => false)
14
17
  end
15
18
 
16
19
  # uses the current Excel instance (connects), if such a running Excel instance exists
17
20
  # creates a new one, otherwise
21
+ # @return [Excel] an Excel instance
18
22
  def self.current
19
23
  new(:reuse => true)
20
24
  end
21
25
 
22
26
  # returns an Excel instance
23
- # given: a WIN32OLE object representing an Excel instance, or a Hash representing options:
27
+ # given a WIN32OLE object representing an Excel instance, or a Hash representing options:
28
+ # @param [Hash] options the options
29
+ # @option options [Boolean] :reuse
30
+ # @option options [Boolean] :displayalerts
31
+ # @option options [Boolean] :visible
32
+ # options:
24
33
  # :reuse connects to an already running Excel instance (true) or
25
34
  # creates a new Excel instance (false) (default: true)
26
35
  # :displayalerts allows display alerts in Excel (default: false)
27
36
  # :visible makes the Excel visible (default: false)
28
37
  # if :reuse => true, then DisplayAlerts and Visible are set only if they are given
38
+ # @return [Excel] an Excel instance
29
39
  def self.new(options = {})
30
40
  if options.is_a? WIN32OLE
31
41
  excel = options
@@ -66,8 +76,13 @@ module RobustExcelOle
66
76
  end
67
77
 
68
78
  # reopens a closed Excel instance
79
+ # @param [Hash] opts the options
80
+ # @option opts [Boolean] :reopen_workbooks
81
+ # @option opts [Boolean] :displayalerts
82
+ # @option opts [Boolean] :visible
69
83
  # options: reopen_workbooks (default: false): reopen the workbooks in the Excel instances
70
84
  # :visible (default: false), :displayalerts (default: false)
85
+ # @return [Excel] an Excel instance
71
86
  def recreate(opts = {})
72
87
  unless self.alive?
73
88
  opts = {
@@ -99,7 +114,7 @@ module RobustExcelOle
99
114
  begin
100
115
  result.Visible # send any method, just to see if it responds
101
116
  rescue
102
- t "dead excel " + ("Window-handle = #{result.HWnd}" rescue "without window handle")
117
+ trace "dead excel " + ("Window-handle = #{result.HWnd}" rescue "without window handle")
103
118
  return nil
104
119
  end
105
120
  end
@@ -110,14 +125,21 @@ module RobustExcelOle
110
125
  public
111
126
 
112
127
  # closes all Excel instances
128
+ # @param [Hash] options the options
129
+ # @option options [Symbol] :if_unsaved :raise, :save, :forget, :alert, or :keep_open
130
+ # @option options [Boolean] :hard
131
+ # @option options [Boolean] :kill_if_timeout
113
132
  # options:
114
133
  # :if_unsaved if unsaved workbooks are open in an Excel instance
115
134
  # :raise (default) -> raises an exception
116
135
  # :save -> saves the workbooks before closing
117
136
  # :forget -> closes the excel instance without saving the workbooks
137
+ # :keep_open -> let the workbooks open
118
138
  # :alert -> give control to Excel
119
139
  # :hard closes Excel instances soft (default: false), or, additionally kills the Excel processes hard (true)
120
140
  # :kill_if_timeout: kills Excel instances hard if the closing process exceeds a certain time limit (default: true)
141
+ # @raise ExcelError if time limit has exceeded, some Excel instance cannot be closed, or
142
+ # unsaved workbooks exist and option :if_unsaved is :raise
121
143
  def self.close_all(options={})
122
144
  options = {
123
145
  :if_unsaved => :raise,
@@ -153,12 +175,12 @@ module RobustExcelOle
153
175
 
154
176
  private
155
177
 
156
- def self.manage_unsaved_workbooks(excel, options)
178
+ def self.manage_unsaved_workbooks(excel, options) # :nodoc: #
157
179
  unsaved_workbooks = []
158
180
  begin
159
181
  excel.Workbooks.each {|w| unsaved_workbooks << w unless (w.Saved || w.ReadOnly)}
160
182
  rescue RuntimeError => msg
161
- t "RuntimeError: #{msg.message}"
183
+ trace "RuntimeError: #{msg.message}"
162
184
  raise ExcelErrorOpen, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
163
185
  end
164
186
  unless unsaved_workbooks.empty?
@@ -199,7 +221,7 @@ module RobustExcelOle
199
221
  end
200
222
  end
201
223
 
202
- def self.close_excel_ole_instance(ole_excel)
224
+ def self.close_excel_ole_instance(ole_excel) # :nodoc: #
203
225
  @@hwnd2excel.delete(ole_excel.Hwnd)
204
226
  excel = ole_excel
205
227
  ole_excel = nil
@@ -214,21 +236,21 @@ module RobustExcelOle
214
236
  if weak_excel_ref.weakref_alive? then
215
237
  begin
216
238
  weak_excel_ref.ole_free
217
- t "successfully ole_freed #{weak_excel_ref}"
239
+ trace "successfully ole_freed #{weak_excel_ref}"
218
240
  rescue
219
- t "could not do ole_free on #{weak_excel_ref}"
241
+ trace "could not do ole_free on #{weak_excel_ref}"
220
242
  end
221
243
  end
222
244
  @@hwnd2excel.delete(excel_hwnd)
223
245
  rescue => e
224
- t "Error when closing Excel: #{e.message}"
246
+ trace "Error when closing Excel: #{e.message}"
225
247
  #t e.backtrace
226
248
  end
227
249
  free_all_ole_objects
228
250
  end
229
251
 
230
252
  # frees all OLE objects in the object space
231
- def self.free_all_ole_objects
253
+ def self.free_all_ole_objects # :nodoc: #
232
254
  anz_objekte = 0
233
255
  ObjectSpace.each_object(WIN32OLE) do |o|
234
256
  anz_objekte += 1
@@ -238,18 +260,21 @@ module RobustExcelOle
238
260
  #t o.ole_type rescue nil
239
261
  begin
240
262
  o.ole_free
241
- #t "olefree OK"
263
+ #trace "olefree OK"
242
264
  rescue
243
- #t "olefree_error: #{$!}"
244
- #t $!.backtrace.first(9).join "\n"
265
+ #trace "olefree_error: #{$!}"
266
+ #trace $!.backtrace.first(9).join "\n"
245
267
  end
246
268
  end
247
- t "went through #{anz_objekte} OLE objects"
269
+ trace "went through #{anz_objekte} OLE objects"
248
270
  end
249
271
 
250
272
  public
251
273
 
252
274
  # closes the Excel
275
+ # @param [Hash] options the options
276
+ # @option options [Symbol] :if_unsaved :raise, :save, :forget, or :keep_open
277
+ # @option options [Boolean] :hard
253
278
  # :if_unsaved if unsaved workbooks are open in an Excel instance
254
279
  # :raise (default) -> raises an exception
255
280
  # :save -> saves the workbooks before closing
@@ -285,10 +310,10 @@ module RobustExcelOle
285
310
  if weak_excel_ref.weakref_alive? then
286
311
  begin
287
312
  weak_excel_ref.ole_free
288
- t "successfully ole_freed #{weak_excel_ref}"
313
+ trace "successfully ole_freed #{weak_excel_ref}"
289
314
  rescue => msg
290
- t "#{msg.message}"
291
- t "could not do ole_free on #{weak_excel_ref}"
315
+ trace "#{msg.message}"
316
+ trace "could not do ole_free on #{weak_excel_ref}"
292
317
  end
293
318
  end
294
319
  @@hwnd2excel.delete(excel_hwnd)
@@ -305,6 +330,7 @@ module RobustExcelOle
305
330
  public
306
331
 
307
332
  # kill all Excel instances
333
+ # @return [Fixnum] number of killed Excel processes
308
334
  def self.kill_all
309
335
  procs = WIN32OLE.connect("winmgmts:\\\\.")
310
336
  processes = procs.InstancesOf("win32_process")
@@ -345,31 +371,31 @@ module RobustExcelOle
345
371
  result
346
372
  end
347
373
 
348
- def excel
374
+ def excel # :nodoc: #
349
375
  self
350
376
  end
351
377
 
352
- def self.hwnd2excel(hwnd)
378
+ def self.hwnd2excel(hwnd) # :nodoc: #
353
379
  excel_weakref = @@hwnd2excel[hwnd]
354
380
  if excel_weakref
355
381
  if excel_weakref.weakref_alive?
356
382
  excel_weakref.__getobj__
357
383
  else
358
- t "dead reference to an Excel"
384
+ trace "dead reference to an Excel"
359
385
  begin
360
386
  @@hwnd2excel.delete(hwnd)
361
387
  rescue
362
- t "Warning: deleting dead reference failed! (hwnd: #{hwnd.inspect})"
388
+ trace "Warning: deleting dead reference failed! (hwnd: #{hwnd.inspect})"
363
389
  end
364
390
  end
365
391
  end
366
392
  end
367
393
 
368
- def hwnd
394
+ def hwnd # :nodoc: #
369
395
  self.Hwnd rescue nil
370
396
  end
371
397
 
372
- def self.print_hwnd2excel
398
+ def self.print_hwnd2excel # :nodoc: #
373
399
  @@hwnd2excel.each do |hwnd,wr_excel|
374
400
  excel_string = (wr_excel.weakref_alive? ? wr_excel.__getobj__.to_s : "weakref not alive")
375
401
  printf("hwnd: %8i => excel: %s\n", hwnd, excel_string)
@@ -393,7 +419,7 @@ module RobustExcelOle
393
419
 
394
420
 
395
421
  # returns all unsaved workbooks in Excel instances
396
- def self.unsaved_workbooks_all
422
+ def self.unsaved_workbooks_all # :nodoc: #
397
423
  result = []
398
424
  @@hwnd2excel.each do |hwnd,wr_excel|
399
425
  excel = wr_excel.__getobj__
@@ -408,7 +434,7 @@ module RobustExcelOle
408
434
  begin
409
435
  self.Workbooks.each {|w| result << w unless (w.Saved || w.ReadOnly)}
410
436
  rescue RuntimeError => msg
411
- t "RuntimeError: #{msg.message}"
437
+ trace "RuntimeError: #{msg.message}"
412
438
  raise ExcelErrorOpen, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
413
439
  end
414
440
  result
@@ -417,11 +443,11 @@ module RobustExcelOle
417
443
  #self.class.map {|w| (not w.Saved)}
418
444
 
419
445
  def print_workbooks
420
- self.Workbooks.each {|w| t "#{w.Name} #{w}"}
446
+ self.Workbooks.each {|w| puts "#{w.Name} #{w}"}
421
447
  end
422
448
 
423
449
 
424
- # empty workbook is generated, saved and closed
450
+ # generates, saves, and closes empty workbook
425
451
  def generate_workbook file_name
426
452
  self.Workbooks.Add
427
453
  empty_workbook = self.Workbooks.Item(self.Workbooks.Count)
@@ -457,7 +483,7 @@ module RobustExcelOle
457
483
  @displayalerts = @ole_excel.DisplayAlerts = displayalerts_value
458
484
  end
459
485
 
460
- # return if in the current Excel instance DisplayAlerts is enabled
486
+ # return whether DisplayAlerts is enabled in the current Excel instance
461
487
  def displayalerts
462
488
  @displayalerts = @ole_excel.DisplayAlerts
463
489
  end
@@ -472,15 +498,15 @@ module RobustExcelOle
472
498
  @visible = @ole_excel.Visible
473
499
  end
474
500
 
475
- def to_s
501
+ def to_s # :nodoc: #
476
502
  "#<Excel: " + "#{hwnd}" + ("#{"not alive" unless self.alive?}") + ">"
477
503
  end
478
504
 
479
- def inspect
505
+ def inspect # :nodoc: #
480
506
  self.to_s
481
507
  end
482
508
 
483
- def self.book_class
509
+ def self.book_class # :nodoc: #
484
510
  @book_class ||= begin
485
511
  module_name = self.parent_name
486
512
  "#{module_name}::Book".constantize
@@ -489,13 +515,27 @@ module RobustExcelOle
489
515
  end
490
516
  end
491
517
 
492
- def book_class
518
+ def book_class # :nodoc: #
493
519
  self.class.book_class
494
520
  end
495
521
 
522
+ def respond_to?(name, include_private = false) # :nodoc: #
523
+ raise ExcelError, "respond_to?: Excel not alive" unless alive?
524
+ super
525
+ end
526
+
527
+ def methods # :nodoc: #
528
+ (super + @ole_excel.ole_methods.map{|m| m.to_s}).uniq
529
+ end
530
+
531
+ def special_methods # :nodoc: #
532
+ (methods - Object.methods).sort
533
+ end
534
+
535
+
496
536
  private
497
537
 
498
- def method_missing(name, *args)
538
+ def method_missing(name, *args) # :nodoc: #
499
539
  if name.to_s[0,1] =~ /[A-Z]/
500
540
  begin
501
541
  raise ExcelError, "method missing: Excel not alive" unless alive?
@@ -1,5 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
+
3
+ include Utilities
4
+
2
5
  module RobustExcelOle
6
+
3
7
  class Sheet
4
8
  attr_reader :worksheet
5
9
 
@@ -16,10 +20,13 @@ module RobustExcelOle
16
20
  end
17
21
  end
18
22
 
23
+ # returns name of the workbook
19
24
  def name
20
25
  @worksheet.Name
21
26
  end
22
27
 
28
+ # name the sheet
29
+ # @param [String] new_name the new name of the sheet
23
30
  def name= (new_name)
24
31
  begin
25
32
  @worksheet.Name = new_name
@@ -27,7 +34,7 @@ module RobustExcelOle
27
34
  if msg.message =~ /800A03EC/
28
35
  raise ExcelErrorSheet, "sheet name #{new_name.inspect} already exists"
29
36
  else
30
- t "#{msg.message}"
37
+ trace "#{msg.message}"
31
38
  raise ExcelErrorSheetUnknown
32
39
  end
33
40
  end
@@ -104,8 +111,12 @@ module RobustExcelOle
104
111
  end
105
112
 
106
113
  # returns the contents of a range with given name
114
+ # @param [String] name the range name
115
+ # @param [Hash] opts the options
116
+ # @option opts [Variant] :default default value (default: nil)
107
117
  # if no contents could returned, then return default value, if a default value was provided
108
118
  # raise an error, otherwise
119
+ # @raise SheetError if value of the range cannot be evaluated
109
120
  def nvalue(name, opts = {:default => nil})
110
121
  begin
111
122
  value = self.Evaluate(name)
@@ -116,12 +127,16 @@ module RobustExcelOle
116
127
  end
117
128
  if value == -2146826259
118
129
  return opts[:default] if opts[:default]
119
- raise SheetError, "cannot evaluate name #{name.inspect} in sheet"
130
+ raise SheeetError, "cannot evaluate name #{name.inspect} in sheet"
120
131
  end
121
132
  return opts[:default] if (value.nil? && opts[:default])
122
133
  value
123
134
  end
124
135
 
136
+ # assigns a value to a range with given name
137
+ # @param [String] name the range name
138
+ # @param [Variant] value the assigned value
139
+ # @raise SheetError if name is not in the sheet or the value cannot be assigned
125
140
  def set_nvalue(name,value)
126
141
  begin
127
142
  item = self.Names.Item(name)
@@ -136,6 +151,9 @@ module RobustExcelOle
136
151
  end
137
152
 
138
153
  # assigns a name to a range (a cell) given by an address
154
+ # @param [String] name the range name
155
+ # @param [Fixnum] row the row
156
+ # @param [Fixnum] column the column
139
157
  def set_name(name,row,column)
140
158
  begin
141
159
  old_name = self[row,column].Name.Name rescue nil
@@ -146,28 +164,36 @@ module RobustExcelOle
146
164
  self.Names.Add("Name" => name, "RefersToR1C1" => "=" + address)
147
165
  end
148
166
  rescue WIN32OLERuntimeError => msg
149
- t "WIN32OLERuntimeError: #{msg.message}"
167
+ trace "WIN32OLERuntimeError: #{msg.message}"
150
168
  raise SheetError, "cannot add name #{name.inspect} to cell with row #{row.inspect} and column #{column.inspect}"
151
169
  end
152
170
  end
153
171
 
172
+ def respond_to?(name, include_private = false) # :nodoc: #
173
+ super
174
+ end
175
+
176
+ def methods # :nodoc: #
177
+ super
178
+ end
179
+
154
180
  private
155
181
 
156
- def method_missing(name, *args)
157
- if name.to_s[0,1] =~ /[A-Z]/
158
- begin
159
- @worksheet.send(name, *args)
160
- rescue WIN32OLERuntimeError => msg
161
- if msg.message =~ /unknown property or method/
162
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
163
- else
164
- raise msg
182
+ def method_missing(name, *args) # :nodoc: #
183
+ if name.to_s[0,1] =~ /[A-Z]/
184
+ begin
185
+ @worksheet.send(name, *args)
186
+ rescue WIN32OLERuntimeError => msg
187
+ if msg.message =~ /unknown property or method/
188
+ raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
189
+ else
190
+ raise msg
191
+ end
165
192
  end
193
+ else
194
+ super
166
195
  end
167
- else
168
- super
169
196
  end
170
- end
171
197
 
172
198
 
173
199
  def last_row