robust_excel_ole 1.8 → 1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2bdbfacd559753a2e1126c1ff1a28520a6c6cd57
4
- data.tar.gz: a5cdf8708f6862cda1630fc9152f65a498e446b0
2
+ SHA256:
3
+ metadata.gz: 3cf45dfa8d36893f06b8196d99990c94b76056fdfd8b98e2a1cff18f11dedec5
4
+ data.tar.gz: 2fb8ee7deb7bb700c9a7557340933b0a60a6ee38a8eaff9ed2c2f4b4669cadc0
5
5
  SHA512:
6
- metadata.gz: 830ff59640559e837e7d15daf2ee5ec18c734aaebaba3a56189ea837f755deb8d4e7f7bf06aa01365ea59f633837ef4faa27bf1951a6af0f7ba834c083fd0e79
7
- data.tar.gz: 4aea6e53ba40c1c14a26ce2bef43591e0a39194d65a1b90b4acc6623206bfad9796b8a69351d35aa7f99bca2201f8b23eac55a951f0d23b42a34f9f93fd91b03
6
+ metadata.gz: 6b8c23bf581b8ec11b9e510bf0bf805e2309a089a307c01bdcaf4aead8dc1aa31becd972e94beedb5e7ac82183ac4f84bd0c5ea6883a4a15baa2d2f95fd13dba
7
+ data.tar.gz: 6958f89f6a6204f359a2f16469ca602a33b1572588b31944a073ffbc48936a9e9412edd4b742bb57ecff4e3b96756e99258924f60d5d2b7aaf8fefabafc0c697
data/Changelog CHANGED
@@ -1,6 +1,14 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [1.9]
5
+
6
+ ### Added
7
+ - Workbook#create
8
+ - Excel#workbooks, each_workbook, each_workbook_with_index
9
+ - Address#int_range,#r1c1,#a1
10
+ - ReoCommon::RangeOwners#add_name: allowing infinite ranges
11
+
4
12
  ## [1.6]
5
13
 
6
14
  ### Added
@@ -28,13 +28,17 @@ RobustExcelOle can be used either for scripts
28
28
  require 'robust_excel_ole'
29
29
  include RobustExcelOle
30
30
 
31
- or as a console
31
+ or as a console. If you want to use RobustExcelOle as a console, you go (using 'cd') into the gem path that you find with help of
32
+
33
+ gem env
34
+
35
+ and go into the path of the gem 'robust_excel_ole'. There you start the console via the command
32
36
 
33
37
  reo
34
38
 
35
39
  The call of the console will include RobustExcelOle for you.
36
40
 
37
- The following examples can be used for both scripts and console. If you are in the path of the gem and start the console, you can just put these examples.
41
+ The following examples can be used for both scripts and console. If you have started the console in the gem path, you can just put these examples.
38
42
 
39
43
  == Description
40
44
 
@@ -100,7 +104,11 @@ RobustExcelOle allows unobtrusively reading and modifying workbooks, i.e. access
100
104
  # do something
101
105
  end
102
106
 
103
- === More features when opening, modifying, saving and closing workbooks
107
+ You can also create a new, empty workbook.
108
+
109
+ Workbook.create('spec/data/new_workbook.xls', :visible => true)
110
+
111
+ === More features when opening, modifying, creating saving and closing workbooks
104
112
 
105
113
  We can open the workbook using a block, similar to, e.g., +File.open+.
106
114
 
@@ -174,6 +182,12 @@ For hard terminating all Excel processes we can use
174
182
 
