robust_excel_ole 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/README.rdoc +266 -71
  2. data/TodoList.md +33 -0
  3. data/examples/edit_sheets/example_adding_sheets.rb +7 -0
  4. data/examples/open_save_close/example_control_to_excel.rb +4 -4
  5. data/examples/open_save_close/example_default_excel.rb +49 -0
  6. data/examples/open_save_close/example_force_excel.rb +34 -0
  7. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
  8. data/examples/open_save_close/example_if_obstructed_forget.rb +3 -3
  9. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  10. data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
  11. data/examples/open_save_close/example_if_unsaved_forget_more.rb +3 -3
  12. data/examples/open_save_close/example_read_only.rb +1 -1
  13. data/examples/open_save_close/example_rename_cells.rb +69 -0
  14. data/examples/open_save_close/example_reuse.rb +8 -8
  15. data/examples/open_save_close/example_simple.rb +3 -3
  16. data/examples/open_save_close/example_unobtrusively.rb +28 -0
  17. data/examples/save_sheets/example_save_sheets.rb +2 -6
  18. data/lib/robust_excel_ole.rb +18 -0
  19. data/lib/robust_excel_ole/book.rb +356 -123
  20. data/lib/robust_excel_ole/book_store.rb +75 -0
  21. data/lib/robust_excel_ole/excel.rb +105 -53
  22. data/lib/robust_excel_ole/robustexcelole.sublime-project +8 -8
  23. data/lib/robust_excel_ole/sheet.rb +29 -1
  24. data/lib/robust_excel_ole/version.rb +1 -1
  25. data/robust_excel_ole.gemspec +3 -3
  26. data/spec/book_spec.rb +1031 -247
  27. data/spec/book_store_spec.rb +306 -0
  28. data/spec/data/book_with_blank.xls +0 -0
  29. data/spec/data/merge_cells.xls +0 -0
  30. data/spec/data/more_simple.xls +0 -0
  31. data/spec/data/simple.xls +0 -0
  32. data/spec/excel_spec.rb +145 -90
  33. data/spec/sheet_spec.rb +31 -7
  34. metadata +15 -7
@@ -1,3 +1,4 @@
1
+
1
2
  # -*- coding: utf-8 -*-
2
3
 
3
4
  require 'weakref'
@@ -5,129 +6,221 @@ require 'weakref'
5
6
  module RobustExcelOle
6
7
 
7
8
  class Book
8
- attr_reader :workbook
9
-
9
+ attr_accessor :excel
10
+ attr_accessor :workbook
11
+ attr_accessor :stored_filename
10
12
 
11
13
  class << self
12
14
 
13
15
  # opens a book.
14
16
  #
15
17
  # options:
