robust_excel_ole 0.4 → 0.5

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.
Files changed (47) hide show
  1. data/.gitignore +1 -0
  2. data/Changelog +15 -0
  3. data/README.rdoc +128 -63
  4. data/README_detail.rdoc +130 -60
  5. data/examples/edit_sheets/example_access_sheets_and_cells.rb +1 -1
  6. data/examples/edit_sheets/example_adding_sheets.rb +2 -2
  7. data/examples/edit_sheets/example_copying.rb +1 -1
  8. data/examples/edit_sheets/example_expanding.rb +1 -1
  9. data/examples/edit_sheets/example_ranges.rb +1 -1
  10. data/examples/edit_sheets/example_saving.rb +2 -2
  11. data/examples/open_save_close/example_control_to_excel.rb +1 -1
  12. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +2 -2
  13. data/examples/open_save_close/example_if_obstructed_save.rb +2 -2
  14. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  15. data/examples/open_save_close/example_if_unsaved_forget.rb +2 -2
  16. data/examples/open_save_close/example_if_unsaved_forget_more.rb +3 -3
  17. data/examples/open_save_close/example_read_only.rb +1 -1
  18. data/examples/open_save_close/example_rename_cells.rb +1 -1
  19. data/examples/open_save_close/example_simple.rb +1 -1
  20. data/examples/open_save_close/example_unobtrusively.rb +3 -3
  21. data/lib/robust_excel_ole.rb +1 -0
  22. data/lib/robust_excel_ole/book.rb +249 -193
  23. data/lib/robust_excel_ole/bookstore.rb +1 -1
  24. data/lib/robust_excel_ole/cell.rb +1 -1
  25. data/lib/robust_excel_ole/excel.rb +125 -4
  26. data/lib/robust_excel_ole/general.rb +1 -92
  27. data/lib/robust_excel_ole/range.rb +1 -1
  28. data/lib/robust_excel_ole/reo_common.rb +37 -0
  29. data/lib/robust_excel_ole/sheet.rb +77 -24
  30. data/lib/robust_excel_ole/version.rb +1 -1
  31. data/spec/book_spec.rb +112 -82
  32. data/spec/book_specs/book_close_spec.rb +44 -1
  33. data/spec/book_specs/book_misc_spec.rb +97 -92
  34. data/spec/book_specs/book_open_spec.rb +40 -8
  35. data/spec/book_specs/book_save_spec.rb +77 -7
  36. data/spec/book_specs/book_sheet_spec.rb +290 -66
  37. data/spec/book_specs/book_unobtr_spec.rb +99 -73
  38. data/spec/bookstore_spec.rb +1 -1
  39. data/spec/cell_spec.rb +2 -2
  40. data/spec/data/another_workbook.xls +0 -0
  41. data/spec/data/workbook.xls +0 -0
  42. data/spec/excel_spec.rb +174 -23
  43. data/spec/general_spec.rb +3 -18
  44. data/spec/range_spec.rb +3 -3
  45. data/spec/reo_common_spec.rb +104 -0
  46. data/spec/sheet_spec.rb +101 -60
  47. metadata +6 -4
@@ -14,7 +14,7 @@ begin
14
14
  simple_save_file = dir + 'workbook_save.xls'
15
15
  File.delete simple_save_file rescue nil
16
16
  book = Book.open(simple_file) # open a book
17
- sheet = book[0] # access a sheet via integer
17
+ sheet = book.sheet(1) # access a sheet via integer
18
18
  cell = sheet[1,1] # access the first cell
19
19
  puts "1st cell: #{cell.Value}" # put the value of the first cell
20
20
  sheet[1,1] = "complex" # write a value into a cell
@@ -33,7 +33,7 @@ begin
33
33
  show_sheets
34
34
 
35
35
  puts "adding a copy of the 2nd sheet"
36
- sheet = @book[1]
36
+ sheet = @book.sheet(2)
37
37
  @book.add_sheet sheet
38
38
  show_sheets
39
39
 
@@ -50,7 +50,7 @@ begin
50
50
  show_sheets
51
51
 
52
52
  puts "adding a copy of the 4th sheet before the 7th sheet and name it 'sheet_copy'"
53
- @book.add_sheet(@book[3], :as => 'sheet_copy', :after => @book[6])
53
+ @book.add_sheet(@book.sheet(4), :as => 'sheet_copy', :after => @book.sheet(7))
54
54
  show_sheets
55
55
 
56
56
  puts"adding a copy of the 2nd sheet and name it again 'second_sheet_copy'"
@@ -41,7 +41,7 @@ begin
41
41
  end
42
42
 
43
43
  sheet_names.each do |sheet_name|
44
- book[sheet_name].Delete()
44
+ book.sheet(sheet_name).Delete()
45
45
  end
46
46
  end
47
47
  end
@@ -42,7 +42,7 @@ begin
42
42
  end
43
43
 
44
44
  sheet_names.each do |sheet_name|
45
- book[sheet_name].Delete()
45
+ book.sheet(sheet_name).Delete()
46
46
  end
47
47
  end
48
48
  end
@@ -14,7 +14,7 @@ begin
14
14
  simple_save_file = dir + 'workbook_save.xls'
15
15
  File.delete simple_save_file rescue nil
16
16
  book = Book.open(simple_file) # open a book
17
- sheet = book['Sheet1'] # access a sheet via the name
17
+ sheet = book.sheet('Sheet1') # access a sheet via the name
18
18
  row_r = sheet.row_range(1) # access the whole range of the first row
19
19
  col_r = sheet.col_range(1, 1..2) # access the first two cells of the range of the first column
20
20
  cell = col_r[0] # access the first cell of these cells