175
183
  For more details about creating Excel instances see README_excel[https://github.com/Thomas008/robust_excel_ole/blob/master/README/README_excel_rdoc]
176
184
 
185
+ === Generating a new workbook
186
+
187
+ You can creata a new, empty workbook by
188
+
189
+ Excel.generate_workbook('spec/data/new_workbook.xls')
190
+
177
191
  === Opening workbooks in several Excel instances
178
192
 
179
193
  RobustExcelOle enables opening and processing workbooks in several Excel instances. Using more than one Excel process allows, e.g., running a script that operates in one Excel instance, while a user (or another script) modifies workbooks in another Excel instance.
@@ -101,6 +101,29 @@ You can set options for all workbooks of an Excel instance.
101
101
 
102
102
  excel.for_all_workooks(:visible => true, :read_only => true)
103
103
 
104
+
105
+ === Showing and traversing through all workbooks
106
+
107
+ You can yield an array of all Workbook objects of an Excel instance.
108
+
109
+ excel.workbooks
110
+
111
+ You can access all Workbook objects by using the methods Excel#each_workbook and Excel#each_workbook_with_index. Here are some examples:
112
+
113
+ excel.each_workook {|w| puts w.filename}
114
+
115
+ and
116
+
117
+ excel.each_workbook.with_index {|w,i| puts w,i }
118
+
119
+ With help of these methods you can also supply options, e.g. without a block,
120
+
121
+ excel.each_workbook(:visible => true)
122
+
123
+ or, with a block,
124
+
125
+ excel.each_workbook(:visible => true) {|w| puts w}
126
+
104
127
  === Bringing an Excel instance to the foreground
105
128
 
106
129
  excel1.focus
@@ -142,15 +142,21 @@ For example, you can access a range consisting of one cell by providing the row
142
142
 
143
143
  range = sheet.range([1,1])
144
144
 
145
- Using the A1-format you write
145
+ Using the A1-format and R1C1-format you write
146
146
 
147
147
  range = sheet.range("A1")
148
148
 
149
+ and
150
+
151
+ range = sheet.range("Z1S1")
152
+
153
+ respectively.
154
+
149
155
  You can access a rectangular range by providing the row and column of the top left cell and the row and column of the bottum right cell.
150
156
 
151
157
  range = sheet.range([1..3,1..4])
152
158
 
153
- or using the a1-format
159
+ or using the A1-format.
154
160
 
155
161
  range = sheet.range([1..3,"A".."D"])
156
162
 
@@ -158,6 +164,28 @@ or
158
164
 
159
165
  range = sheet.range("A1:D3")
160
166
 
167
+ or using the R1C1-format
168
+
169
+ range = sheet.range("Z1S1:Z3S4")
170
+
171
+ Infinite ranges are defined, e.g., by setting the rows or columns to +nil+
172
+
173
+ range = sheet.range([1..3,nil])
174
+ range = sheet.range([nil,"A".."B"])
175
+
176
+ Using the A1-format you write
177
+
178
+ range = sheet.range("1:3")
179
+ range = sheet.range("A:B")
180
+
181
+ You can also apply relative references by using brackets, e.g.
182
+
183
+ range = sheet.range("Z[1]S1:Z3S[4]")
184
+
185
+ or
186
+
187
+ range = sheet.range([[1]..3,2..[4]])
188
+
161
189
  You get the values of the range as flat array with help of
162
190
 
163
191
  range.values
@@ -194,13 +222,53 @@ Moreover, you can state, whether you want to copy the values only, and whether y
194
222
 
195
223
  Note that when you don't copy the values only but all formating as well, and you either copy into another Excel instance or transpose the range, the clipboard is being used.
196
224
 
197
- === Naming a cell
225
+ === Naming a range
226
+
227
+ You can (re-) define a name referring to a range by stating its name, and the address. The address is given by integer-range-format, r1c1-format or a1-format. For example, you can define a name for a rectangular range by
198
228
 
199
- You can (re-) define a name referring to a cell with help of VBA methods by stating its name, and the row and the column of the cell.
229
+ book.add_name("name",[1..2,3..5])
230
+
231
+ or
232
+
233
+ book.add_name("name","Z1S3:Z2S5")
234
+
235
+ or
236
+
237
+ book.add_name("name", "C1:E2")
238
+
239
+ Similarly you can define a name referring to a cell
200
240
 
201
241
  book.add_name("name",[1,1])
202
242
 
203
- Most methods can be done for Workbook, Worksheet, and Excel objects.
243
+ or
244
+
245
+ book.add_name("name","Z1S1")
246
+
247
+ or
248
+
249
+ book.add_name("name","A1")
250
+
251
+ and infinite ranges
252
+
253
+ book.add_name("name",[1..2,nil])
254
+
255
+ or
256
+
257
+ book.add_name("name", "Z1:Z2")
258
+
259
+ or
260
+
261
+ book.add_name("name","1:2")
262
+
263
+ Furthermore, you can define a name using relative references with the r1c1-format.
264
+
265
+ book.add_name("name", "Z1S[1]:Z[2]S4")
266
+
267
+ or
268
+
269
+ book.add_name("name", [1..[2],[1]..4])
270
+
271
+ You can do the same for an worksheet or Excel object.
204
272
 
205
273
  === Reading and writing the contents of a named range in a workbook.
206
274
 
@@ -0,0 +1,57 @@
1
+ # example_naming.rb:
2
+ # each cell is named with the name equaling its value unless it is empty or not a string
3
+ # the contents of each cell is copied
4
+ # the new workbook's name is extended by the suffix "_named"
5
+
6
+ require File.expand_path('../../lib/robust_excel_ole', File.dirname(__FILE__))
7
+ require File.join(File.dirname(File.expand_path(__FILE__)), '../../spec/helpers/create_temporary_dir')
8
+ require "fileutils"
9
+
10
+ include RobustExcelOle
11
+
12
+ begin
13
+ @id2exl = ["house", "tree", "cat", "mouse", "elephant", "yes", "no"]
14
+ column_ids = [2,4,6]
15
+ dir = File.expand_path('../../spec/data', File.dirname(__FILE__))
16
+ workbook_name = 'workbook.xls'
17
+ filename = dir + "/" + workbook_name
18
+ puts "filename: #{filename}"
19
+
20
+ Excel.close_all if_unsaved: :forget
21
+ book = Workbook.new filename, if_absent: :create, visible: true, if_unsaved: :accept
22
+ sheet = book.sheet(1)
23
+ puts "book: #{book}"
24
+ puts "sheet: #{sheet}"
25
+
26
+ # book.Names.Add("µ", RefersToR1C1Local:"=Z")
27
+ # book.Names.Add("µ_1", RefersToR1C1Local:"=Z(-1)")
28
+
29
+ def define_columns sheet, columns_ids
30
+ puts "define_columns:"
31
+ first_column = sheet.range("A")
32
+ puts "first_column: #{first_column}"
33
+
34
+ columns_ids.each_with_index do |id,idx|
35
+ puts "id: #{id}"
36
+ puts "idx: #{idx}"
37
+
38
+ nam = @id2exl[id]
39
+ puts "nam: #{nam}"
40
+ colnr = idx+1
41
+ puts "colnr: #{colnr}"
42
+ sheet[1,colnr] = nam
43
+ puts "sheet[1,colnr]: #{sheet[1,colnr].Value}"
44
+ sheet.add_name(nam,[nil,colnr])
45
+ puts "sheet.Range().Address: #{sheet.Range(nam).Address}"
46
+ #sheet.add_name(nam,"A")
47
+ # blatt.Names.Add(Name: nam, RefersTo: "="+erste_spalte.Offset(0,idx).Address)
48
+ end
49
+ end
50
+
51
+ define_columns sheet, column_ids
52
+
53
+
54
+ # book.save
55
+
56
+ end
57
+
@@ -57,7 +57,7 @@ begin
57
57
  begin
58
58
  @book.add_sheet(sheet, :as => 'second_sheet_copy')
59
59
  rescue NameAlreadyExists => msg
60
- puts "error: add_sheet: #{msg.message}"
60
+ puts "results in an error: add_sheet: #{msg.message}"
61
61
  end
62
62
 
63
63
  @book.close(:if_unsaved => :forget) # close the book without saving it
@@ -32,5 +32,5 @@ begin
32
32
  new_book.close # close the books
33
33
  ensure
34
34
  Excel.kill_all # close all workbooks, quit Excel application
35
- rm_tmp(dir)
35
+ #rm_tmp(dir)
36
36
  end
@@ -1,4 +1,4 @@
1
- # example_ifunsaved_forget.rb:
1
+ # example_if_unsaved_forget.rb:
2
2
  # open with :if_unsaved => :forget, :new_excel, close with :if_unsaved => :save
3
3
 
4
4
  require File.expand_path('../../lib/robust_excel_ole', File.dirname(__FILE__))
@@ -13,7 +13,7 @@ begin
13
13
  file_name2 = dir + 'different_workbook.xls'
14
14
  file_name3 = dir + 'different_workbook.xls'
15
15
  file_name4 = dir + 'book_with_blank.xls'
16
- book1 = Workbook.open(file_name1) # open a book in a new Excel instance since no Excel is open
16
+ book1 = Workbook.open(file_name1) # open a book in a new Excel instance since no Excel is open
17
17
  book1.excel.visible = true # make current Excel visible
18
18
  sleep 2
19
19
  book2 = Workbook.open(file_name2) # open a new book in the same Excel instance
@@ -1,5 +1,7 @@
1
1
  require 'win32ole'
2
2
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/reo_common')
3
+ require File.join(File.dirname(__FILE__), 'robust_excel_ole/range_owners')
4
+ require File.join(File.dirname(__FILE__), 'robust_excel_ole/address')
3
5
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/general')
4
6
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/excel')
5
7
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/bookstore')
@@ -0,0 +1,114 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module RobustExcelOle
4
+
5
+ class Address < REOCommon
6
+
7
+ def self.new(r1c1_letters)
8
+ @@row_letter = r1c1_letters[0..0]
9
+ @@col_letter = r1c1_letters[1..1]
10
+ end
11
+
12
+ # address formats taht are valid:
13
+ # r1c1-format: e.g. "Z3S1", "Z3S1:Z5S2", "Z[3]S1", "Z3S[-1]:Z[5]S1", "Z[3]", "S[-2]"
14
+ # infinite ranges are not possible, e.g. "Z3:Z5", "S2:S5", "Z2", "S3", "Z[2]"
15
+ # int_range: e.g. [3,1], [3,"A"], [3..5,1..2], [3..5, "A".."B"],
16
+ # [3..4, nil], [nil, 2..4], [2,nil], [nil,4]
17
+ # a1-format: e.g. "A3", "A3:B5", "A:B", "3:5", "A", "3"
18
+
19
+ def self.r1c1(address)
20
+ transform_address(address,:r1c1)
21
+ end
22
+
23
+ def self.a1(address)
24
+ transform_address(address,:a1)
25
+ end
26
+
27
+ # valid address formats: e.g. [3,1], [3,"A"], [3..5,1..2], [3..5, "A".."B"],
28
+ # [3..4, nil], [nil, 2..4], [2,nil], [nil,4]
29
+ def self.int_range(address)
30
+ transform_address(address,:int_range)
31
+ end
32
+
33
+ private
34
+
35
+ def self.transform_address(address, format)
36
+ address = address.is_a?(Array) ? address : [address]
37
+ raise AddressInvalid, "address #{address.inspect} has more than two components" if address.size > 2
38
+ begin
39
+ if address.size == 1
40
+ comp1, comp2 = address[0].split(':')
41
+ a1_expr = /^(([A-Z]+[0-9]+)|([A-Z]+$)|([0-9]+))$/
42
+ is_a1 = comp1 =~ a1_expr && (comp2.nil? || comp2 =~ a1_expr)
43
+ r1c1_expr = /^(([A-Z]\[?-?[0-9]+\]?[A-Z]\[?-?[0-9]+\]?)|([A-Z]\[?-?[0-9]+\]?)|([A-Z]\[?-?[0-9]+\]?))$/
44
+ is_r1c1 = comp1 =~ r1c1_expr && (comp2.nil? || comp2 =~ r1c1_expr) && (not is_a1)
45
+ raise AddressInvalid, "address #{address.inspect} not in A1- or r1c1-format" unless (is_a1 || is_r1c1)
46
+ return address[0].gsub('[','(').gsub(']',')') if (is_a1 && format==:a1) || (is_r1c1 && format==:r1c1)
47
+ given_format = (is_a1) ? :a1 : :r1c1
48
+ row_comp1, col_comp1 = analyze(comp1,given_format)
49
+ row_comp2, col_comp2 = analyze(comp2,given_format) unless comp2.nil?
50
+ address_comp1 = comp2 && (not row_comp1.nil?) ? (row_comp1 .. row_comp2) : row_comp1
51
+ address_comp2 = comp2 && (not col_comp1.nil?) ? (col_comp1 .. col_comp2) : col_comp1
52
+ else
53
+ address_comp1, address_comp2 = address
54
+ end
55
+ address_comp1 = address_comp1..address_comp1 if (address_comp1.is_a?(Integer) || address_comp1.is_a?(String) || address_comp1.is_a?(Array))
56
+ address_comp2 = address_comp2..address_comp2 if (address_comp2.is_a?(Integer) || address_comp2.is_a?(String) || address_comp2.is_a?(Array))
57
+ #raise unless address_comp1.nil? || address_comp1.begin.to_i!=0 || address_comp1.begin.empty?
58
+ rows = unless address_comp1.nil? || address_comp1.begin == 0 # || address_comp1.begin.to_i==0
59
+ address_comp1.begin..address_comp1.end
60
+ end
61
+ columns = unless address_comp2.nil?
62
+ if address_comp2.begin.is_a?(String) #address_comp2.begin.to_i == 0
63
+ col_range = str2num(address_comp2.begin)..str2num(address_comp2.end)
64
+ col_range==(0..0) ? nil : col_range
65
+ else
66
+ address_comp2.begin..address_comp2.end
67
+ end
68
+ end
69
+ rescue
70
+ raise AddressInvalid, "address (#{address.inspect}) format not correct"
71
+ end
72
+ if format==:r1c1
73
+ r1c1_string(@@row_letter,rows,:min) + r1c1_string(@@col_letter,columns,:min) + ":" +
74
+ r1c1_string(@@row_letter,rows,:max) + r1c1_string(@@col_letter,columns,:max)
75
+ elsif format==:int_range
76
+ [rows,columns]
77
+ else
78
+ raise NotImplementedREOError, "not implemented"
79
+ end
80
+ end
81
+
82
+ # @private
83
+ def self.r1c1_string(letter,int_range,type)
84
+ return "" if int_range.nil? || int_range.begin.nil?
85
+ parameter = type == :min ? int_range.begin : int_range.end
86
+ is_relative = parameter.is_a?(Array)
87
+ parameter = parameter.first if is_relative
88
+ letter + (is_relative ? "(" : "") + parameter.to_s + (is_relative ? ")" : "")
89
+ end
90
+
91
+ # @private
92
+ def self.analyze(comp,format)
93
+ row_comp, col_comp = if format==:a1
94
+ [comp.gsub(/[A-Z]/,''), comp.gsub(/[0-9]/,'')]
95
+ else
96
+ a,b = comp.split(@@row_letter)
97
+ c,d = b.split(@@col_letter)
98
+ b.nil? ? ["",b] : (d.nil? ? [c,""] : [c,d])
99
+ end
100
+ def self.s2n(s)
101
+ s!="" ? (s[0] == "[" ? [s.gsub(/\[|\]/,'').to_i] : (s.to_i!=0 ? s.to_i : s)) : nil
102
+ end
103
+ [s2n(row_comp), s2n(col_comp)]
104
+ end
105
+
106
+
107
+ # @private
108
+ def self.str2num(str)
109
+ str.tr("A-Z","0-9A-P").to_i(26) + (26**str.size-1)/25
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -135,7 +135,7 @@ module RobustExcelOle
135
135
  self
136
136
  end
137
137
 
138
- private
138
+ private
139
139
 
140
140
  # returns a Win32OLE object that represents a Excel instance to which Excel connects
141
141
  # connects to the first opened Excel instance
@@ -162,23 +162,32 @@ module RobustExcelOle
162
162
  result
163
163
  end
164
164
 
165
- public
166
-
167
165
  # retain the saved status of all workbooks
166
+ # @private
168
167
  def retain_saved_workbooks
169
- saved = []
170
- @ole_excel.Workbooks.each { |w| saved << w.Saved }
168
+ saved_stati = @ole_excel.Workbooks.map { |w| w.Saved }
171
169
  begin
172
170
  yield self
173
171
  ensure
174
- i = 0
175
- @ole_excel.Workbooks.each do |w|
176
- w.Saved = saved[i] if saved[i]
177
- i += 1
172
+ @ole_excel.Workbooks.zip(saved_stati) { |w,s| w.Saved = s }
173
+ end
174
+ end
175
+
176
+ # @private
177
+ def ole_workbooks
178
+ ole_workbooks = begin
179
+ @ole_excel.Workbooks
180
+ rescue WIN32OLERuntimeError => msg
181
+ if msg.message =~ /failed to get Dispatch Interface/
182
+ raise ExcelDamaged, 'Excel instance not alive or damaged'
183
+ else
184
+ raise ExcelREOError, 'workbooks could not be determined'
178
185
  end
179
186
  end
180
187
  end
181
188
 
189
+ public
190
+
182
191
  def self.contains_unsaved_workbooks?
183
192
  !Excel.current.unsaved_workbooks.empty?
184
193
  end
@@ -198,8 +207,8 @@ module RobustExcelOle
198
207
  # @option options [Symbol] :if_unsaved :raise, :save, :forget, :alert, Proc
199
208
  # :if_unsaved if unsaved workbooks are open in an Excel instance
200
209
  # :raise (default) -> raises an exception
201
- # :save -> saves the workbooks before closing
202
210
  # :forget -> closes the Excel instance without saving the workbooks
211
+ # :save -> saves the workbooks before closing
203
212
  # :alert -> let Excel do it
204
213
  def close_workbooks(options = { :if_unsaved => :raise })
205
214
  return unless alive?
@@ -210,7 +219,9 @@ module RobustExcelOle
210
219
  when Proc
211
220
  options[:if_unsaved].call(self, unsaved_workbooks)
212
221
  when :raise
213
- raise UnsavedWorkbooks, 'Excel contains unsaved workbooks'
222
+ raise UnsavedWorkbooks, "Excel contains unsaved workbooks" +
223
+ "\nHint: Use option :if_unsaved with values :forget and :save to close the
224
+ Excel instance without or with saving the unsaved workbooks before, respectively"
214
225
  when :alert
215
226
  # nothing
216
227
  when :forget
@@ -218,7 +229,8 @@ module RobustExcelOle
218
229
  when :save
219
230
  unsaved_workbooks.each { |m| m.Save }
220
231
  else
221
- raise OptionInvalid, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}"
232
+ raise OptionInvalid, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}" +
233
+ "\nHint: Valid values are :raise, :forget, :save and :alert"
222
234
  end
