robust_excel_ole 1.1.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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