@@ -25,13 +25,13 @@ begin
25
25
  book = Book.open(file_sheet_name)
26
26
  book.add_sheet sheet_orig
27
27
  book.each do |sheet|
28
- sheet.Delete() unless sheet.name == sheet_orig.name
28
+ sheet.Delete unless sheet.name == sheet_orig.name
29
29
  end
30
30
  book.close(:if_unsaved => :save)
31
31
  # alternative: delete all other sheets
32
32
  #book = Book.open(file_sheet_name, :force_excel => :new, :visible => true)
33
33
  #book.each do |sheet|
34
- # book[sheet.Name].Delete() unless sheet.Name == sheet_orig.Name
34
+ # book.sheet(sheet.Name).Delete() unless sheet.Name == sheet_orig.Name
35
35
  #end
36
36
  end
37
37
  end
@@ -14,7 +14,7 @@ begin
14
14
  book = Book.open(file_name) # open a book
15
15
  book.excel.visible = true # make current Excel visible
16
16
  sleep 1
17
- sheet = book[0] # access a sheet
17
+ sheet = book.sheet(1) # access a sheet
18
18
  sheet[1,1] = sheet[1,1].Value == "simple" ? "complex" : "simple" # change a cell
19
19
  sleep 1
20
20
  begin
@@ -14,7 +14,7 @@ begin
14
14
  other_file_name = dir + 'more_data/workbook.xls'
15
15
  book = Book.open(file_name, :visible => true) # open a book, make Excel visible
16
16
  sleep 1
17
- sheet = book[0]
17
+ sheet = book.sheet(1)
18
18
  first_cell = sheet[1,1].Value # access a sheet
19
19
  sheet[1,1] = first_cell == "simple" ? "complex" : "simple" # change a cell
20
20
  sleep 1
@@ -26,7 +26,7 @@ begin
26
26
  book.save # save the unsaved book
27
27
  new_book = Book.open(file_name, :if_obstructed => :close_if_saved) # open the new book, close the saved book
28
28
  sleep 1
29
- new_sheet = new_book[0]
29
+ new_sheet = new_book.sheet(1)
30
30
  new_first_cell = new_sheet[1,1].Value
31
31
  puts "the old book was saved" unless new_first_cell == first_cell
32
32
  new_book.close # close the books
@@ -14,7 +14,7 @@ begin
14
14
  other_file_name = dir + 'more_data/workbook.xls'
15
15
  book = Book.open(file_name, :visible => true) # open a book, make Excel visible
16
16
  sleep 1
17
- sheet = book[0]
17
+ sheet = book.sheet(1)
18
18
  first_cell = sheet[1,1].value # access a sheet
19
19
  sheet[1,1] = first_cell == "simple" ? "complex" : "simple" # change a cell
20
20
  sleep 1
@@ -22,7 +22,7 @@ begin
22
22
  sleep 1 #save the old book, close it, before
23
23
  old_book = Book.open(file_name, :if_obstructed => :forget ,:visible => true) # open the old book
24
24
  sleep 1
25
- old_sheet = old_book[0]
25
+ old_sheet = old_book.sheet(1)
26
26
  old_first_cell = old_sheet[1,1].value
27
27
  puts "the old book was saved" unless old_first_cell == first_cell
28
28
  new_book.close # close the books
@@ -12,7 +12,7 @@ begin
12
12
  dir = create_tmpdir
13
13
  file_name = dir + 'workbook.xls'
14
14
  book = Book.open(file_name) # open a book
15
- sheet = book[0] # access a sheet
15
+ sheet = book.sheet(1) # access a sheet
16
16
  sheet[1,1] = sheet[1,1].value == "simple" ? "complex" : "simple" # change a cell
17
17
  begin
18
18
  new_book = Book.open(file_name) # open another book with the same file name
@@ -14,13 +14,13 @@ begin
14
14
  book = Book.open(file_name) # open a book
15
15
  book.excel.visible = true # make current Excel visible
16
16
  sleep 1
17
- sheet = book[0] # access a sheet
17
+ sheet = book.sheet(1) # access a sheet
18
18
  first_cell = sheet[1,1].value
19
19
  sheet[1,1] = first_cell == "simple" ? "complex" : "simple" # change a cell
20
20
  sleep 1
21
21
  new_book = Book.open(file_name, :if_unsaved => :forget) # open another book with the same file name
22
22
  # and close the unsaved book without saving it
23
- sheet_new_book = new_book[0]
23
+ sheet_new_book = new_book.sheet(1)
24
24
  if (not book.alive?) && new_book.alive? && sheet_new_book[1,1].value == first_cell then # check whether the unsaved book
25
25
  puts "open with :if_unsaved => :forget : the unsaved book is closed and not saved." # is closed and was not saved
26
26
  end
@@ -14,12 +14,12 @@ begin
14
14
  book = Book.open(file_name) # open a book
15
15
  book.excel.visible = true # make current Excel visible
16
16
  sleep
17
- sheet = book[0] # access a sheet
17
+ sheet = book.sheet(1) # access a sheet
18
18
  first_cell = sheet[1,1].value
19
19
  sheet[1,1] = first_cell == "simple" ? "complex" : "simple" # change a cell
20
20
  sleep 1
21
21
  new_book = Book.open(file_name, :if_unsaved => :new_excel, :visible => true) # open another book with the same file name in a new Excel
22
- sheet_new_book = new_book[0]
22
+ sheet_new_book = new_book.sheet(1)
23
23
  if (not book.alive?) && new_book.alive? && sheet_new_book[1,1].value == first_cell then # check whether the unsaved book