223
235
  end
224
236
  begin
@@ -501,27 +513,9 @@ module RobustExcelOle
501
513
  self.Workbooks.each { |w| trace "#{w.Name} #{w}" }
502
514
  end
503
515
 
504
- # generates, saves, and closes empty workbook
505
516
  # @private
506
- def generate_workbook file_name
507
- raise FileNameNotGiven, 'filename is nil' if file_name.nil?
508
-
509
- self.Workbooks.Add
510
- empty_workbook = self.Workbooks.Item(self.Workbooks.Count)
511
- filename = General.absolute_path(file_name).tr('/','\\')
512
- unless File.exist?(filename)
513
- begin
514
- empty_workbook.SaveAs(filename)
515
- rescue WIN32OLERuntimeError => msg
516
- # if msg.message =~ /SaveAs/ and msg.message =~ /Workbook/ then
517
- raise FileNotFound, "could not save workbook with filename #{file_name.inspect}"
518
- # else
519
- # # todo some time: find out when this occurs :
520
- # raise UnexpectedREOError, "unknown WIN32OLERuntimeError with filename #{file_name.inspect}: \n#{msg.message}"
521
- # end
522
- end
523
- end
524
- empty_workbook
517
+ def generate_workbook file_name # :deprecated: #
518
+ workbook_class.open(file_name, :if_absent => :create, :force => {:excel => self})
525
519
  end