16
- # :read_only open in read-only mode (default: false)
17
- # :if_unsaved if an unsaved book with the same name is open, then
18
- # :raise -> raise an exception (default)
19
- # :forget -> close the unsaved book, open the new book
20
- # :accept -> let the unsaved book open
21
- # :alert -> give control to excel
22
- # :new_app -> open the new book in a new excel application
23
- # :if_obstructed if a book with the same name in a different path is open, then
24
- # :raise -> raise an exception (default)
25
- # :forget -> close the old book, open the new book
26
- # :save -> save the old book, close it, open the new book
27
- # :close_if_saved -> close the old book and open the new book, if the old book is saved
28
- # raise an exception otherwise
29
- # :new_app -> open the new book in a new excel application
30
- # :reuse use a running Excel-application (default: true)
31
- # :excel an Excel application (default: nil)
32
- # :displayalerts allow display alerts in Excel (default: false)
33
- # :visible make visibe in Excel (default: false)
34
- def open(file, options={ :reuse => true}, &block)
35
- new(file, options, &block)
36
- end
18
+ # :default_excel if the book was already open in an Excel instance, then open it there, otherwise:
19
+ # :reuse (default) -> connect to a running Excel instance if it exists, open in a new Excel otherwise
20
+ # :new -> open in a new Excel instance
21
+ # <instance> -> open in the given Excel instance
22
+ # :force_excel no matter whether the book was already open
23
+ # :new (default) -> open in a new Excel
24
+ # <instance> -> open in the given Excel instance
25
+ # :if_locked if the book is open in another Excel instance, then
26
+ # :readonly (default) -> open the book as readonly, if is should be opened in a new Excel,
27
+ # use the old book otherwise
28
+ # :take_writable -> use the Excel instance in which the book is writable,
29
+ # if such an Excel instance exists
30
+ # :force_writability -> make it writable in the desired Excel
31
+ # :if_locked_unsaved if the book is open in another Excel instance and contains unsaved changes
32
+ # :raise -> raise an exception
33
+ # :save -> save the unsaved book
34
+ # (not implemented yet)
35
+ # :if_unsaved if an unsaved book with the same name is open, then
36
+ # :raise (default) -> raise an exception
37
+ # :forget -> close the unsaved book, open the new book
38
+ # :accept -> let the unsaved book open
39
+ # :alert -> give control to Excel
40
+ # :new_excel -> open the new book in a new Excel
41
+ # :if_obstructed if a book with the same name in a different path is open, then
42
+ # :raise (default) -> raise an exception
43
+ # :forget -> close the old book, open the new book
44
+ # :save -> save the old book, close it, open the new book
45
+ # :close_if_saved -> close the old book and open the new book, if the old book is saved
46
+ # raise an exception otherwise
47
+ # :new_excel -> open the new book in a new Excel
48
+ # :reuse_excel -> try the next free running Excel, if it exists, open a new Excel, else
49
+ # :read_only open in read-only mode (default: false)
50
+ # :displayalerts enable DisplayAlerts in Excel (default: false)
51
+ # :visible make visibe in Excel (default: false)
52
+ # If :default_excel is set, then DisplayAlerts and Visible are set only if these parameters are given,
53
+ # not set by default
37
54
 
55
+
56
+ def open(file, opts={ }, &block)
57
+ @options = {
58
+ :excel => :reuse,
59
+ :default_excel => :reuse,
60
+ :if_locked => :readonly,
61
+ :if_unsaved => :raise,
62
+ :if_obstructed => :raise,
63
+ :read_only => false
64
+ }.merge(opts)
65
+ #self.set_defaults(opts) ???
66
+ book = nil
67
+ if (not (@options[:force_excel] == :new && (not @options[:if_locked] == :take_writable)))
68
+ book = book_store.fetch(file, :readonly_excel => (@options[:read_only] ? @options[:force_excel] : nil)) rescue nil
69
+ if book
70
+ if (not @options[:force_excel] || (@options[:force_excel] == book.excel))
71
+ if book.excel.alive?
72
+ # condition: :if_unsaved is not set or :accept or workbook is not unsaved
73
+ if_unsaved_not_set_or_accept_or_workbook_saved = (@options[:if_unsaved] == :accept || @options[:if_unsaved] == :raise || (not book.workbook) || book.workbook.Saved)
74
+ if ((not book.alive?) || if_unsaved_not_set_or_accept_or_workbook_saved)
75
+ book.set_defaults(opts)
76
+ # reopen the book
77
+ book.get_workbook
78
+ end
79
+ return book if book.alive? && if_unsaved_not_set_or_accept_or_workbook_saved
80
+ end
81
+ end
82
+ end
83
+ end
84
+ @options[:excel] = @options[:force_excel] ? @options[:force_excel] : @options[:default_excel]
85
+ new(file, @options, &block)
86
+ end
38
87
  end
39
88
 
40
89
  def initialize(file, opts={ }, &block)
90
+ raise ExcelErrorOpen, "file #{file} not found" unless File.exist?(file)
91
+ set_defaults(opts)
92
+ @file = file
93
+ get_excel
94
+ get_workbook
95
+ book_store.store(self)
96
+ if block
97
+ begin
98
+ yield self
99
+ ensure
100
+ close
101
+ end
102
+ end
103
+ end
104
+
105
+ def set_defaults(opts)
41
106
  @options = {
42
- :reuse => true,
43
- :excel => nil,
44
- :read_only => false,
45
- :if_unsaved => :raise,
46
- :if_obstructed => :raise
107
+ :excel => :reuse,
108
+ :default_excel => :reuse,
109
+ :if_locked => :readonly,
110
+ :if_unsaved => :raise,
111
+ :if_obstructed => :raise,
112
+ :read_only => false
47
113
  }.merge(opts)