24
24
  puts "open with :if_unsaved => :forget : the unsaved book is closed and not saved." # is closed and was not saved
25
25
  end
@@ -28,7 +28,7 @@ begin
28
28
  # open another book in the running Excel application, and make Excel visible, closing the unsaved book
29
29
  another_book = Book.open(file_name, :if_unsaved => :forget, :visible => true)
30
30
  sleep 1
31
- sheet_another_book = another_book[0]
31
+ sheet_another_book = another_book.sheet(1)
32
32
  sheet_another_book[1,1] = sheet_another_book[1,1].value == "simple" ? "complex" : "simple" # change a cell
33
33
  another_book.close(:if_unsaved => :forget ) # close the last book without saving it.
34
34
  book.close(:if_unsaved => :save) # close the first book and save it before
@@ -12,7 +12,7 @@ begin
12
12
  file_name = dir + 'workbook.xls'
13
13
  other_file_name = dir + 'different_workbook.xls'
14
14
  book = Book.open(file_name, :read_only => true, :visible => true) # open a book with read_only and make Excel visible
15
- sheet = book[0] # access a sheet
15
+ sheet = book.sheet(1) # access a sheet
16
16
  sleep 1
17
17
  sheet[1,1] = sheet[1,1].value == "simple" ? "complex" : "simple" # change a cell
18
18
  sleep 1
@@ -13,7 +13,7 @@ begin
13
13
  file_name = dir + 'workbook.xls'
14
14
  book = Book.open(file_name) # open a book. default: :read_only => false
15
15
  book.excel.visible = true # make current Excel visible
16
- sheet = book[0]
16
+ sheet = book.sheet(1)
17
17
  workbook = book.ole_workbook
18
18
  fullname = workbook.Fullname
19
19
  puts "fullname: #{fullname}"
@@ -19,7 +19,7 @@ begin
19
19
  other_file_name = dir + 'different_workbook.xls'
20
20
  book = Book.open(file_name) # open a book. default: :read_only => false
21
21
  book.excel.visible = true # make current Excel visible
22
- sheet = book[0] # access a sheet
22
+ sheet = book.sheet(1) # access a sheet
23
23
  sleep 1
24
24
  sheet[1,1] = sheet[1,1].value == "simple" ? "complex" : "simple" # change a cell
25
25
  sleep 1
@@ -11,14 +11,14 @@ begin
11
11
  dir = create_tmpdir
12
12
  simple_file = dir + 'workbook.xls'
13
13
  book = Book.open(simple_file, :visible => true) # open a book, make Excel visible
14
- old_sheet = book[0]
14
+ old_sheet = book.sheet(1)
15
15
  p "1st cell: #{old_sheet[1,1].value}"
16
16
  sleep 2
17
17
  Book.unobtrusively(simple_file) do |book| # modify the book and keep its status unchanged
18
- sheet = book[0]
18
+ sheet = book.sheet(1)
19
19
  sheet[1,1] = sheet[1,1].value == "simple" ? "complex" : "simple"
20
20
  end
21
- new_sheet = book[0]
21
+ new_sheet = book.sheet(1)
22
22
  p "1st cell: #{new_sheet[1,1].value}"
23
23
  p "book saved" if book.Saved
24
24
  book.close # close the book
@@ -1,4 +1,5 @@
1
1
  require "win32ole"
2
+ require File.join(File.dirname(__FILE__), 'robust_excel_ole/reo_common')
2
3
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/general')
3
4
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/excel')
4
5
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/bookstore')
@@ -4,7 +4,7 @@ require 'weakref'
4
4
 
5
5
  module RobustExcelOle
6
6
 
7
- class Book
7
+ class Book < REOCommon
8
8
 
9
9
  attr_accessor :excel
10
10
  attr_accessor :ole_workbook
@@ -21,7 +21,8 @@ module RobustExcelOle
21
21
  :if_unsaved => :raise,
22
22
  :if_obstructed => :raise,
23
23
  :if_absent => :raise,
24
- :read_only => false
24
+ :read_only => false,
25
+ :check_compatibility => true
25
26
  }
26
27
 
27
28
  class << self
@@ -31,7 +32,7 @@ module RobustExcelOle
31
32
  # @param [Hash] opts the options
32
33
  # @option opts [Variant] :default_excel :reuse (default), :new, or <excel-instance>
33
34
  # @option opts [Variant] :force_excel :new (default), or <excel-instance>
34
- # @option opts [Symbol] :if_unsaved :raise (default), :forget, :accept, :alert, or :new_excel
35
+ # @option opts [Symbol] :if_unsaved :raise (default), :forget, :accept, :alert, :excel, or :new_excel
35
36
  # @option opts [Symbol] :if_obstructed :raise (default), :forget, :save, :close_if_saved, or _new_excel
36
37
  # @option opts [Symbol] :if_absent :raise (default), or :create
37
38
  # @option opts [Boolean] :read_only true (default), or false
@@ -52,7 +53,7 @@ module RobustExcelOle
52
53
  # :raise -> raises an exception
53
54
  # :forget -> close the unsaved workbook, open the new workbook
54
55
  # :accept -> lets the unsaved workbook open
55
- # :alert -> gives control to Excel
56
+ # :alert or :excel -> gives control to Excel
56
57
  # :new_excel -> opens the new workbook in a new Excel instance
57
58
  # :if_obstructed if a workbook with the same name in a different path is open, then
58
59
  # :raise -> raises an exception
@@ -66,7 +67,8 @@ module RobustExcelOle
66
67
  #
67
68
  # :read_only opens in read-only mode
68
69
  # :displayalerts enables DisplayAlerts in Excel
