robust_excel_ole 1.1.3 → 1.1.4

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.
@@ -0,0 +1,77 @@
1
+ = RobustExcelOle
2
+
3
+ == Saving and closing workbooks
4
+
5
+ === Saving a workbook.
6
+
7
+ Imagine, you have opened a workbook with
8
+
9
+ book = Workbook.open('workbook.xls', :visible => true)
10
+
11
+ and have modified it.
12
+
13
+ You can save the workbook by
14
+
15
+ book.save
16
+
17
+ If you want to save a workbook with a file name, then use
18
+
19
+ book.save_as('new_workbook.xls')
20
+
21
+ The options and respective valid values are the following:
22
+
23
+ +:if_exists+:: +:raise+ (default), +:overwrite+, +:alert+
24
+ +:if_obstruced+:: +:raise (default), +:forget+, +:save+, +close_if_saved
25
+
26
+ The option +:if_exists+ :
27
+
28
+ If a workbook with the file name already exists, then
29
+
30
+ +:raise+:: Raise an exeption. Don't write the file.
31
+ +:overwrite+:: Delete the existing file and write the file. If the workbook is open in an Excel instance, then raise an exception.
32
+ +:alert+:: Give the control to Excel.
33
+
34
+ For example, you want to save a workbook and overwrite the file if it exists before, then use
35
+
36
+ book.save_as('another_workbook.xls', :if_exists => :overwrite)
37
+
38
+ If a workbook blocks the workbook that should be saved, then the former one can be saved and closed before.
39
+
40
+ book = Workbook.open('workbook.xls')
41
+ book2 = Workbook.open('another_workbook.xls')
42
+ book2.save_as('dir/workbook.xls', :if_exists => :overwrite, :if_obstructed => :save)
43
+
44
+ === Closing a workbook.
45
+
46
+ You can close the workbook with the command
47
+
48
+ book.close
49
+
50
+ There is one option: +:if_unsaved+ . It can have one of the following values:
51
+
52
+ +:raise+ (default), +:save+, +:forget+, +:alert+
53
+
54
+ The option specifies: If the workbook is unsaved, then
55
+
56
+ +:save+:: Save the workbook before closing it.
57
+ +:raise+:: Raise an exception. Don't close the workbook.
58
+ +:forget+:: Close the workbook.
59
+ +:alert+:: Give control to Excel.
60
+
61
+ === Opening, saving and closing a workbook under a certain name
62
+
63
+ You can open a workbook with given file name.
64
+
65
+ book = Workbook.open('workbook.xls')
66
+
67
+ You can save a workbook with given file name, if it is open.
68
+
69
+ Workbook.save_as('workbook.xls')
70
+
71
+ The workbook can be saved under a new file name, if it is open.
72
+
73
+ Workbook.save_as('workbook.xls', 'new_workbook.xls')
74
+
75
+ Finally the workbook can be closed with a given filename.
76
+
77
+ Workbook.close('workbook.xls')
data/README_sheet.rdoc ADDED
@@ -0,0 +1,71 @@
1
+ = RobustExcelOle
2
+
3
+ == Accessing and processing worksheets
4
+
5
+ Worksheets are represented by Sheet objects.
6
+
7
+ Assume you have opened a workbook
8
+
9
+ book = Workbook.open('workbook.xls', :visible => true)
10
+
11
+ === Accessing a worksheet.
12
+
13
+ You can access a worksheet by giving the number
14
+
15
+ sheet = book.sheet(1)
16
+
17
+ or its name
18
+
19
+ sheet = book.sheet('Sheet1')
20
+
21
+ You can get the first and last worksheet with
22
+
23
+ sheet = book.first_sheet
24
+
25
+ and
26
+
27
+ sheet = book.last_sheet
28
+
29
+ You can access all sheet objects by using the methods Workbook#each.
30
+
31
+ book.each do |sheet|
32
+ # do something with sheet
33
+ end
34
+
35
+ === Reading and changing the worksheet name
36
+
37
+ You can read and change the worksheet name.
38
+
39
+ sheet1.name
40
+ # => "Sheet1"
41
+
42
+ sheet1.name = "new_sheet"
43
+
44
+ === Adding and copying a worksheet.
45
+
46
+ You can add (append) an empty worksheet using
47
+
48
+ book.add_empty_sheet
49
+
50
+ Additionally you can name it.
51
+
52
+ book.add_empty_sheet(:as => 'sheet_name')
53
+
54
+ You can specify the position of the added empty worksheet.
55
+
56
+ book.add_empty_sheet(:as => 'new_name', :before => another_sheet)
57
+
58
+ You can copy a worksheet and add it.
59
+
60
+ book.copy_sheet sheet
61
+
62
+ Additionally you can specify a name and a position.
63
+
64
+ book.copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
65
+
66
+ If you want to copy a worksheet, if a sheet is given, and add an empty worksheet, if no worksheet is given, then use
67
+
68
+ book.add_or_copy_sheet
69
+
70
+ book.add_or_copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
71
+
@@ -26,13 +26,13 @@ begin
26
26
  Excel.current.visible = true