526
520
 
527
521
  # sets DisplayAlerts in a block
@@ -559,16 +553,36 @@ module RobustExcelOle
559
553
  @calculation = calculation_mode
560
554
  calc_mode_changable = @ole_excel.Workbooks.Count > 0 && @ole_excel.Calculation.is_a?(Integer)
561
555
  if calc_mode_changable
562
- #if calculation_mode == :manual
556
+ retain_saved_workbooks do
557
+ begin
558
+ best_wb_to_make_visible = @ole_excel.Workbooks.sort_by {|wb|
559
+ score =
560
+ (wb.Saved ? 0 : 40) + # an unsaved workbooks is most likely the main workbook
561
+ (wb.ReadOnly ? 0 : 20) + # the main wb is usually writable
562
+ case wb.Name.split(".").last.downcase
563
+ when "xlsm" then 10 # the main workbook is more likely to have macros
564
+ when "xls" then 8
565
+ when "xlsx" then 4
566
+ when "xlam" then -2 # libraries are not normally the main workbook
567
+ else 0
568
+ end
569
+ score
570
+ }.last
571
+ best_wb_to_make_visible.Windows(1).Visible = true
572
+ rescue => e
573
+ trace "error setting calculation=#{calculation_mode} msg: " + e.message
574
+ trace e.backtrace
575
+ # continue on errors here, failing would usually disrupt too much
576
+ end
563
577
  saved = []