69
- # :visible makes visible in Excel
70
+ # :visible makes visible in Excel
71
+ # :check_compatibility check compatibility when saving
70
72
  # if :default_excel is set, then DisplayAlerts and Visible are set only if these parameters are given
71
73
  # @return [Book] a workbook
72
74
  def open(file, opts={ }, &block)
@@ -114,7 +116,7 @@ module RobustExcelOle
114
116
  end
115
117
 
116
118
  # creates a new Book object, if a file name is given
117
- # lifts the workbook to a Book object, if a workbook is given
119
+ # Promotes the workbook to a Book object, if a win32ole-workbook is given
118
120
  # @param [Variant] file_or_workbook file name or workbook
119
121
  # @param [Hash] opts the options
120
122
  # @option opts [Symbol] see above
@@ -165,7 +167,11 @@ module RobustExcelOle
165
167
  object.excel
166
168
  end
167
169
  else
168
- object.excel
170
+ begin
171
+ object.excel
172
+ rescue
173
+ raise ExcelErrorOpen, "given object is neither an Excel, a Book, nor a Win32ole"
174
+ end
169
175
  end
170
176
  #rescue
171
177
  # trace "no Excel, Book, or WIN32OLE object representing a Workbook or an Excel instance"
@@ -215,7 +221,7 @@ module RobustExcelOle
215
221
  if obstructed_by_other_book then
216
222
  case options[:if_obstructed]
217
223
  when :raise
218
- raise ExcelErrorOpen, "blocked by a book with the same name in a different path: #{File.basename(file).inspect}"
224
+ raise ExcelErrorOpen, "blocked by a book with the same name in a different path: #{@ole_workbook.Fullname.tr('\\','/')}"
219
225
  when :forget
220
226
  @ole_workbook.Close
221
227
  @ole_workbook = nil
@@ -227,7 +233,7 @@ module RobustExcelOle
227
233
  open_or_create_workbook(file, options)
228
234
  when :close_if_saved
229
235
  if (not @ole_workbook.Saved) then
230
- raise ExcelErrorOpen, "workbook with the same name in a different path is unsaved: #{File.basename(file).inspect}"
236
+ raise ExcelErrorOpen, "workbook with the same name in a different path is unsaved: #{@ole_workbook.Fullname.tr('\\','/')}"
231
237
  else
232
238
  @ole_workbook.Close
233
239
  @ole_workbook = nil
@@ -253,7 +259,7 @@ module RobustExcelOle
253
259
  open_or_create_workbook(file, options)
254
260
  when :accept
255
261
  # do nothing
256
- when :alert
262
+ when :alert, :excel
257
263
  @excel.with_displayalerts true do
258
264
  open_or_create_workbook(file,options)
259
265
  end
@@ -299,7 +305,8 @@ module RobustExcelOle
299
305
  workbooks.Add if @excel.Version == "12.0" && count == 0
300
306
  workbooks.Open(filename,{ 'ReadOnly' => options[:read_only] })
301
307
  workbooks.Item(1).Close if @excel.Version == "12.0" && count == 0
302
- @can_be_closed = false
308
+ workbooks.Item(1).CheckCompatibility = options[:check_compatibility]
309
+ @can_be_closed = false if @can_be_closed.nil?
303
310
  rescue WIN32OLERuntimeError => msg
304
311
  trace "WIN32OLERuntimeError: #{msg.message}"
305
312
  if msg.message =~ /800A03EC/
@@ -329,7 +336,7 @@ module RobustExcelOle
329
336
  # :save -> saves the workbook before it is closed
330
337
  # :forget -> closes the workbook
331
338
  # :keep_open -> keep the workbook open
332
- # :alert -> gives control to excel
339
+ # :alert or :excel -> gives control to excel
333
340
  # @raise ExcelErrorClose if the option :if_unsaved is :raise and the workbook is unsaved, or option is invalid
334
341
  # @raise ExcelErrorCanceled if the user has canceled
335
342
  def close(opts = {:if_unsaved => :raise})
@@ -344,7 +351,7 @@ module RobustExcelOle
344
351
  close_workbook
345
352
  when :keep_open
346
353
  # nothing
347
- when :alert
354
+ when :alert, :excel
348
355
  @excel.with_displayalerts true do
349
356
  close_workbook
350
357
  end
@@ -354,7 +361,8 @@ module RobustExcelOle
354
361
  else
355
362
  close_workbook
356
363
  end
357
- raise ExcelUserCanceled, "close: canceled by user" if alive? && opts[:if_unsaved] == :alert && (not @ole_workbook.Saved)
364
+ raise ExcelUserCanceled, "close: canceled by user" if alive? &&
365
+ (opts[:if_unsaved] == :alert || opts[:if_unsaved] == :excel) && (not @ole_workbook.Saved)
358
366
  end
359
367
 
360
368
  private
@@ -403,8 +411,9 @@ module RobustExcelOle
403
411
  # true: closes it and open it as writable in the Excel instance where it was open so far
404
412
  # false (default) opens it as writable in another running excel instance, if it exists,
405
413
  # otherwise open in a new Excel instance.
406
- # :displayalerts enables DisplayAlerts in Excel
407
- # :visible makes visible in Excel
414
+ # :displayalerts enables DisplayAlerts in Excel
415
+ # :visible makes visible in Excel
416
+ # :check_compatibility checks compatibility when saving
408
417
  # @return [Book] a workbook
409
418
  def self.unobtrusively(file, if_closed = nil, opts = { }, &block)
410
419
  if if_closed.is_a? Hash
@@ -415,7 +424,8 @@ module RobustExcelOle
415
424
  options = {
416
425
  :read_only => false,
417
426
  :readonly_excel => false,
418
- :keep_open => false
427
+ :keep_open => false,
428
+ :check_compatibility => true
419
429
  }.merge(opts)