27
27
  begin
28
28
  book.close(:if_unsaved => :alert) # close the unsaved book.
29
- rescue WorkbookError => msg # user is asked whether the unsaved book shall be saved
29
+ rescue WorkbookREOError => msg # user is asked whether the unsaved book shall be saved
30
30
  puts "#{msg.message}" # if the user chooses to cancel, then an expeption is raised
31
31
  end
32
32
  if new_book then
33
33
  begin
34
34
  new_book.save_as(file_name, :if_exists => :alert) # save the new book, if it was opened
35
- rescue WorkbookError => msg # user is asked, whether the existing file shall be overwritten
35
+ rescue WorkbookREOError => msg # user is asked, whether the existing file shall be overwritten
36
36
  puts "save_as: #{msg.message}" # if the user chooses "no" or "cancel", an exception is raised
37
37
  end
38
38
 
@@ -25,13 +25,8 @@ module RobustExcelOle
25
25
  :check_compatibility => false,
26
26
  :update_links => :never
27
27
  }
28
-
29
- SYNONYMS_OPTS = [[[:default],[:d]], [[:force], [:f]],
30
- [[:default,:excel],[:default_excel]],[[:force,:excel],[:force_excel]],
31
- [[:default,:excel],[:default,:e]], [[:force,:excel],[:force,:e]],
32
- [[:default,:visible],[:default,:v]], [[:force,:visible],[:force,:v]],
33
- [[:force,:visible],[:visible]], [[:force,:visible],[:v]]
34
- ]
28
+
29
+ ABBREVIATIONS = [[:default,:d], [:force, :f], [:excel, :e], [:visible, :v]]
35
30
 
36
31
  class << self
37
32
 
@@ -95,11 +90,14 @@ module RobustExcelOle
95
90
  (not (book.alive? && (not book.saved) && (not options[:if_unsaved] == :accept))))
96
91
  book.options = options
97
92
  book.ensure_excel(options) # unless book.excel.alive?
98
- # if the book is opened as readonly and should be opened as writable, then close it and open the book with the new readonly mode
99
- book.close if (book.alive? && (not book.writable) && (not options[:read_only]))
93
+ # if the ReadOnly status shall be changed, then save, close and reopen it
94
+ book.close(:if_unsaved => :save) if (book.alive? &&
95
+ (((not book.writable) and (not options[:read_only])) or
96
+ (book.writable and options[:read_only])))
100
97
  # reopens the book
101
98
  book.ensure_workbook(file,options) unless book.alive?
102
99
  book.visible = options[:force][:visible] unless options[:force][:visible].nil?
100
+ book.CheckCompatibility = options[:check_compatibility] unless options[:check_compatibility].nil?
103
101
  book.excel.calculation = options[:calculation] unless options[:calculation].nil?
104
102
  return book
105
103
  end
@@ -139,8 +137,9 @@ module RobustExcelOle
139
137
  # @param [Hash] opts the options
140
138
  # @option opts [Symbol] see above
141
139
  # @return [Book] a workbook
142
- def initialize(file_or_workbook, opts={ }, &block)
143
- options = self.class.process_options(opts)
140
+ #def initialize(file_or_workbook, opts={ }, &block)
141
+ def initialize(file_or_workbook, options={ }, &block)
142
+ #options = self.class.process_options(opts)
144
143
  if file_or_workbook.is_a? WIN32OLE
145
144
  workbook = file_or_workbook
146
145
  @ole_workbook = workbook
@@ -169,29 +168,41 @@ module RobustExcelOle
169
168
 
170
169
  private
171
170
 