48
- excel_options = {:reuse => true}.merge(opts).delete_if{|k,v|
49
- k== :read_only || k== :if_unsaved || k == :if_obstructed}
50
- if not File.exist?(file)
51
- raise ExcelErrorOpen, "file #{file} not found"
114
+ end
115
+
116
+ def get_excel
117
+ if @options[:excel] == :reuse
118
+ @excel = Excel.new(:reuse => true)
119
+ end
120
+ @excel_options = nil
121
+ if (not @excel)
122
+ if @options[:excel] == :new
123
+ @excel_options = {:displayalerts => false, :visible => false}.merge(@options)
124
+ @excel_options[:reuse] = false
125
+ @excel = Excel.new(@excel_options)
126
+ else
127
+ @excel = @options[:excel]
128
+ end
52
129
  end
53
- @excel = @options[:excel] ? excel_options[:excel] : Excel.new(excel_options)
54
- workbooks = @excel.Workbooks
55
- @workbook = workbooks.Item(File.basename(file)) rescue nil
130
+ # if :excel => :new or (:excel => :reuse but could not reuse)
131
+ # keep the old values for :visible and :displayalerts, set them only if the parameters are given
132
+ if (not @excel_options)
133
+ @excel.displayalerts = @options[:displayalerts] unless @options[:displayalerts].nil?
134
+ @excel.visible = @options[:visible] unless @options[:visible].nil?
135
+ end
136
+ end
137
+
138
+ def get_workbook
139
+ @workbook = @excel.Workbooks.Item(File.basename(@file)) rescue nil
56
140
  if @workbook then
57
- obstructed_by_other_book = (File.basename(file) == File.basename(@workbook.Fullname)) &&
58
- (not (RobustExcelOle::absolute_path(file) == @workbook.Fullname))
141
+ obstructed_by_other_book = (File.basename(@file) == File.basename(@workbook.Fullname)) &&
142
+ (not (RobustExcelOle::absolute_path(@file) == @workbook.Fullname))
143
+ # if book is obstructed by a book with same name and different path
59
144
  if obstructed_by_other_book then
60
- # @workbook is not the desired workbook
61
145
  case @options[:if_obstructed]
62
146
  when :raise
63
147
  raise ExcelErrorOpen, "blocked by a book with the same name in a different path"
64
148
  when :forget
65
149
  @workbook.Close
150
+ @workbook = nil
151
+ open_workbook
66
152
  when :save
67
153
  save unless @workbook.Saved
68
154
  @workbook.Close
155
+ @workbook = nil
156
+ open_workbook
69
157
  when :close_if_saved
70
158
  if (not @workbook.Saved) then
71
159
  raise ExcelErrorOpen, "book with the same name in a different path is unsaved"
72
160
  else
73
161
  @workbook.Close
162
+ @workbook = nil
163
+ open_workbook
74
164
  end
75
- when :new_app
76
- excel_options[:reuse] = false
77
- @excel = Excel.new(excel_options)
78
- @workbook = nil
165
+ when :new_excel
166
+ @excel_options = {:displayalerts => false, :visible => false}.merge(@options)
167
+ @excel_options[:reuse] = false
168
+ @excel = Excel.new(@excel_options)
169
+ open_workbook
79
170
  else
80
171
  raise ExcelErrorOpen, ":if_obstructed: invalid option"
81
172
  end
82
173
  else
83
- # book open, not saved, not obstructed by an other book
174
+ # book open, not obstructed by an other book, but not saved
84
175
  if (not @workbook.Saved) then
85
- #p "book not saved"
86
176
  case @options[:if_unsaved]
87
177
  when :raise
88
- raise ExcelErrorOpen, "book is already open but not saved (#{File.basename(file)})"
178
+ raise ExcelErrorOpen, "book is already open but not saved (#{File.basename(@file)})"
89
179
  when :forget