420
430
  book = bookstore.fetch(file, :prefer_writable => (not options[:read_only]))
421
431
  was_not_alive_or_nil = book.nil? || (not book.alive?)
@@ -428,7 +438,7 @@ module RobustExcelOle
428
438
  false
429
439
  end
430
440
  was_saved = was_not_alive_or_nil ? true : book.saved
431
- was_writable = book.writable unless was_not_alive_or_nil
441
+ was_writable = book.writable unless was_not_alive_or_nil
432
442
  begin
433
443
  book =
434
444
  if was_not_alive_or_nil
@@ -450,15 +460,19 @@ module RobustExcelOle
450
460
  end
451
461
  book.excel.displayalerts = options[:displayalerts] unless options[:displayalerts].nil?
452
462
  book.excel.visible = options[:visible] unless options[:visible].nil?
463
+ old_check_compatibility = book.CheckCompatibility
464
+ book.CheckCompatibility = options[:check_compatibility]
453
465
  yield book
454
466
  ensure
455
- book.save if (was_not_alive_or_nil || was_saved || ((not options[:read_only]) && (not was_writable))) && (not options[:read_only]) && book && (not book.saved)
467
+ was_saved_or_appeared = was_saved || was_not_alive_or_nil || (not was_writable)
468
+ book.save if book && (not book.saved) && (not options[:read_only]) && was_saved_or_appeared
456
469
  # book was open, readonly and shoud be modified
457
470
  if (not was_not_alive_or_nil) && (not options[:read_only]) && (not was_writable) && options[:readonly_excel]
458
471
  open(file, :force_excel => book.excel, :if_obstructed => :new_excel, :read_only => true)
459
472
  end
460
473
  @can_be_closed = true if options[:keep_open] && book
461
474
  book.close if (was_not_alive_or_nil && (not now_alive) && (not options[:keep_open]) && book)
475
+ book.CheckCompatibility = old_check_compatibility if book && book.alive?
462
476
  end
463
477
  end
464
478
 
@@ -467,136 +481,6 @@ module RobustExcelOle
467
481
  self.class.open(self.stored_filename)
468
482
  end
469
483
 
470
- # renames a range
471
- # @param [String] name the previous range name
472
- # @param [String] new_name the new range name
473
- # @raise ExcelError if name is not in the file, or if new_name cannot be set
474
- def rename_range(name, new_name)
475
- begin
476
- item = self.Names.Item(name)
477
- rescue WIN32OLERuntimeError
478
- raise ExcelError, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
479
- end
480
- begin
481
- item.Name = new_name
482
- rescue WIN32OLERuntimeError
483
- raise ExcelError, "name error in #{File.basename(self.stored_filename).inspect}"
484
- end
485
- end
486
-
487
- # returns the contents of a range with given name
488
- # @param [String] name the range name
489
- # @param [Hash] opts the options
490
- # @option opts [Symbol] :default the default value that is provided if no contents could be returned
491
- # @raise ExcelError if range name is not in the workbook
492
- # @raise SheetError if range value could not be evaluated
493
- # @return [Variant] the contents of a range with given name
494
- # if no contents could be returned, then return default value, if a default value was provided
495
- # raise an error, otherwise
496
- def nvalue(name, opts = {:default => nil})
497
- begin
498
- item = self.Names.Item(name)
499
- rescue WIN32OLERuntimeError
500
- return opts[:default] if opts[:default]
501
- raise ExcelError, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
502
- end
503
- begin
504
- value = item.RefersToRange.Value
505
- rescue WIN32OLERuntimeError
506
- begin
507
- sheet = self[0]
508
- value = sheet.Evaluate(name)
509
- rescue WIN32OLERuntimeError
510
- return opts[:default] if opts[:default]
511
- raise SheetError, "cannot evaluate name #{name.inspect} in sheet"
512
- end
513
- end
514
- if value == -2146826259
515
- return opts[:default] if opts[:default]
516
- raise SheetError, "cannot evaluate name #{name.inspect} in sheet"
517
- end
518
- return opts[:default] if (value.nil? && opts[:default])
519
- value
520
- end
521
-
522
- # sets the contents of a range with given name
523
- # @param [String] name the range name
524
- # @param [Variant] value the contents of the range
525
- # @raise ExcelError if range name is not in the workbook or if a RefersToRange error occurs
526
- def set_nvalue(name, value)
527
- begin
528
- item = self.Names.Item(name)
529
- rescue WIN32OLERuntimeError
530
- raise ExcelError, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
531
- end
532
- begin
533
- item.RefersToRange.Value = value
534
- rescue WIN32OLERuntimeError
535
- raise ExcelError, "RefersToRange error of name #{name.inspect} in #{File.basename(self.stored_filename).inspect}"
536
- end
537
- end
538
-
539
- # brings workbook to foreground, makes it available for heyboard inputs, makes the Excel instance visible
540
- # @raise ExcelError if workbook cannot be activated
541
- def activate
542
- @excel.visible = true
543
- begin
544
- Win32API.new("user32","SetForegroundWindow","I","I").call(@excel.hwnd) # Excel 2010
545
- @ole_workbook.Activate # Excel 2007
546
- rescue WIN32OLERuntimeError
547
- raise ExcelError, "cannot activate"
548
- end
549
- end
550
-
551
- # returns true, if the workbook is visible, false otherwise
552
- def visible
553
- @excel.Windows(@ole_workbook.Name).Visible
554
- end
555
-
556
- # makes a workbook visible or invisible
557
- # @param [Boolean] visible_value value that determines whether the workbook shall be visible
558
- def visible= visible_value
559
- saved = @ole_workbook.Saved
560
- @excel.Windows(@ole_workbook.Name).Visible = visible_value
561
- save if saved
562
- end
563
-
564
- # returns true, if the workbook reacts to methods, false otherwise
565
- def alive?
566
- begin
567
- @ole_workbook.Name
568
- true
569
- rescue
570
- @ole_workbook = nil # dead object won't be alive again
571
- #t $!.message
572
- false
573
- end
574
- end
575
-
576
- # returns the full file name of the workbook
577
- def filename
578
- @ole_workbook.Fullname.tr('\\','/') rescue nil
579
- end
580
-
581
- def writable # :nodoc: #
582
- (not @ole_workbook.ReadOnly) if @ole_workbook
583
- end
584
-
585
- def saved # :nodoc: #
586
- @ole_workbook.Saved if @ole_workbook
587
- end
588
-
589
- # @return [Boolean] true, if the full book names and excel Instances are identical, false otherwise
590
- def == other_book
591
- other_book.is_a?(Book) &&
592
- @excel == other_book.excel &&
593
- self.filename == other_book.filename
594
- end
595
-
596
- def self.books
597
- bookstore.books
598
- end
599
-
600
484
  # simple save of a workbook.