564
- (1..@ole_excel.Workbooks.Count).each { |i| saved << @ole_excel.Workbooks(i).Saved }
565
- #end
566
- @ole_excel.CalculateBeforeSave = false
567
- @ole_excel.Calculation =
568
- calculation_mode == :automatic ? XlCalculationAutomatic : XlCalculationManual
569
- #if calculation_mode == :manual
570
- (1..@ole_excel.Workbooks.Count).each { |i| @ole_excel.Workbooks(i).Saved = true if saved[i - 1] }
571
- #end
578
+ @ole_excel.Workbooks.each { |w| saved << w.Saved }
579
+ @ole_excel.CalculateBeforeSave = false
580
+ @ole_excel.Calculation =
581
+ calculation_mode == :automatic ? XlCalculationAutomatic : XlCalculationManual
582
+ saved = []
583
+ @ole_excel.Workbooks.each { |w| saved << w.Saved }
584
+ end
585
+ #(1..@ole_excel.Workbooks.Count).each { |i| @ole_excel.Workbooks(i).Saved = true if saved[i - 1] }
572
586
  end
573
587
  end
574
588
 
@@ -603,21 +617,36 @@ module RobustExcelOle
603
617
 
604
618
  def set_options(options)
605
619
  for_this_instance(options)
606
- end
620
+ end
607
621
 