90
180
  @workbook.Close
181
+ @workbook = nil
182
+ open_workbook
91
183
  when :accept
92
- #nothing
184
+ # do nothing
93
185
  when :alert
94
- old_displayalerts = @excel.DisplayAlerts # :nodoc:
95
- @excel.DisplayAlerts = true # :nodoc:
96
- when :new_app
97
- excel_options[:reuse] = false
98
- @excel = Excel.new(excel_options)
99
- @workbook = nil
186
+ @excel.with_displayalerts true do
187
+ open_workbook
188
+ end
189
+ when :new_excel
190
+ @excel_options = {:displayalerts => false, :visible => false}.merge(@options)
191
+ @excel_options[:reuse] = false
192
+ @excel = Excel.new(@excel_options)
193
+ open_workbook
100
194
  else
101
195
  raise ExcelErrorOpen, ":if_unsaved: invalid option"
102
196
  end
103
197
  end
104
198
  end
199
+ else
200
+ # book is not open
201
+ open_workbook
105
202
  end
106
- begin
107
- # if book not open (was not open,was closed with option :forget or shall be opened in new application)
108
- # or :if_unsaved => :alert
109
- if ((not alive?) || (@options[:if_unsaved] == :alert)) then
110
- begin
111
- @workbook = @excel.Workbooks.Open(RobustExcelOle::absolute_path(file),{ 'ReadOnly' => @options[:read_only] })
112
- rescue WIN32OLERuntimeError
113
- raise ExcelUserCanceled, "open: canceled by user"
114
- end
115
- end
116
- ensure
117
- if @options[:if_unsaved] == :alert then
118
- @excel.DisplayAlerts = old_displayalerts # :nodoc:
119
- end
120
- end
121
- if block
203
+ end
204
+
205
+ def open_workbook
206
+ #p "open_workbook:"
207
+ #p "@file:#{@file}"
208
+ if ((not @workbook) || (@options[:if_unsaved] == :alert) || @options[:if_obstructed]) then
122
209
  begin
123
- yield self
124
- ensure
125
- close
210
+ filename = RobustExcelOle::absolute_path(@file)
211
+ workbooks = @excel.Workbooks
212
+ workbooks.Open(filename,{ 'ReadOnly' => @options[:read_only] })
213
+ # workaround for bug in Excel 2010: workbook.Open does not always return
214
+ # the workbook with given file name
215
+ @workbook = workbooks.Item(File.basename(filename))
216
+ #rescue BookStoreError => e
217
+ # raise ExcelUserCanceled, "open: canceled by user: #{e}"
218
+ rescue WIN32OLERuntimeError
219
+ raise ExcelUserCanceled, "open: canceled by user"
126
220
  end
127
221
  end
128
- @workbook
129
222
  end
130
-
223
+
131
224
  # closes the book, if it is alive
132
225
  #
133
226
  # options:
@@ -137,35 +230,92 @@ module RobustExcelOle
137
230
  # :forget -> close the book
138
231
  # :alert -> give control to excel
139
232
  def close(opts = {:if_unsaved => :raise})
140
- if ((alive?) && (not @workbook.Saved) && (not @options[:read_only])) then
233
+ if ((alive?) && (not @workbook.Saved) && (not @workbook.ReadOnly)) then
141
234
  case opts[:if_unsaved]
142
235
  when :raise
143
- raise ExcelErrorClose, "book is unsaved (#{File.basename(filename)})"
236
+ raise ExcelErrorClose, "book is unsaved (#{File.basename(self.stored_filename)})"
144
237
  when :save
145
238
  save
239
+ close_workbook
146
240
  when :forget
147
- #nothing
241
+ close_workbook
148
242
  when :alert
149
- old_displayalerts = @excel.DisplayAlerts # :nodoc:
150
- @excel.DisplayAlerts = true # :nodoc:
243
+ @excel.with_displayalerts true do
244
+ close_workbook
245
+ end
151
246
  else