172
- # merges options with defaults and translates abbreviations and synonyms
173
- def self.process_options(options) # :nodoc: #
171
+ # translates abbreviations and synonyms and merges with default options
172
+ def self.process_options(options, proc_opts = {:use_defaults => true})
174
173
  translator = proc do |opts|
175
- SYNONYMS_OPTS.each do |a|
176
- synonym = a[1][1].nil? ? opts[a[1][0]] : opts[a[1][0]][a[1][1]] unless opts[a[1][0]].nil?
177
- unless synonym.nil?
178
- if a[0][1].nil?
179
- opts[a[0][0]] = synonym if opts[a[0][0]].nil?
180
- else
181
- opts[a[0][0]] = {} if opts[a[0][0]].nil?
182
- opts[a[0][0]][a[0][1]] = synonym if opts[a[0][0]][a[0][1]].nil?
174
+ erg = {}
175
+ opts.each do |key,value|
176
+ new_key = key
177
+ ABBREVIATIONS.each{|long,short| new_key = long if key == short}
178
+ if value.is_a?(Hash)
179
+ erg[new_key] = {}
180
+ value.each do |k,v|
181
+ new_k = k
182
+ ABBREVIATIONS.each{|long,short| new_k = long if k == short}
183
+ erg[new_key][new_k] = v
183
184
  end
184
- end
185
+ else
186
+ erg[new_key] = value
187
+ end
185
188
  end
186
- opts[:default][:excel] = :current if (not opts[:default].nil?) && (opts[:default][:excel] == :reuse || opts[:default][:excel] == :active)
187
- opts[:force][:excel] = :current if (not opts[:force].nil?) && (opts[:force][:excel] == :reuse || opts[:force][:excel] == :active)
188
- opts
189
+ erg[:default] = {} if erg[:default].nil?
190
+ erg[:force] = {} if erg[:force].nil?
191
+ force_list = [:visible, :excel]
192
+ erg.each {|key,value| erg[:force][key] = value if force_list.include?(key)}
193
+ erg[:default][:excel] = erg[:default_excel] unless erg[:default_excel].nil?
194
+ erg[:force][:excel] = erg[:force_excel] unless erg[:force_excel].nil?
195
+ erg[:default][:excel] = :current if (erg[:default][:excel] == :reuse || erg[:default][:excel] == :active)
196
+ erg[:force][:excel] = :current if (erg[:force][:excel] == :reuse || erg[:force][:excel] == :active)
197
+ erg
189
198
  end
190
- default_opts = translator.call(DEFAULT_OPEN_OPTS)
191
- given_opts = translator.call(options)
192
- opts = default_opts.merge(given_opts)
199
+ opts = translator.call(options)
200
+ default_open_opts = proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS :
201
+ {:default => {:excel => :current}, :force => {}, :update_links => :never }
202
+ default_opts = translator.call(default_open_opts)
203
+ opts = default_opts.merge(opts)
193
204
  opts[:default] = default_opts[:default].merge(opts[:default]) unless opts[:default].nil?
194
- opts[:force] = default_opts[:force].merge(opts[:force]) unless opts[:force].nil?
205
+ opts[:force] = default_opts[:force].merge(opts[:force]) unless opts[:force].nil?
195
206
  opts
196
207
  end
197
208
 
@@ -210,7 +221,7 @@ module RobustExcelOle
210
221
  begin
211
222
  object.excel
212
223
  rescue
213
- raise TypeErrorREO, "given object is neither an Excel, a Workbook, nor a Win32ole"
224
+ raise TypeREOError, "given object is neither an Excel, a Workbook, nor a Win32ole"
214
225
  end
215
226
  end
216
227
  end
@@ -333,7 +344,7 @@ module RobustExcelOle
333
344
  begin
334
345
  workbooks = @excel.Workbooks
335
346
  rescue WIN32OLERuntimeError => msg
336
- raise UnexpectedError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
347
+ raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
337
348
  end
338
349
  begin
339
350
  with_workaround_linked_workbooks_excel2007(options) do
@@ -343,14 +354,14 @@ module RobustExcelOle
343
354
  rescue WIN32OLERuntimeError => msg
344
355
  # for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
345
356
  # if yes: distinguish these events
346
- raise UnexpectedError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
357
+ raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
347
358
  end
348
359
  begin
349
360
  # workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
350
361
  begin
351
362
  @ole_workbook = workbooks.Item(File.basename(filename))
352
363
  rescue WIN32OLERuntimeError => msg