608
622
  # set options in all workbooks
609
623
  def for_all_workbooks(options)
610
- ole_workbooks = begin
611
- @ole_excel.Workbooks
612
- rescue WIN32OLERuntimeError => msg
613
- if msg.message =~ /failed to get Dispatch Interface/
614
- raise ExcelDamaged, 'Excel instance not alive or damaged'
615
- else
616
- raise ExcelREOError, 'workbooks could not be determined'
617
- end
624
+ each_workbook(options)
625
+ end
626
+
627
+ def workbooks
628
+ #ole_workbooks.map {|ole_workbook| ole_workbook.to_reo }
629
+ ole_workbooks.map {|ole_workbook| workbook_class.new(ole_workbook) }
630
+ end
631
+
632
+ # traverses over all workbooks and sets options if provided
633
+ def each_workbook(opts = { })
634
+ ole_workbooks.each do |ow|
635
+ wb = workbook_class.new(ow, opts)
636
+ block_given? ? (yield wb) : wb
637
+ #if block_given?
638
+ # yield workbook_class.new(ow, opts)
639
+ #else
640
+ # workbook_class.new(ow, opts)
641
+ #end
618
642
  end
619
- ole_workbooks.each do |ole_workbook|
620
- workbook_class.open(ole_workbook).for_this_workbook(options)
643
+ end
644
+
645
+ def each_workbook_with_index(opts = { }, offset = 0)
646
+ i = offset
647
+ ole_workbooks.each do |ow|
648
+ yield workbook_class.new(ow, opts), i
649
+ i += 1
621
650
  end
622
651
  end
623
652
 
@@ -678,7 +707,6 @@ module RobustExcelOle
678
707
  if name.to_s[0,1] =~ /[A-Z]/
679
708
  begin
680
709
  raise ObjectNotAlive, 'method missing: Excel not alive' unless alive?
681
-
682
710
  @ole_excel.send(name, *args)
683
711
  rescue WIN32OLERuntimeError => msg
684
712
  if msg.message =~ /unknown property or method/