152
247
  raise ExcelErrorClose, ":if_unsaved: invalid option"
153
248
  end
249
+ else
250
+ close_workbook
154
251
  end
252
+ raise ExcelUserCanceled, "close: canceled by user" if alive? && opts[:if_unsaved] == :alert && (not @workbook.Saved)
253
+ end
254
+
255
+ private
256
+
257
+ def close_workbook
258
+ @workbook.Close if alive?
259
+ @workbook = nil unless alive?
260
+ end
261
+
262
+ public
263
+
264
+ # modify a book such that its state remains unchanged.
265
+ # options:
266
+ # :visible: Make the book visible in the Excel (default: false)
267
+ # :keep_open: let the book open after modification (default: false)
268
+ # if the book is read_only and modified (unsaved), then
269
+ # only the saved version of the book is unobtrusively modified,
270
+ # not the current changed version
271
+ # returns the block result
272
+ def self.unobtrusively(file, opts = { })
273
+ options = {
274
+ :keep_open => false,
275
+ :visible => false
276
+ }.merge(opts)
277
+ book = book_store.fetch(file)
278
+ was_not_alive_or_nil = book.nil? || (not book.alive?)
279
+ was_saved = was_not_alive_or_nil ? true : book.Saved
280
+ was_readonly = was_not_alive_or_nil ? false : book.ReadOnly
281
+ old_book = book if was_readonly
282
+ old_visible = book ? book.visible : false
155
283
  begin
156
- @workbook.Close if alive?
157
- @workbook = nil unless alive?
158
- raise ExcelUserCanceled, "close: canceled by user" if alive? && opts[:if_unsaved] == :alert && (not @workbook.Saved)
284
+ book = was_not_alive_or_nil ? open(file, :if_obstructed => :new_excel) :
285
+ (was_readonly ? open(file, :force_excel => :new) : book)
286
+ book.visible = options[:visible]
287
+ yield book
159
288
  ensure
160
- if opts[:if_unsaved] == :alert then
161
- @excel.DisplayAlerts = old_displayalerts # :nodoc:
289
+ book.save if (was_not_alive_or_nil || was_saved || was_readonly) && (not book.Saved)
290
+ if was_readonly
291
+ book.close
292
+ book = old_book
162
293
  end
294
+ book.visible = old_visible
295
+ book.close if (was_not_alive_or_nil && (not opts[:keep_open]))
163
296
  end
164
- #@excel.Workbooks.Close
165
- #@excel.Quit
166
297
  end
167
298
 
168
- # returns true, if the work book is alive, false otherwise
299
+ # returns the contents of a range or cell with given name
300
+ def nvalue(name)
301
+ begin
302
+ item = self.Names.Item(name)
303
+ rescue WIN32OLERuntimeError
304
+ raise ExcelErrorNValue, "name #{name} not in #{File.basename(self.stored_filename)}"
305
+ end
306
+ begin
307
+ referstorange = item.RefersToRange
308
+ rescue WIN32OLERuntimeError
309
+ raise ExcelErrorNValue, "range error in #{File.basename(self.stored_filename)}"
310
+ end
311
+ begin
312
+ value = referstorange.Value
313
+ rescue WIN32OLERuntimeError
314
+ raise ExcelErrorNValue, "value error in #{File.basename(self.stored_filename)}"
315
+ end
316
+ end
317
+
318
+ # returns true, if the workbook reacts to methods, false otherwise
169
319
  def alive?
170
320
  begin
171
321
  @workbook.Name
@@ -177,7 +327,7 @@ module RobustExcelOle
177
327
  end
178
328
  end
179
329
 
180
- # returns the full file name of the book
330
+ # returns the full file name of the workbook
181
331
  def filename
182
332
  @workbook.Fullname.tr('\\','/') rescue nil
183
333
  end
@@ -189,9 +339,29 @@ module RobustExcelOle
189
339
  self.filename == other_book.filename
190
340
  end
191
341
 