353
- raise UnexpectedError, "WIN32OLERuntimeError: #{msg.message}"
364
+ raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
354
365
  end
355
366
  if options[:force][:visible].nil? && (not options[:default][:visible].nil?)
356
367
  if @excel.created
@@ -365,7 +376,7 @@ module RobustExcelOle
365
376
  @excel.calculation = options[:calculation] unless options[:calculation].nil?
366
377
  self.Saved = true # unless self.Saved # ToDo: this is too hard
367
378
  rescue WIN32OLERuntimeError => msg
368
- raise UnexpectedError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
379
+ raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
369
380
  end
370
381
  end
371
382
  end
@@ -490,7 +501,6 @@ module RobustExcelOle
490
501
  # @return [Book] a workbook
491
502
 
492
503
  # state = [:open, :saved, :writable, :visible, :calculation, :check_compatibility]
493
-
494
504
  def self.unobtrusively(file, opts = { }, &block)
495
505
  options = {:if_closed => :current,
496
506
  :read_only => false,
@@ -540,14 +550,15 @@ module RobustExcelOle
540
550
  end
541
551
 
542
552
  # reopens a closed workbook
543
- def reopen
544
- self.class.open(self.stored_filename)
553
+ # @options options
554
+ def reopen(options = { })
555
+ self.class.open(@stored_filename, options)
545
556
  end
546
557
 
547
558
  # simple save of a workbook.
548
- # @option opts [Boolean] states, whether colored ranges shall be discolored
559
+ # @option opts [Boolean] :discoloring states, whether colored ranges shall be discolored
549
560
  # @return [Boolean] true, if successfully saved, nil otherwise
550
- def save(opts = {:discoloring => false})
561
+ def save(opts = {:discoloring => false})
551
562
  raise ObjectNotAlive, "workbook is not alive" if (not alive?)
552
563
  raise WorkbookReadOnly, "Not opened for writing (opened with :read_only option)" if @ole_workbook.ReadOnly
553
564
  begin
@@ -558,7 +569,7 @@ module RobustExcelOle
558
569
  if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
559
570
  raise WorkbookNotSaved, "workbook not saved"
560
571
  else
561
- raise UnexpectedError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
572
+ raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
562
573
  end
563
574
  end
564
575
  true
@@ -580,7 +591,7 @@ module RobustExcelOle
580
591
  # :save -> saves the blocking workbook and closes it
581
592
  # :close_if_saved -> closes the blocking workbook, if it is saved,
582
593
  # otherwise raises an exception
583
- # :discoloring states, wheter colored ranges shall be discolored
594
+ # :discoloring states, whether colored ranges shall be discolored
584
595
  # @return [Book], the book itself, if successfully saved, raises an exception otherwise
585
596
  def save_as(file, opts = { } )
586
597
  raise FileNameNotGiven, "filename is nil" if file.nil?
@@ -657,9 +668,9 @@ module RobustExcelOle
657
668
  rescue WIN32OLERuntimeError => msg
658
669
  if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
659
670
  # trace "save: canceled by user" if options[:if_exists] == :alert || options[:if_exists] == :excel
660
- # another possible semantics. raise WorkbookError, "could not save Workbook"
671
+ # another possible semantics. raise WorkbookREOError, "could not save Workbook"
661
672
  else
662
- raise UnexpectedError, "unknown WIN32OELERuntimeError:\n#{msg.message}"
673
+ raise UnexpectedREOError, "unknown WIN32OELERuntimeError:\n#{msg.message}"
663
674
  end
664
675
  end
665
676
  end
@@ -667,6 +678,7 @@ module RobustExcelOle
667
678
  public
668
679
 
669
680
  # closes a given file if it is open
681
+ # @options opts [Symbol] :if_unsaved
670
682
  def self.close(file, opts = {:if_unsaved => :raise})
671
683
  book = bookstore.fetch(file) rescue nil
672
684
  book.close(opts) if book && book.alive?
@@ -799,10 +811,34 @@ module RobustExcelOle
799
811
  begin
800
812
  item.Name = new_name
801
813
  rescue WIN32OLERuntimeError
802
- raise UnexpectedError, "name error in #{File.basename(self.stored_filename).inspect}"
814
+ raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
803
815
  end
804
816
  end
805
817
 
818
+ # sets options
819
+ # @param [Hash] opts
820
+ def for_this_workbook(opts)
821
+ return unless alive?
822
+ opts = self.class.process_options(opts, :use_defaults => false)
823
+ visible_before = visible
824
+ check_compatibility_before = check_compatibility
825
+ unless opts[:read_only].nil?
826
+ # if the ReadOnly status shall be changed, then close and reopen it
827
+ if ((not writable) and (not opts[:read_only])) or (writable and opts[:read_only])
828
+ #opts[:check_compatibility] = opts[:check_compatibility].nil? ? check_compatibility :
829
+ # DEFAULT_OPEN_OPTS[:check_compatibility]
830
+ #opts[:check_compatibility] = opts[:check_compatibility].nil? ? check_compatibility :
831
+ # opts[:check_compatibility]
832
+ opts[:check_compatibility] = check_compatibility if opts[:check_compatibility].nil?
833
+ close(:if_unsaved => true)
834
+ open_or_create_workbook(@stored_filename, opts)
835
+ end
836
+ end
837
+ self.visible = opts[:force][:visible].nil? ? visible_before : opts[:force][:visible]
838
+ self.CheckCompatibility = opts[:check_compatibility].nil? ? check_compatibility_before : opts[:check_compatibility]
839
+ @excel.calculation = opts[:calculation] unless opts[:calculation].nil?
840
+ end
841
+
806
842
  # brings workbook to foreground, makes it available for heyboard inputs, makes the Excel instance visible
807
843
  def focus
808
844
  self.visible = true
@@ -133,10 +133,6 @@ module RobustExcelOle
133
133
  self
134
134
  end
135
135
 
136
- def set_options(options)
137
- self.class.new(@ole_excel, options)
138
- end
139
-
140
136
  private
141
137
 
142
138
  # returns a Win32OLE object that represents a Excel instance to which Excel connects
@@ -217,9 +213,9 @@ module RobustExcelOle
217
213
  @ole_excel.Workbooks.Close
218
214
  rescue WIN32OLERuntimeError => msg
219
215
  if msg.message =~ /800A03EC/
220
- raise ExcelError, "user canceled or runtime error"
216
+ raise ExcelREOError, "user canceled or runtime error"
221
217
  else
222
- raise UnexpectedError, "unknown WIN32OLERuntimeError: #{msg.message}"
218
+ raise UnexpectedREOError, "unknown WIN32OLERuntimeError: #{msg.message}"
223
219
  end
224
220
  end
225
221
  weak_wkbks = nil
@@ -490,7 +486,7 @@ module RobustExcelOle
490
486
  raise FileNotFound, "could not save workbook with filename #{file_name.inspect}"
491
487
  else
492
488
  # todo some time: find out when this occurs :
493
- raise UnexpectedError, "unknown WIN32OLERuntimeError with filename #{file_name.inspect}: \n#{msg.message}"
489
+ raise UnexpectedREOError, "unknown WIN32OLERuntimeError with filename #{file_name.inspect}: \n#{msg.message}"
494
490
  end
495
491
  end
496
492
  end
@@ -568,6 +564,32 @@ module RobustExcelOle
568
564
  end
569
565
  end
570
566
 
567
+ # set options in this Excel instance
568
+ def for_this_instance(options)
569
+ self.class.new(@ole_excel, options)
570
+ end
571
+
572
+ def set_options(options)
573
+ for_this_instance(options)
574
+ end
575
+
576
+ # set options in all workbooks
577
+ def for_all_workbooks(options)
578
+ ole_workbooks = begin
579
+ @ole_excel.Workbooks
580
+ rescue WIN32OLERuntimeError => msg
581
+ if msg.message =~ /failed to get Dispatch Interface/
582
+ raise ExcelDamaged, "Excel instance not alive or damaged"
583
+ else
584
+ raise ExcelREOError, "workbooks could not be determined"
585
+ end
586
+ end
587
+ ole_workbooks.each do |ole_workbook|
588
+ book_class.open(ole_workbook).for_this_workbook(options)
589
+ end
590
+ end
591
+
592
+ =begin
571
593
  # make all workbooks visible or invisible
572
594
  def workbooks_visible= visible_value
573
595
  begin
@@ -580,6 +602,7 @@ module RobustExcelOle
580
602
  raise ExcelDamaged, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
581
603
  end
582
604
  end
605
+ =end
583
606
 
584
607
  def focus
585
608
  self.visible = true