robust_excel_ole 1.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +14 -0
- data/README.rdoc +94 -351
- data/README_detail.rdoc +791 -0
- data/examples/edit_sheets/example_saving.rb +1 -1
- data/examples/open_save_close/example_default_excel.rb +5 -5
- data/examples/open_save_close/example_force_excel.rb +2 -2
- data/lib/robust_excel_ole/book.rb +237 -161
- data/lib/robust_excel_ole/excel.rb +96 -68
- data/lib/robust_excel_ole/reo_common.rb +19 -1
- data/lib/robust_excel_ole/sheet.rb +2 -3
- data/lib/robust_excel_ole/version.rb +1 -1
- data/robust_excel_ole.gemspec +2 -2
- data/spec/book_spec.rb +71 -16
- data/spec/book_specs/book_misc_spec.rb +244 -7
- data/spec/book_specs/book_open_spec.rb +472 -33
- data/spec/book_specs/book_save_spec.rb +19 -0
- data/spec/book_specs/book_unobtr_spec.rb +46 -4
- data/spec/data/another_workbook.xls +0 -0
- data/spec/data/book_with_blank.xls +0 -0
- data/spec/data/different_workbook.xls +0 -0
- data/spec/data/workbook.xls +0 -0
- data/spec/excel_spec.rb +134 -31
- metadata +7 -6
- data/spec/data/refed_wb.xls +0 -0
@@ -28,7 +28,7 @@ begin
|
|
28
28
|
end
|
29
29
|
book.close(:if_unsaved => :save)
|
30
30
|
# alternative: delete all other sheets
|
31
|
-
#book = Book.open(file_sheet_name, :
|
31
|
+
#book = Book.open(file_sheet_name, :force => {:excel => :new}, :visible => true)
|
32
32
|
#book.each do |sheet|
|
33
33
|
# book.sheet(sheet.Name).Delete() unless sheet.Name == sheet_orig.Name
|
34
34
|
#end
|
@@ -23,18 +23,18 @@ begin
|
|
23
23
|
p "book1 == book2" if book2 == book1 # the books are identical
|
24
24
|
sleep 2
|
25
25
|
new_excel = Excel.new(:reuse => false) # create a new Excel
|
26
|
-
book3 = Book.open(file_name2, :
|
27
|
-
if book3.excel == book2.excel then # since this book cannot be reopened, the option :
|
28
|
-
p "book3 opened in the first Excel" # according to :
|
26
|
+
book3 = Book.open(file_name2, :default => {:excel => :current}, :visible => true) # open another book
|
27
|
+
if book3.excel == book2.excel then # since this book cannot be reopened, the option :default => {:excel} applies:
|
28
|
+
p "book3 opened in the first Excel" # according to :default => {:excel => :current} the book is opened
|
29
29
|
end # in the Excel instance the was created first
|
30
30
|
sleep 2
|
31
31
|
new_excel = Excel.new(:reuse => false)
|
32
|
-
book4 = Book.open(file_name3, :
|
32
|
+
book4 = Book.open(file_name3, :default => {:excel => new_excel}, :visible => true) # open another book
|
33
33
|
if book4.excel == new_excel then # since this book cannot be reopened, the option :default_excel applies:
|
34
34
|
p "book4 opened in the second Excel" # according to :default_excel => new_excel the book is opened
|
35
35
|
end # in the given Excel, namely the second Excel instance new_excel
|
36
36
|
sleep 2
|
37
|
-
book5 = Book.open(file_name4, :
|
37
|
+
book5 = Book.open(file_name4, :default => {:excel => :new}, :visible => true) # open another book
|
38
38
|
if ((not book5.excel == book1.excel) && (not book5.excel == new_excel)) then # since this book cannot be reopened,
|
39
39
|
p "book5 opened in a third Excel" # the option :default_excel applies. according to :default_excel => :new
|
40
40
|
end # the book is opened in a new Excel
|
@@ -17,11 +17,11 @@ begin
|
|
17
17
|
book2 = Book.open(simple_file) # open a new book in the same Excel instance
|
18
18
|
p "book1 == book2" if book2 == book1 # the books are identical
|
19
19
|
sleep 2
|
20
|
-
book3 = Book.open(simple_file, :
|
20
|
+
book3 = Book.open(simple_file, :force => {:excel => :new, :visible => true}) # open another book in a new Excel instance,
|
21
21
|
p "book3 != book1" if (not (book3 == book1)) # the books are not identical
|
22
22
|
sleep 2
|
23
23
|
new_excel = Excel.new(:reuse => false) # create a third Excel instance
|
24
|
-
book4 = Book.open(simple_file, :
|
24
|
+
book4 = Book.open(simple_file, :force => {:excel => new_excel, :visible => true}) # open another book in the new Excel instance
|
25
25
|
p "book4 != book3 && book4 != book1" if (not (book4 == book3) && (not (book4 == book1)))
|
26
26
|
sleep 2 # (Excel chooses the first Excel application)
|
27
27
|
book4.close # close the books
|
@@ -9,47 +9,54 @@ module RobustExcelOle
|
|
9
9
|
attr_accessor :excel
|
10
10
|
attr_accessor :ole_workbook
|
11
11
|
attr_accessor :stored_filename
|
12
|
-
attr_accessor :options
|
13
|
-
attr_accessor :can_be_closed
|
12
|
+
attr_accessor :options
|
14
13
|
|
15
14
|
alias ole_object ole_workbook
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
DEFAULT_OPEN_OPTS = {
|
17
|
+
:default => {:excel => :current},
|
18
|
+
:force => {},
|
19
|
+
:if_unsaved => :raise,
|
20
|
+
:if_obstructed => :raise,
|
21
|
+
:if_absent => :raise,
|
22
|
+
:read_only => false,
|
23
|
+
:check_compatibility => false,
|
24
|
+
:update_links => :never
|
25
|
+
}
|
26
|
+
|
27
|
+
SYNONYMS_OPTS = [[[:default],[:d]], [[:force], [:f]],
|
28
|
+
[[:default,:excel],[:default_excel]],[[:force,:excel],[:force_excel]],
|
29
|
+
[[:default,:excel],[:default,:e]], [[:force,:excel],[:force,:e]],
|
30
|
+
[[:default,:visible],[:default,:v]], [[:force,:visible],[:force,:v]],
|
31
|
+
[[:force,:visible],[:visible]], [[:force,:visible],[:v]]
|
32
|
+
]
|
26
33
|
|
27
34
|
class << self
|
28
35
|
|
29
36
|
# opens a workbook.
|
30
37
|
# @param [String] file the file name
|
31
38
|
# @param [Hash] opts the options
|
32
|
-
# @option opts [
|
33
|
-
# @option opts [
|
39
|
+
# @option opts [Hash] :default or :d
|
40
|
+
# @option opts [Hash] :force or :f
|
34
41
|
# @option opts [Symbol] :if_unsaved :raise (default), :forget, :accept, :alert, :excel, or :new_excel
|
35
42
|
# @option opts [Symbol] :if_obstructed :raise (default), :forget, :save, :close_if_saved, or _new_excel
|
36
43
|
# @option opts [Symbol] :if_absent :raise (default) or :create
|
37
44
|
# @option opts [Boolean] :read_only true (default) or false
|
38
45
|
# @option opts [Boolean] :update_links :never (default), :always, :alert
|
39
|
-
# @option opts [Boolean] :
|
46
|
+
# @option opts [Boolean] :calculation :manual, :automatic, or nil (default)
|
40
47
|
# options:
|
41
|
-
# :
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
48
|
+
# :default : if the workbook was already open before, then use (unchange) its properties,
|
49
|
+
# otherwise, i.e. if the workbook cannot be reopened, use the properties stated in :default
|
50
|
+
# :force : no matter whether the workbook was already open before, use the properties stated in :force
|
51
|
+
# :default and :force contain: :excel, :visible
|
52
|
+
# :excel :current (or :active or :reuse)
|
53
|
+
# -> connects to a running (the first opened) Excel instance,
|
54
|
+
# excluding the hidden Excel instance, if it exists,
|
55
|
+
# otherwise opens in a new Excel instance.
|
56
|
+
# :new -> opens in a new Excel instance
|
57
|
+
# <excel-instance> -> opens in the given Excel instance
|
58
|
+
# :visible true, false, or nil (default)
|
59
|
+
# alternatives: :default_excel, :force_excel, :visible, :d, :f, :e, :v
|
53
60
|
# :if_unsaved if an unsaved workbook with the same name is open, then
|
54
61
|
# :raise -> raises an exception
|
55
62
|
# :forget -> close the unsaved workbook, open the new workbook
|
@@ -65,35 +72,33 @@ module RobustExcelOle
|
|
65
72
|
# :new_excel -> opens the new workbook in a new Excel instance
|
66
73
|
# :if_absent :raise -> raises an exception , if the file does not exists
|
67
74
|
# :create -> creates a new Excel file, if it does not exists
|
68
|
-
# :read_only true -> opens in read-only mode
|
69
|
-
# :
|
70
|
-
# :visible true -> makes the window of the workbook visible
|
75
|
+
# :read_only true -> opens in read-only mode
|
76
|
+
# :visible true -> makes the workbook visible
|
71
77
|
# :check_compatibility true -> check compatibility when saving
|
78
|
+
# :update_links true -> user is being asked how to update links, false -> links are never updated
|
72
79
|
# @return [Book] a workbook
|
73
80
|
def open(file, opts={ }, &block)
|
74
|
-
options =
|
75
|
-
options[:default_excel] = :current if (options[:default_excel] == :reuse || options[:default_excel] == :active)
|
76
|
-
options[:force_excel] = :current if (options[:force_excel] == :reuse || options[:force_excel] == :active)
|
81
|
+
options = process_options(opts)
|
77
82
|
book = nil
|
78
|
-
if (not (options[:
|
83
|
+
if (not (options[:force][:excel] == :new))
|
79
84
|
# if readonly is true, then prefer a book that is given in force_excel if this option is set
|
80
|
-
forced_excel = if options[:
|
81
|
-
options[:
|
85
|
+
forced_excel = if options[:force][:excel]
|
86
|
+
options[:force][:excel] == :current ? excel_class.new(:reuse => true) : excel_of(options[:force][:excel])
|
82
87
|
end
|
83
88
|
book = bookstore.fetch(file,
|
84
89
|
:prefer_writable => (not options[:read_only]),
|
85
90
|
:prefer_excel => (options[:read_only] ? forced_excel : nil)) rescue nil
|
86
91
|
if book
|
87
|
-
if (((not options[:
|
92
|
+
if (((not options[:force][:excel]) || (forced_excel == book.excel)) &&
|
88
93
|
(not (book.alive? && (not book.saved) && (not options[:if_unsaved] == :accept))))
|
89
|
-
book.options =
|
90
|
-
book.ensure_excel(options) unless book.excel.alive?
|
94
|
+
book.options = options
|
95
|
+
book.ensure_excel(options) # unless book.excel.alive?
|
91
96
|
# 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
|
92
97
|
book.close if (book.alive? && (not book.writable) && (not options[:read_only]))
|
93
98
|
# reopens the book
|
94
99
|
book.ensure_workbook(file,options) unless book.alive?
|
95
|
-
book.visible = options[:visible]
|
96
|
-
|
100
|
+
book.visible = options[:force][:visible] unless options[:force][:visible].nil?
|
101
|
+
book.excel.calculation = options[:calculation] unless options[:calculation].nil?
|
97
102
|
return book
|
98
103
|
end
|
99
104
|
end
|
@@ -108,14 +113,16 @@ module RobustExcelOle
|
|
108
113
|
# @param [Hash] opts the options
|
109
114
|
# @option opts [Symbol] see above
|
110
115
|
# @return [Book] a workbook
|
111
|
-
def self.new(workbook, opts={ }, &block)
|
116
|
+
def self.new(workbook, opts={ }, &block)
|
112
117
|
if workbook && (workbook.is_a? WIN32OLE)
|
118
|
+
opts = process_options(opts)
|
113
119
|
filename = workbook.Fullname.tr('\\','/') rescue nil
|
114
120
|
if filename
|
115
121
|
book = bookstore.fetch(filename)
|
116
122
|
if book && book.alive?
|
117
|
-
|
118
|
-
book.
|
123
|
+
book.visible = opts[:force][:visible] unless opts[:force][:visible].nil?
|
124
|
+
#book.excel.calculation = opts[:calculation].nil? ? book.excel.calculation : opts[:calculation]
|
125
|
+
book.excel.calculation = opts[:calculation] unless opts[:calculation].nil?
|
119
126
|
return book
|
120
127
|
end
|
121
128
|
end
|
@@ -130,21 +137,20 @@ module RobustExcelOle
|
|
130
137
|
# @option opts [Symbol] see above
|
131
138
|
# @return [Book] a workbook
|
132
139
|
def initialize(file_or_workbook, opts={ }, &block)
|
133
|
-
options =
|
134
|
-
if file_or_workbook.is_a? WIN32OLE
|
140
|
+
options = self.class.process_options(opts)
|
141
|
+
if file_or_workbook.is_a? WIN32OLE
|
135
142
|
workbook = file_or_workbook
|
136
143
|
@ole_workbook = workbook
|
137
144
|
# use the Excel instance where the workbook is opened
|
138
145
|
win32ole_excel = WIN32OLE.connect(workbook.Fullname).Application rescue nil
|
139
146
|
@excel = excel_class.new(win32ole_excel)
|
140
|
-
@excel.visible = options[:visible] unless options[:visible].nil?
|
141
|
-
|
147
|
+
@excel.visible = options[force][:visible] unless options[:force][:visible].nil?
|
148
|
+
@excel.calculation = options[:calculation] unless options[:calculation].nil?
|
142
149
|
ensure_excel(options)
|
143
150
|
else
|
144
151
|
file = file_or_workbook
|
145
152
|
ensure_excel(options)
|
146
153
|
ensure_workbook(file, options)
|
147
|
-
@can_be_closed = false if @can_be_closed.nil?
|
148
154
|
end
|
149
155
|
bookstore.store(self)
|
150
156
|
if block
|
@@ -158,6 +164,32 @@ module RobustExcelOle
|
|
158
164
|
|
159
165
|
private
|
160
166
|
|
167
|
+
# merges options with defaults and translates abbreviations and synonyms
|
168
|
+
def self.process_options(options) # :nodoc: #
|
169
|
+
translator = proc do |opts|
|
170
|
+
SYNONYMS_OPTS.each do |a|
|
171
|
+
synonym = a[1][1].nil? ? opts[a[1][0]] : opts[a[1][0]][a[1][1]] unless opts[a[1][0]].nil?
|
172
|
+
unless synonym.nil?
|
173
|
+
if a[0][1].nil?
|
174
|
+
opts[a[0][0]] = synonym if opts[a[0][0]].nil?
|
175
|
+
else
|
176
|
+
opts[a[0][0]] = {} if opts[a[0][0]].nil?
|
177
|
+
opts[a[0][0]][a[0][1]] = synonym if opts[a[0][0]][a[0][1]].nil?
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
opts[:default][:excel] = :current if (not opts[:default].nil?) && (opts[:default][:excel] == :reuse || opts[:default][:excel] == :active)
|
182
|
+
opts[:force][:excel] = :current if (not opts[:force].nil?) && (opts[:force][:excel] == :reuse || opts[:force][:excel] == :active)
|
183
|
+
opts
|
184
|
+
end
|
185
|
+
default_opts = translator.call(DEFAULT_OPEN_OPTS)
|
186
|
+
given_opts = translator.call(options)
|
187
|
+
opts = default_opts.merge(given_opts)
|
188
|
+
opts[:default] = default_opts[:default].merge(opts[:default]) unless opts[:default].nil?
|
189
|
+
opts[:force] = default_opts[:force].merge(opts[:force]) unless opts[:force].nil?
|
190
|
+
opts
|
191
|
+
end
|
192
|
+
|
161
193
|
# returns an Excel object when given Excel, Book or Win32ole object representing a Workbook or an Excel
|
162
194
|
def self.excel_of(object) # :nodoc: #
|
163
195
|
if object.is_a? WIN32OLE
|
@@ -186,8 +218,7 @@ module RobustExcelOle
|
|
186
218
|
filename = General::absolute_path(file)
|
187
219
|
ole_workbook = WIN32OLE.connect(filename)
|
188
220
|
workbook = Book.new(ole_workbook)
|
189
|
-
|
190
|
-
workbook.visible = options[:visible].nil? ? workbook.excel.visible : options[:visible]
|
221
|
+
workbook.visible = options[:force][:visible] unless options[:force][:visible].nil?
|
191
222
|
update_links_opt =
|
192
223
|
case options[:update_links]
|
193
224
|
when :alert; RobustExcelOle::XlUpdateLinksUserSetting
|
@@ -200,13 +231,19 @@ module RobustExcelOle
|
|
200
231
|
workbook
|
201
232
|
end
|
202
233
|
|
203
|
-
|
204
234
|
def ensure_excel(options) # :nodoc: #
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
235
|
+
if @excel && @excel.alive?
|
236
|
+
@excel.created = false
|
237
|
+
return
|
238
|
+
end
|
239
|
+
excel_option = (options[:force].nil? or options[:force][:excel].nil?) ? options[:default][:excel] : options[:force][:excel]
|
240
|
+
@excel = self.class.excel_of(excel_option) unless (excel_option == :current || excel_option == :new)
|
241
|
+
@excel = excel_class.new(:reuse => (excel_option == :current)) unless (@excel && @excel.alive?)
|
242
|
+
|
243
|
+
#options[:excel] = options[:force_excel] ? options[:force_excel] : options[:default_excel]
|
244
|
+
#options[:excel] = :current if (options[:excel] == :reuse || options[:excel] == :active)
|
245
|
+
#@excel = self.class.excel_of(options[:excel]) unless (options[:excel] == :current || options[:excel] == :new)
|
246
|
+
#@excel = excel_class.new(:reuse => (options[:excel] == :current)) unless (@excel && @excel.alive?)
|
210
247
|
end
|
211
248
|
|
212
249
|
def ensure_workbook(file, options) # :nodoc: #
|
@@ -246,9 +283,7 @@ module RobustExcelOle
|
|
246
283
|
open_or_create_workbook(file, options)
|
247
284
|
end
|
248
285
|
when :new_excel
|
249
|
-
|
250
|
-
excel_options[:reuse] = false
|
251
|
-
@excel = excel_class.new(excel_options)
|
286
|
+
@excel = excel_class.new(:reuse => false)
|
252
287
|
open_or_create_workbook(file, options)
|
253
288
|
else
|
254
289
|
raise OptionInvalid, ":if_obstructed: invalid option: #{options[:if_obstructed].inspect}"
|
@@ -268,7 +303,6 @@ module RobustExcelOle
|
|
268
303
|
when :alert, :excel
|
269
304
|
@excel.with_displayalerts(true) { open_or_create_workbook(file,options) }
|
270
305
|
when :new_excel
|
271
|
-
excel_options = {:visible => false}.merge(options)
|
272
306
|
excel_options[:reuse] = false
|
273
307
|
@excel = excel_class.new(excel_options)
|
274
308
|
open_or_create_workbook(file, options)
|
@@ -278,7 +312,7 @@ module RobustExcelOle
|
|
278
312
|
end
|
279
313
|
end
|
280
314
|
else
|
281
|
-
#
|
315
|
+
# open a new workbook
|
282
316
|
open_or_create_workbook(file, options)
|
283
317
|
end
|
284
318
|
end
|
@@ -292,58 +326,80 @@ module RobustExcelOle
|
|
292
326
|
begin
|
293
327
|
workbooks = @excel.Workbooks
|
294
328
|
rescue RuntimeError => msg
|
295
|
-
trace "RuntimeError: #{msg.message}"
|
296
329
|
if msg.message =~ /method missing: Excel not alive/
|
297
330
|
raise ExcelDamaged, "Excel instance not alive or damaged"
|
298
331
|
else
|
299
|
-
raise UnexpectedError, "unknown RuntimeError"
|
332
|
+
raise UnexpectedError, "unknown RuntimeError: #{msg.message}"
|
300
333
|
end
|
301
|
-
rescue WeakRef::RefError => msg
|
302
|
-
raise WeakRef::RefError, "#{msg.message}"
|
303
|
-
end
|
304
|
-
# workaround for linked workbooks for Excel 2007:
|
305
|
-
# opening and closing a dummy workbook if Excel has no workbooks.
|
306
|
-
# delay: with visible: 0.2 sec, without visible almost none
|
307
|
-
count = workbooks.Count
|
308
|
-
if @excel.Version.split(".").first.to_i >= 12 && count == 0
|
309
|
-
workbooks.Add
|
310
|
-
#@excel.set_calculation(:automatic)
|
311
|
-
end
|
312
|
-
update_links_opt =
|
313
|
-
case options[:update_links]
|
314
|
-
when :alert; RobustExcelOle::XlUpdateLinksUserSetting
|
315
|
-
when :never; RobustExcelOle::XlUpdateLinksNever
|
316
|
-
when :always; RobustExcelOle::XlUpdateLinksAlways
|
317
|
-
else RobustExcelOle::XlUpdateLinksNever
|
318
|
-
end
|
319
|
-
# probably bug in Excel: setting UpadateLinks seems to be dependent on calculation mode:
|
320
|
-
# update happens, if calcultion mode is automatic, does not, if calculation mode is manual
|
321
|
-
# parameter 'UpdateLinks' has no effect
|
322
|
-
@excel.with_displayalerts(update_links_opt == :alert ? true : @excel.displayalerts) do
|
323
|
-
workbooks.Open(filename, { 'ReadOnly' => options[:read_only] , 'UpdateLinks' => update_links_opt } )
|
324
334
|
end
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
335
|
+
begin
|
336
|
+
with_workaround_linked_workbooks_excel2007(options) do
|
337
|
+
workbooks.Open(filename, { 'ReadOnly' => options[:read_only] ,
|
338
|
+
'UpdateLinks' => updatelinks_vba(options[:update_links]) })
|
339
|
+
end
|
340
|
+
rescue WIN32OLERuntimeError => msg
|
341
|
+
if msg.message =~ /800A03EC/
|
342
|
+
raise ExcelError, "user canceled or runtime error #{msg.message}"
|
343
|
+
else
|
344
|
+
raise UnexpectedError, "unexpected WIN32OLERuntimeError: #{msg.message}"
|
345
|
+
end
|
332
346
|
end
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
347
|
+
begin
|
348
|
+
# workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
|
349
|
+
begin
|
350
|
+
@ole_workbook = workbooks.Item(File.basename(filename))
|
351
|
+
rescue WIN32OLERuntimeError => msg
|
352
|
+
raise UnexpectedError, "unexpected WIN32OLERuntimeError: #{msg.message}"
|
353
|
+
end
|
354
|
+
if options[:force][:visible].nil? && (not options[:default][:visible].nil?)
|
355
|
+
if @excel.created
|
356
|
+
self.visible = options[:default][:visible]
|
357
|
+
else
|
358
|
+
self.window_visible = options[:default][:visible]
|
359
|
+
end
|
360
|
+
else
|
361
|
+
self.visible = options[:force][:visible] unless options[:force][:visible].nil?
|
362
|
+
end
|
363
|
+
@ole_workbook.CheckCompatibility = options[:check_compatibility]
|
364
|
+
@excel.calculation = options[:calculation] unless options[:calculation].nil?
|
365
|
+
self.Saved = true # unless self.Saved # ToDo: this is too hard
|
366
|
+
rescue WIN32OLERuntimeError => msg
|
367
|
+
raise UnexpectedError, "unexpected WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# translating the option UpdateLinks from REO to VBA
|
374
|
+
# setting UpdateLinks works only if calculation mode is automatic,
|
375
|
+
# parameter 'UpdateLinks' has no effect
|
376
|
+
def updatelinks_vba(updatelinks_reo)
|
377
|
+
case updatelinks_reo
|
378
|
+
when :alert; RobustExcelOle::XlUpdateLinksUserSetting
|
379
|
+
when :never; RobustExcelOle::XlUpdateLinksNever
|
380
|
+
when :always; RobustExcelOle::XlUpdateLinksAlways
|
381
|
+
else RobustExcelOle::XlUpdateLinksNever
|
344
382
|
end
|
345
383
|
end
|
346
384
|
|
385
|
+
# workaround for linked workbooks for Excel 2007:
|
386
|
+
# opening and closing a dummy workbook if Excel has no workbooks.
|
387
|
+
# delay: with visible: 0.2 sec, without visible almost none
|
388
|
+
def with_workaround_linked_workbooks_excel2007(options)
|
389
|
+
workbooks = @excel.Workbooks
|
390
|
+
workaround_condition = @excel.Version.split(".").first.to_i >= 12 && workbooks.Count == 0
|
391
|
+
if workaround_condition
|
392
|
+
workbooks.Add
|
393
|
+
@excel.calculation = options[:calculation].nil? ? @excel.calculation : options[:calculation]
|
394
|
+
end
|
395
|
+
begin
|
396
|
+
#@excel.with_displayalerts(update_links_opt == :alert ? true : @excel.displayalerts) do
|
397
|
+
yield self
|
398
|
+
ensure
|
399
|
+
@excel.with_displayalerts(false){workbooks.Item(1).Close} if workaround_condition
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
347
403
|
public
|
348
404
|
|
349
405
|
# closes the workbook, if it is alive
|
@@ -391,6 +447,16 @@ module RobustExcelOle
|
|
391
447
|
|
392
448
|
public
|
393
449
|
|
450
|
+
# keeps the saved-status unchanged
|
451
|
+
def retain_saved
|
452
|
+
saved = self.Saved
|
453
|
+
begin
|
454
|
+
yield self
|
455
|
+
ensure
|
456
|
+
self.Saved = saved
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
394
460
|
def self.for_reading(*args, &block)
|
395
461
|
args = args.dup
|
396
462
|
opts = args.last.is_a?(Hash) ? args.pop : {}
|
@@ -407,7 +473,7 @@ module RobustExcelOle
|
|
407
473
|
unobtrusively(*args, &block)
|
408
474
|
end
|
409
475
|
|
410
|
-
#
|
476
|
+
# allows to modify a workbook such that its state (open/close, saved/unsaved, readonly/writable) remains unchanged
|
411
477
|
# @param [String] file the file name
|
412
478
|
# @param [Hash] if_closed an option
|
413
479
|
# @param [Hash] opts the options
|
@@ -420,13 +486,13 @@ module RobustExcelOle
|
|
420
486
|
# :if_closed : if the workbook is closed, then open it in
|
421
487
|
# :current (or :active, :reuse) -> the Excel instance of the workbook, if it exists,
|
422
488
|
# reuse another Excel, otherwise
|
423
|
-
# :hidden -> a separate Excel instance that is not visible and has no
|
489
|
+
# :hidden -> a separate Excel instance that is not visible and has no displayalerts
|
424
490
|
# <excel-instance> -> the given Excel instance
|
425
491
|
# :readonly_excel: if the workbook is opened only as ReadOnly and shall be modified, then
|
426
492
|
# true: closes it and open it as writable in the Excel instance where it was open so far
|
427
493
|
# false (default) opens it as writable in another running excel instance, if it exists,
|
428
494
|
# otherwise open in a new Excel instance.
|
429
|
-
# :visible, :
|
495
|
+
# :visible, :read_only, :update_links, :check_compatibility : see options in #open
|
430
496
|
# @return [Book] a workbook
|
431
497
|
def self.unobtrusively(file, if_closed = nil, opts = { }, &block)
|
432
498
|
if if_closed.is_a? Hash
|
@@ -435,58 +501,60 @@ module RobustExcelOle
|
|
435
501
|
end
|
436
502
|
if_closed = :current if (if_closed == :reuse || if_closed == :active)
|
437
503
|
if_closed = :current unless if_closed
|
438
|
-
options =
|
439
|
-
|
440
|
-
:readonly_excel => false,
|
441
|
-
:keep_open => false,
|
442
|
-
:check_compatibility => false
|
443
|
-
}.merge(opts)
|
504
|
+
options = process_options(opts)
|
505
|
+
opts = {:readonly_excel => false,:keep_open => false}.merge(opts)
|
444
506
|
book = bookstore.fetch(file, :prefer_writable => (not options[:read_only]))
|
445
507
|
was_not_alive_or_nil = book.nil? || (not book.alive?)
|
446
508
|
workbook = book.excel.Workbooks.Item(File.basename(file)) rescue nil
|
447
|
-
now_alive =
|
448
|
-
begin
|
449
|
-
workbook.Name
|
450
|
-
true
|
451
|
-
rescue
|
452
|
-
false
|
453
|
-
end
|
454
509
|
was_saved = was_not_alive_or_nil ? true : book.saved
|
455
|
-
was_writable = book.writable unless was_not_alive_or_nil
|
510
|
+
was_writable = book.writable unless was_not_alive_or_nil
|
511
|
+
was_check_compatibility = book.CheckCompatibility unless was_not_alive_or_nil
|
456
512
|
begin
|
457
513
|
book =
|
458
514
|
if was_not_alive_or_nil
|
459
515
|
case if_closed
|
460
516
|
when :current
|
461
|
-
open(file, :
|
517
|
+
open(file, :default => {:excel => :current}, :read_only => options[:read_only])
|
462
518
|
when :hidden
|
463
|
-
open(file, :
|
519
|
+
open(file, :force => {:excel => bookstore.hidden_excel}, :read_only => options[:read_only])
|
464
520
|
else
|
465
|
-
open(file, :
|
521
|
+
open(file, :force => {:excel => if_closed}, :read_only => options[:read_only])
|
466
522
|
end
|
467
523
|
else
|
468
524
|
if was_writable || options[:read_only]
|
469
525
|
book
|
470
526
|
else
|
471
|
-
options[:readonly_excel] ? open(file, :
|
472
|
-
open(file, :
|
527
|
+
options[:readonly_excel] ? open(file, :force => {:excel => book.excel}, :read_only => options[:read_only]) :
|
528
|
+
open(file, :force => {:excel => :new}, :read_only => options[:read_only])
|
473
529
|
end
|
474
530
|
end
|
475
|
-
#book.excel.visible = options[:visible] unless options[:visible].nil?
|
476
|
-
|
477
|
-
|
478
|
-
|
531
|
+
#book.excel.visible = options[:force][:visible] unless options[:force].nil? or options[:force][:visible].nil?
|
532
|
+
options = process_options(options)
|
533
|
+
if options[:force][:visible].nil? && (not options[:default][:visible].nil?)
|
534
|
+
if book.excel.created
|
535
|
+
book.visible = options[:default][:visible]
|
536
|
+
else
|
537
|
+
book.window_visible = options[:default][:visible]
|
538
|
+
end
|
539
|
+
else
|
540
|
+
book.visible = options[:force][:visible] unless options[:force][:visible].nil?
|
541
|
+
end
|
542
|
+
book.CheckCompatibility = options[:check_compatibility] unless options[:check_compatibility].nil?
|
479
543
|
yield book
|
480
544
|
ensure
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
545
|
+
if book && book.alive?
|
546
|
+
unless book.saved
|
547
|
+
book.save unless options[:read_only]
|
548
|
+
book.Saved = true if was_saved && options[:read_only]
|
549
|
+
book.Saved = false unless was_saved || options[:read_only]
|
550
|
+
end
|
551
|
+
# book was open, readonly and should be modified
|
552
|
+
unless was_not_alive_or_nil || options[:read_only] || was_writable || (not options[:readonly_excel])
|
553
|
+
open(file, :force => {:excel => book.excel}, :if_obstructed => :new_excel, :read_only => true)
|
554
|
+
end
|
555
|
+
book.CheckCompatibility = was_check_compatibility unless was_check_compatibility.nil?
|
556
|
+
book.close if was_not_alive_or_nil && (not options[:keep_open])
|
486
557
|
end
|
487
|
-
@can_be_closed = true if options[:keep_open] && book
|
488
|
-
book.close if (was_not_alive_or_nil && (not now_alive) && (not options[:keep_open]) && book)
|
489
|
-
book.CheckCompatibility = old_check_compatibility if book && book.alive?
|
490
558
|
end
|
491
559
|
end
|
492
560
|
|
@@ -561,26 +629,21 @@ module RobustExcelOle
|
|
561
629
|
raise OptionInvalid, ":if_exists: invalid option: #{options[:if_exists].inspect}"
|
562
630
|
end
|
563
631
|
end
|
564
|
-
|
565
|
-
|
566
|
-
@excel.Workbooks.Item(File.basename(file))
|
567
|
-
rescue WIN32OLERuntimeError => msg
|
568
|
-
nil
|
569
|
-
end
|
570
|
-
if blocking_workbook then
|
632
|
+
other_workbook = @excel.Workbooks.Item(File.basename(file)) rescue nil
|
633
|
+
if other_workbook && (not(self.filename == other_workbook.Fullname.tr('\\','/'))) then
|
571
634
|
case options[:if_obstructed]
|
572
635
|
when :raise
|
573
|
-
raise WorkbookBlocked, "blocked by another workbook: #{
|
636
|
+
raise WorkbookBlocked, "blocked by another workbook: #{other_workbook.Fullname.tr('\\','/')}"
|
574
637
|
when :forget
|
575
638
|
# nothing
|
576
639
|
when :save
|
577
|
-
|
640
|
+
other_workbook.Save
|
578
641
|
when :close_if_saved
|
579
|
-
raise WorkbookBlocked, "blocking workbook is unsaved: #{File.basename(file).inspect}" unless
|
642
|
+
raise WorkbookBlocked, "blocking workbook is unsaved: #{File.basename(file).inspect}" unless other_workbook.Saved
|
580
643
|
else
|
581
644
|
raise OptionInvalid, ":if_obstructed: invalid option: #{options[:if_obstructed].inspect}"
|
582
645
|
end
|
583
|
-
|
646
|
+
other_workbook.Close
|
584
647
|
end
|
585
648
|
save_as_workbook(file, options)
|
586
649
|
self
|
@@ -619,7 +682,6 @@ module RobustExcelOle
|
|
619
682
|
sheet_class.new(@ole_workbook.Worksheets.Item(name))
|
620
683
|
rescue WIN32OLERuntimeError => msg
|
621
684
|
raise NameNotFound, "could not return a sheet with name #{name.inspect}"
|
622
|
-
# trace "#{msg.message}"
|
623
685
|
end
|
624
686
|
end
|
625
687
|
|
@@ -742,7 +804,7 @@ module RobustExcelOle
|
|
742
804
|
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(self.stored_filename).inspect}"
|
743
805
|
end
|
744
806
|
end
|
745
|
-
if value
|
807
|
+
if value.is_a?(Bignum) #RobustExcelOle::XlErrName
|
746
808
|
return default_val if default_val
|
747
809
|
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(self.stored_filename).inspect}"
|
748
810
|
end
|
@@ -794,19 +856,35 @@ module RobustExcelOle
|
|
794
856
|
@excel.visible && @ole_workbook.Windows(@ole_workbook.Name).Visible
|
795
857
|
end
|
796
858
|
|
797
|
-
# makes the window of the workbook visible or invisible
|
798
|
-
# @param [Boolean] visible_value
|
859
|
+
# makes both the Excel instance and the window of the workbook visible, or the window invisible
|
860
|
+
# @param [Boolean] visible_value determines whether the workbook shall be visible
|
799
861
|
def visible= visible_value
|
800
|
-
saved = @ole_workbook.Saved
|
801
862
|
@excel.visible = true if visible_value
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
863
|
+
self.window_visible = visible_value
|
864
|
+
end
|
865
|
+
|
866
|
+
# returns true, if the window of the workbook is set to visible, false otherwise
|
867
|
+
def window_visible
|
868
|
+
return @ole_workbook.Windows(@ole_workbook.Name).Visible
|
869
|
+
end
|
870
|
+
|
871
|
+
# makes the window of the workbook visible or invisible
|
872
|
+
# @param [Boolean] visible_value determines whether the window of the workbook shall be visible
|
873
|
+
def window_visible= visible_value
|
874
|
+
retain_saved do
|
875
|
+
@ole_workbook.Windows(@ole_workbook.Name).Visible = visible_value if @ole_workbook.Windows.Count > 0
|
806
876
|
end
|
807
|
-
@ole_workbook.Saved = saved
|
808
877
|
end
|
809
878
|
|
879
|
+
# def visible= visible_value
|
880
|
+
# retain_saved do
|
881
|
+
# @excel.visible = true if visible_value
|
882
|
+
# if @ole_workbook.Windows.Count > 0
|
883
|
+
# @ole_workbook.Windows(@ole_workbook.Name).Visible = visible_value
|
884
|
+
# end
|
885
|
+
# end
|
886
|
+
# end
|
887
|
+
|
810
888
|
# returns true, if the workbook reacts to methods, false otherwise
|
811
889
|
def alive?
|
812
890
|
begin
|
@@ -920,6 +998,4 @@ public
|
|
920
998
|
|
921
999
|
Workbook = Book
|
922
1000
|
|
923
|
-
|
924
|
-
|
925
1001
|
end
|