601
485
  # @raise ExcelErrorSave if workbook is not alive or opened for read-only, or another error occurs
602
486
  # @return [Boolean] true, if successfully saved, nil otherwise
@@ -618,13 +502,13 @@ module RobustExcelOle
618
502
  # saves a workbook with a given file name.
619
503
  # @param [String] file file name
620
504
  # @param [Hash] opts the options
621
- # @option opts [Symbol] :if_exists :raise (default), :overwrite, or :alert
505
+ # @option opts [Symbol] :if_exists :raise (default), :overwrite, or :alert, :excel
622
506
  # @option opts [Symbol] :if_obstructed :raise (default), :forget, :save, or :close_if_saved
623
507
  # options:
624
508
  # :if_exists if a file with the same name exists, then
625
509
  # :raise -> raises an exception, dont't write the file (default)
626
510
  # :overwrite -> writes the file, delete the old file
627
- # :alert -> gives control to Excel
511
+ # :alert or :excel -> gives control to Excel
628
512
  # :if_obstructed if a workbook with the same name and different path is already open and blocks the saving, then
629
513
  # :raise -> raises an exception
630
514
  # :forget -> closes the blocking workbook
@@ -634,7 +518,7 @@ module RobustExcelOle
634
518
  # @raise ExcelErrorSave if workbook is not alive, opened in read-only mode, invalid options,
635
519
  # the file already exists (with option :if_exists :raise),
636
520
  # the workbook is blocked by another one (with option :if_obstructed :raise)
637
- # @return [Boolean] true, if successfully saved, nil otherwise
521
+ # @return [Book], the book itself, if successfully saved, raises an exception otherwise
638
522
  def save_as(file = nil, opts = { } )
639
523
  raise ExcelErrorSave, "Workbook is not alive" if (not alive?)
640
524
  raise ExcelErrorSave, "Not opened for writing (opened with :read_only option)" if @ole_workbook.ReadOnly
@@ -647,7 +531,7 @@ module RobustExcelOle
647
531
  when :overwrite
648
532
  if file == self.filename
649
533
  save
650
- return
534
+ return self
651
535
  else
652
536
  begin
653
537
  File.delete(file)
@@ -655,12 +539,11 @@ module RobustExcelOle
655
539
  raise ExcelErrorSave, "workbook is open and used in Excel"
656
540
  end
657
541
  end
658
- when :alert
542
+ when :alert, :excel
659
543
  @excel.with_displayalerts true do
660
544
  save_as_workbook(file, options)
661
545
  end
662
- true
663
- return
546
+ return self
664
547
  when :raise
665
548
  raise ExcelErrorSave, "file already exists: #{File.basename(file).inspect}"
666
549
  else
@@ -676,7 +559,7 @@ module RobustExcelOle
676
559
  if blocking_workbook then
677
560
  case options[:if_obstructed]
678
561
  when :raise
679
- raise ExcelErrorSave, "blocked by another workbook: #{File.basename(file).inspect}"
562
+ raise ExcelErrorSave, "blocked by another workbook: #{blocking_workbook.Fullname.tr('\\','/')}"
680
563
  when :forget
681
564
  # nothing
682
565
  when :save
@@ -689,7 +572,7 @@ module RobustExcelOle
689
572
  blocking_workbook.Close
690
573
  end
691
574
  save_as_workbook(file, options)
692
- true
575
+ self
693
576
  end
694
577
 
695
578
  private
@@ -707,7 +590,7 @@ module RobustExcelOle
707
590
  bookstore.store(self)
708
591
  rescue WIN32OLERuntimeError => msg
709
592
  if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
710
- if options[:if_exists] == :alert then
593
+ if options[:if_exists] == :alert || options[:if_exists] == :excel then
711
594
  raise ExcelErrorSave, "not saved or canceled by user"
712
595
  else
713
596
  return nil
@@ -722,65 +605,238 @@ module RobustExcelOle
722
605
  public
723
606
 
724
607
  # returns a sheet, if a sheet name or a number is given
725
- # returns the value of the range, if a global name of a range in the book is given
726
- def [] name
727
- name += 1 if name.is_a? Numeric
608
+ # @param [String] or [Number]
609
+ # @returns [Sheet]
610
+ def sheet(name)
728
611
  begin
729
612
  sheet_class.new(@ole_workbook.Worksheets.Item(name))
730
613
  rescue WIN32OLERuntimeError => msg