342
+ # returns if the Excel instance is visible
343
+ def visible
344
+ @excel.visible
345
+ end
346
+
347
+ # make the Excel instance visible or invisible
348
+ # option: visible_value true -> make Excel visible, false -> make Excel invisible
349
+ def visible= visible_value
350
+ @excel.visible = visible_value
351
+ end
352
+
353
+ # returns if DisplayAlerts is enabed in the Excel instance
354
+ def displayalerts
355
+ @excel.displayalerts
356
+ end
192
357
 
193
- attr_reader :excel
358
+ # enable in the Excel instance Dispayalerts
359
+ # option: displayalerts_value true -> enable DisplayAlerts, false -> disable DispayAlerts
360
+ def displayalerts= displayalerts_value
361
+ @excel.displayalerts = displayalerts_value
362
+ end
194
363
 
364
+
195
365
  # saves a book.
196
366
  # returns true, if successfully saved, nil otherwise
197
367
  def save
@@ -214,35 +384,47 @@ module RobustExcelOle
214
384
  # returns true, if successfully saved, nil otherwise
215
385
  def save_as(file = nil, opts = {:if_exists => :raise} )
216
386
  raise IOError, "Not opened for writing(open with :read_only option)" if @options[:read_only]
217
- dirname, basename = File.split(file)
218
- file_format =
219
- case File.extname(basename)
220
- when '.xls' : RobustExcelOle::XlExcel8
221
- when '.xlsx': RobustExcelOle::XlOpenXMLWorkbook
222
- when '.xlsm': RobustExcelOle::XlOpenXMLWorkbookMacroEnabled
223
- end
387
+ @opts = opts
224
388
  if File.exist?(file) then
225
- case opts[:if_exists]
389
+ case @opts[:if_exists]
226
390
  when :overwrite
227
391
  begin
228
392
  File.delete(file)
229
393
  rescue Errno::EACCES
230
394
  raise ExcelErrorSave, "book is open and used in Excel"
231
395
  end
396
+ save_as_workbook(file)
232
397
  when :alert
233
- old_displayalerts = @excel.DisplayAlerts # :nodoc:
234
- @excel.DisplayAlerts = true # :nodoc:
398
+ @excel.with_displayalerts true do
399
+ save_as_workbook(file)
400
+ end
235
401
  when :raise
236
- raise ExcelErrorSave, "book already exists: #{basename}"
402
+ raise ExcelErrorSave, "book already exists: #{File.basename(file)}"
237
403
  else
238
404
  raise ExcelErrorSave, ":if_exists: invalid option"
239
405
  end
406
+ else
407
+ save_as_workbook(file)
240
408
  end
409
+ true
410
+ end
411
+
412
+ private
413
+
414
+ def save_as_workbook(file)
241
415
  begin
242
- @workbook.SaveAs(RobustExcelOle::absolute_path(File.join(dirname, basename)), file_format)
416
+ dirname, basename = File.split(file)
417
+ file_format =
418
+ case File.extname(basename)
419
+ when '.xls' : RobustExcelOle::XlExcel8
420
+ when '.xlsx': RobustExcelOle::XlOpenXMLWorkbook
421
+ when '.xlsm': RobustExcelOle::XlOpenXMLWorkbookMacroEnabled
422
+ end
423
+ @workbook.SaveAs(RobustExcelOle::absolute_path(file), file_format)
424
+ book_store.store(self)
243
425
  rescue WIN32OLERuntimeError => msg
244
426
  if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
245
- if opts[:if_exists] == :alert then
427
+ if @opts[:if_exists] == :alert then
246
428
  raise ExcelErrorSave, "not saved or canceled by user"
247
429
  else
248
430
  return nil
@@ -251,15 +433,11 @@ module RobustExcelOle
251
433
  else
252
434
  raise ExcelErrorSaveUnknown, "unknown WIN32OELERuntimeError:\n#{msg.message}"
253
435
  end
254
- ensure
255
- if opts[:if_exists] == :alert then
256
- @excel.DisplayAlerts = old_displayalerts # :nodoc:
257
- end
258
436
  end
259
- true
260
437
  end
261
438
 
262
-
439
+ public
440
+
263
441
  def [] sheet
264
442
  sheet += 1 if sheet.is_a? Numeric