731
- if msg.message =~ /8002000B/
732
- nvalue(name)
733
- else
734
- raise ExcelError, "could neither return a sheet nor a value of a range when giving the name #{name.inspect}"
735
- end
614
+ raise ExcelError, "could not return a sheet with name #{name.inspect}"
615
+ trace "#{msg.message}"
736
616
  end
737
- end
738
-
739
- # sets the value of a range given its name
740
- # @param [String] name the name of the range
741
- # @param [Variant] value the contents of the range
742
- def []= (name, value)
743
- set_nvalue(name,value)
744
- end
617
+ end
745
618
 
746
619
  def each
747
620
  @ole_workbook.Worksheets.each do |sheet|
748
621
  yield sheet_class.new(sheet)
749
622
  end
750
623
  end
624
+
625
+ # copies a sheet to another position
626
+ # default: copied sheet is appended
627
+ # @param [Sheet] sheet a sheet that shall be copied
628
+ # @param [Hash] opts the options
629
+ # @option opts [Symbol] :as new name of the copied sheet
630
+ # @option opts [Symbol] :before a sheet before which the sheet shall be inserted
631
+ # @option opts [Symbol] :after a sheet after which the sheet shall be inserted
632
+ # @raise ExcelErrorSheet if the sheet name already exists
633
+ # @return [Sheet] the copied sheet
634
+ def copy_sheet(sheet, opts = { })
635
+ new_sheet_name = opts.delete(:as)
636
+ after_or_before, base_sheet = opts.to_a.first || [:after, last_sheet]
637
+ sheet.Copy({ after_or_before.to_s => base_sheet.worksheet })
638
+ new_sheet = sheet_class.new(@excel.Activesheet)
639
+ begin
640
+ new_sheet.name = new_sheet_name if new_sheet_name
641
+ rescue WIN32OLERuntimeError => msg
642
+ msg.message =~ /800A03EC/ ? raise(ExcelErrorSheet, "sheet name already exists") : raise(ExcelErrorSheetUnknown)
643
+ end
644
+ new_sheet
645
+ end
751
646
 
752
- # adds a sheet to the workbook
753
- # @param [Sheet] sheet a sheet
647
+ # adds an empty sheet
648
+ # default: empty sheet is appended
754
649
  # @param [Hash] opts the options
755
- # @option opts [Symbol] :as new name of the copyed sheet
650
+ # @option opts [Symbol] :as new name of the copied added sheet
756
651
  # @option opts [Symbol] :before a sheet before which the sheet shall be inserted
757
652
  # @option opts [Symbol] :after a sheet after which the sheet shall be inserted
758
653
  # @raise ExcelErrorSheet if the sheet name already exists
759
654
  # @return [Sheet] the added sheet
760
- def add_sheet(sheet = nil, opts = { })
761
- if sheet.is_a? Hash
762
- opts = sheet
763
- sheet = nil
764
- end
655
+ def add_empty_sheet(opts = { })
765
656
  new_sheet_name = opts.delete(:as)
766
- ws = @ole_workbook.Worksheets
767
- after_or_before, base_sheet = opts.to_a.first || [:after, sheet_class.new(ws.Item(ws.Count))]
768
- base_sheet = base_sheet.worksheet
769
- sheet ? sheet.Copy({ after_or_before.to_s => base_sheet }) : @ole_workbook.WorkSheets.Add({ after_or_before.to_s => base_sheet })
657
+ after_or_before, base_sheet = opts.to_a.first || [:after, last_sheet]
658
+ @ole_workbook.Worksheets.Add({ after_or_before.to_s => base_sheet.worksheet })
770
659
  new_sheet = sheet_class.new(@excel.Activesheet)
771
660
  begin
772
661
  new_sheet.name = new_sheet_name if new_sheet_name
773
662
  rescue WIN32OLERuntimeError => msg
774
- if msg.message =~ /800A03EC/
775
- raise ExcelErrorSheet, "sheet name already exists"
776
- else
777
- trace "#{msg.message}"
778
- raise ExcelErrorSheetUnknown
779
- end
663
+ msg.message =~ /800A03EC/ ? raise(ExcelErrorSheet, "sheet name already exists") : raise(ExcelErrorSheetUnknown)
780
664
  end
781
665
  new_sheet
666
+ end
667
+
668
+ # copies a sheet to another position if a sheet is given, or adds an empty sheet
669
+ # default: copied or empty sheet is appended, i.e. added behind the last sheet
670
+ # @param [Sheet] sheet a sheet that shall be copied (optional)
671
+ # @param [Hash] opts the options
672
+ # @option opts [Symbol] :as new name of the copied or added sheet
673
+ # @option opts [Symbol] :before a sheet before which the sheet shall be inserted
674
+ # @option opts [Symbol] :after a sheet after which the sheet shall be inserted
675
+ # @raise ExcelErrorSheet if the sheet name already exists
676
+ # @return [Sheet] the copied or added sheet
677
+ def add_or_copy_sheet(sheet = nil, opts = { })
678
+ if sheet.is_a? Hash
679
+ opts = sheet
680
+ sheet = nil
681
+ end
682
+ sheet ? copy_sheet(sheet, opts) : add_empty_sheet(opts)
782
683
  end
783
684
 
685
+ # for compatibility to older versions
686
+ def add_sheet(sheet = nil, opts = { })
687
+ add_or_copy_sheet(sheet, opts)
688
+ end
689
+
690
+ def last_sheet
691
+ sheet_class.new(@ole_workbook.Worksheets.Item(@ole_workbook.Worksheets.Count))
692
+ end
693
+
694
+ def first_sheet
695
+ sheet_class.new(@ole_workbook.Worksheets.Item(1))
696
+ end
697
+
698
+ # returns the value of a range
699
+ # @param [String] name the name of a range
700
+ # @returns [Variant] the value of the range
701
+ def [] name
702
+ nameval(name)
703
+ end
704
+
705
+ # sets the value of a range
706
+ # @param [String] name the name of the range
707
+ # @param [Variant] value the contents of the range
708
+ def []= (name, value)
709
+ set_nameval(name,value)
710
+ end
711
+
712
+ # returns the contents of a range with given name
713
+ # evaluates formula contents of the range is a formula
714
+ # if no contents could be returned, then return default value, if provided, raise error otherwise
715
+ # @param [String] name the name of the range
716
+ # @param [Hash] opts the options
717
+ # @option opts [Symbol] :default the default value that is provided if no contents could be returned
718
+ # @raise ExcelError if range name is not in the workbook or if range value could not be evaluated
719
+ # @return [Variant] the contents of a range with given name
720
+ def nameval(name, opts = {:default => nil})
721
+ begin
722
+ name_obj = self.Names.Item(name)
723
+ rescue WIN32OLERuntimeError
724
+ return opts[:default] if opts[:default]
725
+ raise ExcelError, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
726
+ end
727
+ begin
728
+ value = name_obj.RefersToRange.Value
729
+ rescue WIN32OLERuntimeError
730
+ begin
731
+ value = self.sheet(1).Evaluate(name_obj.Name)
732
+ rescue WIN32OLERuntimeError
733
+ return opts[:default] if opts[:default]
734
+ raise SheetError, "cannot evaluate name #{name.inspect} in #{File.basename(self.stored_filename).inspect}"
735
+ end
736
+ end
737
+ if value == -2146826259
738
+ return opts[:default] if opts[:default]
739
+ raise SheetError, "cannot evaluate name #{name.inspect} in #{File.basename(self.stored_filename).inspect}"
740
+ end
741
+ return opts[:default] if (value.nil? && opts[:default])
742
+ value
743
+ end
744
+
745
+ # sets the contents of a range
746
+ # @param [String] name the name of a range
747
+ # @param [Variant] value the contents of the range
748
+ # @raise ExcelError if range name is not in the workbook or if value could not be assigned to range
749
+ def set_nameval(name, value)
750
+ begin
751
+ name_obj = self.Names.Item(name)
752
+ rescue WIN32OLERuntimeError
753
+ raise ExcelError, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
754
+ end
755
+ begin
756
+ name_obj.RefersToRange.Value = value
757
+ rescue WIN32OLERuntimeError
758
+ raise ExcelError, "cannot assign value to range named #{name.inspect} in #{File.basename(self.stored_filename).inspect}"
759
+ end
760
+ end
761
+
762
+ # renames a range
763
+ # @param [String] name the previous range name
764
+ # @param [String] new_name the new range name
765
+ # @raise ExcelError if name is not in the file, or if new_name cannot be set
766
+ def rename_range(name, new_name)
767
+ begin
768
+ item = self.Names.Item(name)
769
+ rescue WIN32OLERuntimeError
770
+ raise ExcelError, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
771
+ end
772
+ begin
773
+ item.Name = new_name
774
+ rescue WIN32OLERuntimeError
775
+ raise ExcelError, "name error in #{File.basename(self.stored_filename).inspect}"
776
+ end
777
+ end
778
+
779
+ # brings workbook to foreground, makes it available for heyboard inputs, makes the Excel instance visible
780
+ # @raise ExcelError if workbook cannot be activated
781
+ def activate
782
+ @excel.visible = true
783
+ begin
784
+ Win32API.new("user32","SetForegroundWindow","I","I").call(@excel.hwnd) # Excel 2010
785
+ @ole_workbook.Activate # Excel 2007
786
+ rescue WIN32OLERuntimeError
787
+ raise ExcelError, "cannot activate"
788
+ end
789
+ end
790
+
791
+ # returns true, if the workbook is visible, false otherwise
792
+ def visible
793
+ @excel.Windows(@ole_workbook.Name).Visible
794
+ end
795
+
796
+ # makes a workbook visible or invisible
797
+ # @param [Boolean] visible_value value that determines whether the workbook shall be visible
798
+ def visible= visible_value
799
+ saved = @ole_workbook.Saved
800
+ @excel.Windows(@ole_workbook.Name).Visible = visible_value
801
+ save if saved
802
+ end
803
+
804
+ # returns true, if the workbook reacts to methods, false otherwise
805
+ def alive?
806
+ begin
807
+ @ole_workbook.Name
808
+ true
809
+ rescue
810
+ @ole_workbook = nil # dead object won't be alive again
811
+ #t $!.message
812
+ false
813
+ end
814
+ end
815
+
816
+ # returns the full file name of the workbook
817
+ def filename
818
+ @ole_workbook.Fullname.tr('\\','/') rescue nil
819
+ end
820
+
821
+ def writable # :nodoc: #
822
+ (not @ole_workbook.ReadOnly) if @ole_workbook
823
+ end
824
+
825
+ def saved # :nodoc: #
826
+ @ole_workbook.Saved if @ole_workbook
827
+ end
828
+
829
+ # @return [Boolean] true, if the full book names and excel Instances are identical, false otherwise
830
+ def == other_book
831
+ other_book.is_a?(Book) &&
832
+ @excel == other_book.excel &&
833
+ self.filename == other_book.filename
834
+ end
835
+
836
+ def self.books
837
+ bookstore.books
838
+ end
839
+
784
840
  def self.bookstore # :nodoc: #
785
841
  @@bookstore ||= Bookstore.new
786
842
  end