265
443
  RobustExcelOle::Sheet.new(@workbook.Worksheets.Item(sheet))
@@ -282,33 +460,88 @@ module RobustExcelOle
282
460
  after_or_before, base_sheet = opts.to_a.first || [:after, RobustExcelOle::Sheet.new(@workbook.Worksheets.Item(@workbook.Worksheets.Count))]
283
461
  base_sheet = base_sheet.sheet
284
462
  sheet ? sheet.Copy({ after_or_before.to_s => base_sheet }) : @workbook.WorkSheets.Add({ after_or_before.to_s => base_sheet })
285
-
286
463
  new_sheet = RobustExcelOle::Sheet.new(@excel.Activesheet)
287
- new_sheet.name = new_sheet_name if new_sheet_name
464
+ begin
465
+ new_sheet.name = new_sheet_name if new_sheet_name
466
+ rescue WIN32OLERuntimeError => msg
467
+ if msg.message =~ /OLE error code:800A03EC/
468
+ raise ExcelErrorSheet, "sheet name already exists"
469
+ end
470
+ end
288
471
  new_sheet
289
472
  end
290
473
 
474
+ def self.book_store
475
+ @@bookstore ||= BookStore.new
476
+ end
477
+
478
+ def book_store
479
+ self.class.book_store
480
+ end
481
+
482
+ private
483
+
484
+ def method_missing(name, *args)
485
+ if name.to_s[0,1] =~ /[A-Z]/
486
+ begin
487
+ @workbook.send(name, *args)
488
+ rescue WIN32OLERuntimeError => msg
489
+ if msg.message =~ /unknown property or method/
490
+ raise VBAMethodMissingError, "unknown VBA property or method #{name}"
491
+ else
492
+ raise msg
493
+ end
494
+ end
495
+ else
496
+ super
497
+ end
498
+ end
499
+
291
500
  end
501
+
502
+ public
292
503
 
293
- end
504
+ class ExcelErrorNValue < WIN32OLERuntimeError # :nodoc #
505
+ end
294
506
 
295
- class ExcelUserCanceled < RuntimeError # :nodoc: #
296
- end
507
+ class ExcelUserCanceled < RuntimeError # :nodoc: #
508
+ end
297
509
 
298
- class ExcelError < RuntimeError # :nodoc: #
299
- end
510
+ class ExcelError < RuntimeError # :nodoc: #
511
+ end
300
512
 
301
- class ExcelErrorSave < ExcelError # :nodoc: #
302
- end
513
+ class ExcelErrorSave < ExcelError # :nodoc: #
514
+ end
303
515
 
304
- class ExcelErrorSaveFailed < ExcelErrorSave # :nodoc: #
305
- end
516
+ class ExcelErrorSaveFailed < ExcelErrorSave # :nodoc: #
517
+ end
306
518
 
307
- class ExcelErrorSaveUnknown < ExcelErrorSave # :nodoc: #
308
- end
519
+ class ExcelErrorSaveUnknown < ExcelErrorSave # :nodoc: #
520
+ end
521
+
522
+ class ExcelErrorOpen < ExcelError # :nodoc: #
523
+ end
524
+
525
+ class ExcelErrorClose < ExcelError # :nodoc: #
526
+ end
527
+
528
+ class ExcelErrorSheet < ExcelError # :nodoc: #
529
+ end
309
530
 
310
- class ExcelErrorOpen < ExcelError # :nodoc: #
311
- end
312
531
 
313
- class ExcelErrorClose < ExcelError # :nodoc: #
314
532
  end
533
+
534
+
535
+ __END__
536
+
537
+
538
+ class Object
539
+ def update_extracted hash, key
540
+ value = hash[param_name]
541
+ self.send("#{key}=", value) if value
542
+ end
543
+ end
544
+ @excel.visible = @options[:visible] if @options[:visible]
545
+ @excel.displayalerts = @options[:dispayalerts]
546
+ @excel.update_extracted(@options, [:visible, :dispayalerts])
547
+ @excel.options.merge(@options.extract(:visible, :dispayalerts))