robust_excel_ole 1.4 → 1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +6 -0
- data/README.rdoc +7 -1
- data/docs/README_ranges.rdoc +1 -1
- data/lib/reo_console.rb +19 -17
- data/lib/robust_excel_ole.rb +1 -2
- data/lib/robust_excel_ole/bookstore.rb +45 -45
- data/lib/robust_excel_ole/cell.rb +11 -12
- data/lib/robust_excel_ole/cygwin.rb +11 -11
- data/lib/robust_excel_ole/excel.rb +218 -201
- data/lib/robust_excel_ole/general.rb +23 -21
- data/lib/robust_excel_ole/range.rb +18 -19
- data/lib/robust_excel_ole/reo_common.rb +95 -82
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +199 -205
- data/lib/robust_excel_ole/worksheet.rb +33 -56
- data/lib/spec_helper.rb +3 -3
- data/spec/workbook_spec.rb +18 -0
- data/spec/workbook_specs/workbook_misc_spec.rb +2 -2
- data/spec/worksheet_spec.rb +18 -0
- metadata +2 -2
@@ -11,27 +11,27 @@ module RobustExcelOle
|
|
11
11
|
attr_accessor :excel
|
12
12
|
attr_accessor :ole_workbook
|
13
13
|
attr_accessor :stored_filename
|
14
|
-
attr_accessor :options
|
14
|
+
attr_accessor :options
|
15
15
|
attr_accessor :modified_cells
|
16
16
|
attr_reader :workbook
|
17
17
|
|
18
18
|
alias ole_object ole_workbook
|
19
19
|
|
20
|
-
DEFAULT_OPEN_OPTS = {
|
20
|
+
DEFAULT_OPEN_OPTS = {
|
21
21
|
:default => {:excel => :current},
|
22
|
-
:force => {},
|
22
|
+
:force => {},
|
23
23
|
:if_unsaved => :raise,
|
24
24
|
:if_obstructed => :raise,
|
25
25
|
:if_absent => :raise,
|
26
26
|
:read_only => false,
|
27
|
-
:check_compatibility => false,
|
27
|
+
:check_compatibility => false,
|
28
28
|
:update_links => :never
|
29
|
-
}
|
30
|
-
|
31
|
-
ABBREVIATIONS = [[:default,:d], [:force, :f], [:excel, :e], [:visible, :v]]
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
ABBREVIATIONS = [[:default,:d], [:force, :f], [:excel, :e], [:visible, :v]].freeze
|
32
32
|
|
33
33
|
class << self
|
34
|
-
|
34
|
+
|
35
35
|
# opens a workbook.
|
36
36
|
# @param [String] file the file name
|
37
37
|
# @param [Hash] opts the options
|
@@ -42,66 +42,66 @@ module RobustExcelOle
|
|
42
42
|
# @option opts [Symbol] :if_absent :raise (default) or :create
|
43
43
|
# @option opts [Boolean] :read_only true (default) or false
|
44
44
|
# @option opts [Boolean] :update_links :never (default), :always, :alert
|
45
|
-
# @option opts [Boolean] :calculation :manual, :automatic, or nil (default)
|
46
|
-
# options:
|
45
|
+
# @option opts [Boolean] :calculation :manual, :automatic, or nil (default)
|
46
|
+
# options:
|
47
47
|
# :default : if the workbook was already open before, then use (unchange) its properties,
|
48
48
|
# otherwise, i.e. if the workbook cannot be reopened, use the properties stated in :default
|
49
|
-
# :force : no matter whether the workbook was already open before, use the properties stated in :force
|
49
|
+
# :force : no matter whether the workbook was already open before, use the properties stated in :force
|
50
50
|
# :default and :force contain: :excel, :visible
|
51
|
-
# :excel :current (or :active or :reuse)
|
51
|
+
# :excel :current (or :active or :reuse)
|
52
52
|
# -> connects to a running (the first opened) Excel instance,
|
53
53
|
# excluding the hidden Excel instance, if it exists,
|
54
54
|
# otherwise opens in a new Excel instance.
|
55
|
-
# :new -> opens in a new Excel instance
|
55
|
+
# :new -> opens in a new Excel instance
|
56
56
|
# <excel-instance> -> opens in the given Excel instance
|
57
57
|
# :visible true, false, or nil (default)
|
58
58
|
# alternatives: :default_excel, :force_excel, :visible, :d, :f, :e, :v
|
59
59
|
# :if_unsaved if an unsaved workbook with the same name is open, then
|
60
60
|
# :raise -> raises an exception
|
61
|
-
# :forget -> close the unsaved workbook, open the new workbook
|
62
|
-
# :accept -> lets the unsaved workbook open
|
61
|
+
# :forget -> close the unsaved workbook, open the new workbook
|
62
|
+
# :accept -> lets the unsaved workbook open
|
63
63
|
# :alert or :excel -> gives control to Excel
|
64
64
|
# :new_excel -> opens the new workbook in a new Excel instance
|
65
65
|
# :if_obstructed if a workbook with the same name in a different path is open, then
|
66
|
-
# :raise -> raises an exception
|
66
|
+
# :raise -> raises an exception
|
67
67
|
# :forget -> closes the old workbook, open the new workbook
|
68
68
|
# :save -> saves the old workbook, close it, open the new workbook
|
69
69
|
# :close_if_saved -> closes the old workbook and open the new workbook, if the old workbook is saved,
|
70
70
|
# otherwise raises an exception.
|
71
|
-
# :new_excel -> opens the new workbook in a new Excel instance
|
71
|
+
# :new_excel -> opens the new workbook in a new Excel instance
|
72
72
|
# :if_absent :raise -> raises an exception , if the file does not exists
|
73
|
-
# :create -> creates a new Excel file, if it does not exists
|
74
|
-
# :read_only true -> opens in read-only mode
|
73
|
+
# :create -> creates a new Excel file, if it does not exists
|
74
|
+
# :read_only true -> opens in read-only mode
|
75
75
|
# :visible true -> makes the workbook visible
|
76
76
|
# :check_compatibility true -> check compatibility when saving
|
77
77
|
# :update_links true -> user is being asked how to update links, false -> links are never updated
|
78
78
|
# @return [Workbook] a representation of a workbook
|
79
|
-
def open(file, opts={ }, &block)
|
79
|
+
def open(file, opts = { }, &block)
|
80
80
|
options = @options = process_options(opts)
|
81
81
|
book = nil
|
82
|
-
if (
|
82
|
+
if (options[:force][:excel] != :new) && (options[:force][:excel] != :reserved_new)
|
83
83
|
# if readonly is true, then prefer a book that is given in force_excel if this option is set
|
84
84
|
forced_excel = if options[:force][:excel]
|
85
|
-
|
85
|
+
options[:force][:excel] == :current ? excel_class.new(:reuse => true) : excel_of(options[:force][:excel])
|
86
86
|
end
|
87
|
-
book = bookstore.fetch(file,
|
88
|
-
:prefer_writable => (
|
87
|
+
book = bookstore.fetch(file,
|
88
|
+
:prefer_writable => !(options[:read_only]),
|
89
89
|
:prefer_excel => (options[:read_only] ? forced_excel : nil)) rescue nil
|
90
90
|
if book
|
91
|
-
#if forced_excel != book.excel &&
|
91
|
+
# if forced_excel != book.excel &&
|
92
92
|
# (not (book.alive? && (not book.saved) && (not options[:if_unsaved] == :accept)))
|
93
|
-
if ((
|
94
|
-
|
93
|
+
if (!(options[:force][:excel]) || (forced_excel == book.excel)) &&
|
94
|
+
!(book.alive? && !book.saved && (options[:if_unsaved] != :accept))
|
95
95
|
book.options = options
|
96
96
|
book.ensure_excel(options) # unless book.excel.alive?
|
97
97
|
# if the ReadOnly status shall be changed, save, close and reopen it
|
98
|
-
if book.alive?
|
99
|
-
(book.writable
|
100
|
-
book.save if book.writable &&
|
98
|
+
if book.alive? && ((!book.writable && !(options[:read_only])) ||
|
99
|
+
(book.writable && options[:read_only]))
|
100
|
+
book.save if book.writable && !book.saved
|
101
101
|
book.close(:if_unsaved => :forget)
|
102
|
-
end
|
103
|
-
# reopens the book if it was closed
|
104
|
-
book.ensure_workbook(file,options) unless book.alive?
|
102
|
+
end
|
103
|
+
# reopens the book if it was closed
|
104
|
+
book.ensure_workbook(file,options) unless book.alive?
|
105
105
|
book.visible = options[:force][:visible] unless options[:force][:visible].nil?
|
106
106
|
book.CheckCompatibility = options[:check_compatibility] unless options[:check_compatibility].nil?
|
107
107
|
book.excel.calculation = options[:calculation] unless options[:calculation].nil?
|
@@ -111,7 +111,7 @@ module RobustExcelOle
|
|
111
111
|
end
|
112
112
|
new(file, options, &block)
|
113
113
|
end
|
114
|
-
end
|
114
|
+
end
|
115
115
|
|
116
116
|
# creates a Workbook object by opening an Excel file given its filename workbook
|
117
117
|
# or by promoting a Win32OLE object representing an Excel file
|
@@ -119,16 +119,16 @@ module RobustExcelOle
|
|
119
119
|
# @param [Hash] opts the options
|
120
120
|
# @option opts [Symbol] see above
|
121
121
|
# @return [Workbook] a workbook
|
122
|
-
def self.new(workbook, opts={ }, &block)
|
122
|
+
def self.new(workbook, opts = { }, &block)
|
123
123
|
opts = process_options(opts)
|
124
124
|
if workbook && (workbook.is_a? WIN32OLE)
|
125
125
|
filename = workbook.Fullname.tr('\\','/') rescue nil
|
126
126
|
if filename
|
127
127
|
book = bookstore.fetch(filename)
|
128
128
|
if book && book.alive?
|
129
|
-
book.visible = opts[:force][:visible] unless opts[:force].nil?
|
129
|
+
book.visible = opts[:force][:visible] unless opts[:force].nil? || opts[:force][:visible].nil?
|
130
130
|
book.excel.calculation = opts[:calculation] unless opts[:calculation].nil?
|
131
|
-
return book
|
131
|
+
return book
|
132
132
|
else
|
133
133
|
super
|
134
134
|
end
|
@@ -139,20 +139,20 @@ module RobustExcelOle
|
|
139
139
|
end
|
140
140
|
|
141
141
|
# creates a new Workbook object, if a file name is given
|
142
|
-
# Promotes the win32ole workbook to a Workbook object, if a win32ole-workbook is given
|
142
|
+
# Promotes the win32ole workbook to a Workbook object, if a win32ole-workbook is given
|
143
143
|
# @param [Variant] file_or_workbook file name or workbook
|
144
144
|
# @param [Hash] opts the options
|
145
145
|
# @option opts [Symbol] see above
|
146
146
|
# @return [Workbook] a workbook
|
147
|
-
def initialize(file_or_workbook, options={ }, &block)
|
148
|
-
#options = @options = self.class.process_options(options) if options.empty?
|
149
|
-
if file_or_workbook.is_a? WIN32OLE
|
147
|
+
def initialize(file_or_workbook, options = { }, &block)
|
148
|
+
# options = @options = self.class.process_options(options) if options.empty?
|
149
|
+
if file_or_workbook.is_a? WIN32OLE
|
150
150
|
workbook = file_or_workbook
|
151
|
-
@ole_workbook = workbook
|
151
|
+
@ole_workbook = workbook
|
152
152
|
# use the Excel instance where the workbook is opened
|
153
|
-
win32ole_excel = WIN32OLE.connect(workbook.Fullname).Application rescue nil
|
154
|
-
@excel = excel_class.new(win32ole_excel)
|
155
|
-
@excel.visible = options[force][:visible] unless options[:force][:visible].nil?
|
153
|
+
win32ole_excel = WIN32OLE.connect(workbook.Fullname).Application rescue nil
|
154
|
+
@excel = excel_class.new(win32ole_excel)
|
155
|
+
@excel.visible = options[force][:visible] unless options[:force][:visible].nil?
|
156
156
|
@excel.calculation = options[:calculation] unless options[:calculation].nil?
|
157
157
|
ensure_excel(options)
|
158
158
|
else
|
@@ -180,12 +180,12 @@ module RobustExcelOle
|
|
180
180
|
erg = {}
|
181
181
|
opts.each do |key,value|
|
182
182
|
new_key = key
|
183
|
-
ABBREVIATIONS.each{|long,short| new_key = long if key == short}
|
183
|
+
ABBREVIATIONS.each { |long,short| new_key = long if key == short }
|
184
184
|
if value.is_a?(Hash)
|
185
185
|
erg[new_key] = {}
|
186
186
|
value.each do |k,v|
|
187
187
|
new_k = k
|
188
|
-
ABBREVIATIONS.each{|l,s| new_k = l if k == s}
|
188
|
+
ABBREVIATIONS.each { |l,s| new_k = l if k == s }
|
189
189
|
erg[new_key][new_k] = v
|
190
190
|
end
|
191
191
|
else
|
@@ -195,20 +195,20 @@ module RobustExcelOle
|
|
195
195
|
erg[:default] = {} if erg[:default].nil?
|
196
196
|
erg[:force] = {} if erg[:force].nil?
|
197
197
|
force_list = [:visible, :excel]
|
198
|
-
erg.each {|key,value| erg[:force][key] = value if force_list.include?(key)}
|
198
|
+
erg.each { |key,value| erg[:force][key] = value if force_list.include?(key) }
|
199
199
|
erg[:default][:excel] = erg[:default_excel] unless erg[:default_excel].nil?
|
200
200
|
erg[:force][:excel] = erg[:force_excel] unless erg[:force_excel].nil?
|
201
|
-
erg[:default][:excel] = :current if
|
202
|
-
erg[:force][:excel] = :current if
|
201
|
+
erg[:default][:excel] = :current if erg[:default][:excel] == :reuse || erg[:default][:excel] == :active
|
202
|
+
erg[:force][:excel] = :current if erg[:force][:excel] == :reuse || erg[:force][:excel] == :active
|
203
203
|
erg
|
204
204
|
end
|
205
205
|
opts = translator.call(options)
|
206
|
-
default_open_opts = proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS :
|
206
|
+
default_open_opts = proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS :
|
207
207
|
{:default => {:excel => :current}, :force => {}, :update_links => :never }
|
208
208
|
default_opts = translator.call(default_open_opts)
|
209
209
|
opts = default_opts.merge(opts)
|
210
210
|
opts[:default] = default_opts[:default].merge(opts[:default]) unless opts[:default].nil?
|
211
|
-
opts[:force] = default_opts[:force].merge(opts[:force]) unless opts[:force].nil?
|
211
|
+
opts[:force] = default_opts[:force].merge(opts[:force]) unless opts[:force].nil?
|
212
212
|
opts
|
213
213
|
end
|
214
214
|
|
@@ -216,8 +216,8 @@ module RobustExcelOle
|
|
216
216
|
def self.excel_of(object) # :nodoc: #
|
217
217
|
if object.is_a? WIN32OLE
|
218
218
|
case object.ole_obj_help.name
|
219
|
-
when /Workbook/i
|
220
|
-
new(object).excel
|
219
|
+
when /Workbook/i
|
220
|
+
new(object).excel
|
221
221
|
when /Application/i
|
222
222
|
excel_class.new(object)
|
223
223
|
else
|
@@ -227,7 +227,7 @@ module RobustExcelOle
|
|
227
227
|
begin
|
228
228
|
object.excel
|
229
229
|
rescue
|
230
|
-
raise TypeREOError,
|
230
|
+
raise TypeREOError, 'given object is neither an Excel, a Workbook, nor a Win32ole'
|
231
231
|
end
|
232
232
|
end
|
233
233
|
end
|
@@ -235,34 +235,34 @@ module RobustExcelOle
|
|
235
235
|
public
|
236
236
|
|
237
237
|
def ensure_excel(options) # :nodoc: #
|
238
|
-
if excel && @excel.alive?
|
238
|
+
if excel && @excel.alive?
|
239
239
|
@excel.created = false
|
240
240
|
return
|
241
241
|
end
|
242
|
-
excel_option =
|
243
|
-
@excel = self.class.excel_of(excel_option) unless
|
244
|
-
excel_class.new(:reuse => false) if excel_option == :reserved_new
|
245
|
-
@excel = excel_class.new(:reuse => (excel_option == :current)) unless
|
242
|
+
excel_option = options[:force].nil? || options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
|
243
|
+
@excel = self.class.excel_of(excel_option) unless excel_option == :current || excel_option == :new || excel_option == :reserved_new
|
244
|
+
excel_class.new(:reuse => false) if (excel_option == :reserved_new) && Excel.known_excel_instances.empty?
|
245
|
+
@excel = excel_class.new(:reuse => (excel_option == :current)) unless @excel && @excel.alive?
|
246
246
|
@excel
|
247
|
-
end
|
247
|
+
end
|
248
248
|
|
249
249
|
def ensure_workbook(file, options) # :nodoc: #
|
250
250
|
file = @stored_filename ? @stored_filename : file
|
251
|
-
raise(FileNameNotGiven,
|
252
|
-
raise(FileNotFound, "file #{General
|
251
|
+
raise(FileNameNotGiven, 'filename is nil') if file.nil?
|
252
|
+
raise(FileNotFound, "file #{General.absolute_path(file).inspect} is a directory") if File.directory?(file)
|
253
253
|
unless File.exist?(file)
|
254
254
|
if options[:if_absent] == :create
|
255
255
|
@ole_workbook = excel_class.current.generate_workbook(file)
|
256
|
-
else
|
257
|
-
raise FileNotFound, "file #{General
|
256
|
+
else
|
257
|
+
raise FileNotFound, "file #{General.absolute_path(file).inspect} not found"
|
258
258
|
end
|
259
259
|
end
|
260
260
|
@ole_workbook = @excel.Workbooks.Item(File.basename(file)) rescue nil
|
261
|
-
if @ole_workbook
|
262
|
-
obstructed_by_other_book = (File.basename(file) == File.basename(@ole_workbook.Fullname)) &&
|
263
|
-
(
|
261
|
+
if @ole_workbook
|
262
|
+
obstructed_by_other_book = (File.basename(file) == File.basename(@ole_workbook.Fullname)) &&
|
263
|
+
(General.absolute_path(file) != @ole_workbook.Fullname)
|
264
264
|
# if workbook is obstructed by a workbook with same name and different path
|
265
|
-
if obstructed_by_other_book
|
265
|
+
if obstructed_by_other_book
|
266
266
|
case options[:if_obstructed]
|
267
267
|
when :raise
|
268
268
|
raise WorkbookBlocked, "blocked by a workbook with the same name in a different path: #{@ole_workbook.Fullname.tr('\\','/')}"
|
@@ -276,14 +276,14 @@ module RobustExcelOle
|
|
276
276
|
@ole_workbook = nil
|
277
277
|
open_or_create_workbook(file, options)
|
278
278
|
when :close_if_saved
|
279
|
-
if
|
279
|
+
if !@ole_workbook.Saved
|
280
280
|
raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{@ole_workbook.Fullname.tr('\\','/')}"
|
281
|
-
else
|
281
|
+
else
|
282
282
|
@ole_workbook.Close
|
283
283
|
@ole_workbook = nil
|
284
284
|
open_or_create_workbook(file, options)
|
285
285
|
end
|
286
|
-
when :new_excel
|
286
|
+
when :new_excel
|
287
287
|
@excel = excel_class.new(:reuse => false)
|
288
288
|
open_or_create_workbook(file, options)
|
289
289
|
else
|
@@ -291,7 +291,7 @@ module RobustExcelOle
|
|
291
291
|
end
|
292
292
|
else
|
293
293
|
# book open, not obstructed by an other book, but not saved and writable
|
294
|
-
|
294
|
+
unless @ole_workbook.Saved
|
295
295
|
case options[:if_unsaved]
|
296
296
|
when :raise
|
297
297
|
raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(file).inspect}"
|
@@ -320,9 +320,9 @@ module RobustExcelOle
|
|
320
320
|
private
|
321
321
|
|
322
322
|
def open_or_create_workbook(file, options) # :nodoc: #
|
323
|
-
if
|
323
|
+
if !@ole_workbook || (options[:if_unsaved] == :alert) || options[:if_obstructed]
|
324
324
|
begin
|
325
|
-
filename = General
|
325
|
+
filename = General.absolute_path(file)
|
326
326
|
begin
|
327
327
|
workbooks = @excel.Workbooks
|
328
328
|
rescue WIN32OLERuntimeError => msg
|
@@ -330,7 +330,7 @@ module RobustExcelOle
|
|
330
330
|
end
|
331
331
|
begin
|
332
332
|
with_workaround_linked_workbooks_excel2007(options) do
|
333
|
-
workbooks.Open(filename, { 'ReadOnly' => options[:read_only]
|
333
|
+
workbooks.Open(filename, { 'ReadOnly' => options[:read_only],
|
334
334
|
'UpdateLinks' => updatelinks_vba(options[:update_links]) })
|
335
335
|
end
|
336
336
|
rescue WIN32OLERuntimeError => msg
|
@@ -345,9 +345,9 @@ module RobustExcelOle
|
|
345
345
|
rescue WIN32OLERuntimeError => msg
|
346
346
|
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
|
347
347
|
end
|
348
|
-
if options[:force][:visible].nil? &&
|
349
|
-
if @excel.created
|
350
|
-
self.visible = options[:default][:visible]
|
348
|
+
if options[:force][:visible].nil? && !options[:default][:visible].nil?
|
349
|
+
if @excel.created
|
350
|
+
self.visible = options[:default][:visible]
|
351
351
|
else
|
352
352
|
self.window_visible = options[:default][:visible]
|
353
353
|
end
|
@@ -359,43 +359,43 @@ module RobustExcelOle
|
|
359
359
|
self.Saved = true # unless self.Saved # ToDo: this is too hard
|
360
360
|
rescue WIN32OLERuntimeError => msg
|
361
361
|
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
|
362
|
-
end
|
362
|
+
end
|
363
363
|
end
|
364
364
|
end
|
365
365
|
end
|
366
366
|
|
367
|
-
# translating the option UpdateLinks from REO to VBA
|
367
|
+
# translating the option UpdateLinks from REO to VBA
|
368
368
|
# setting UpdateLinks works only if calculation mode is automatic,
|
369
369
|
# parameter 'UpdateLinks' has no effect
|
370
370
|
def updatelinks_vba(updatelinks_reo)
|
371
371
|
case updatelinks_reo
|
372
|
-
when :alert
|
373
|
-
when :never
|
374
|
-
when :always
|
372
|
+
when :alert then RobustExcelOle::XlUpdateLinksUserSetting
|
373
|
+
when :never then RobustExcelOle::XlUpdateLinksNever
|
374
|
+
when :always then RobustExcelOle::XlUpdateLinksAlways
|
375
375
|
else RobustExcelOle::XlUpdateLinksNever
|
376
376
|
end
|
377
377
|
end
|
378
378
|
|
379
|
-
# workaround for linked workbooks for Excel 2007:
|
379
|
+
# workaround for linked workbooks for Excel 2007:
|
380
380
|
# opening and closing a dummy workbook if Excel has no workbooks.
|
381
381
|
# delay: with visible: 0.2 sec, without visible almost none
|
382
382
|
def with_workaround_linked_workbooks_excel2007(options)
|
383
383
|
old_visible_value = @excel.Visible
|
384
384
|
workbooks = @excel.Workbooks
|
385
|
-
workaround_condition = @excel.Version.split(
|
385
|
+
workaround_condition = @excel.Version.split('.').first.to_i == 12 && workbooks.Count == 0
|
386
386
|
if workaround_condition
|
387
|
-
workbooks.Add
|
388
|
-
@excel.calculation = options[:calculation].nil? ? @excel.calculation : options[:calculation]
|
387
|
+
workbooks.Add
|
388
|
+
@excel.calculation = options[:calculation].nil? ? @excel.calculation : options[:calculation]
|
389
389
|
end
|
390
390
|
begin
|
391
|
-
|
391
|
+
# @excel.with_displayalerts(update_links_opt == :alert ? true : @excel.displayalerts) do
|
392
392
|
yield self
|
393
393
|
ensure
|
394
|
-
@excel.with_displayalerts(false){workbooks.Item(1).Close} if workaround_condition
|
395
|
-
@excel.visible = old_visible_value
|
394
|
+
@excel.with_displayalerts(false) { workbooks.Item(1).Close } if workaround_condition
|
395
|
+
@excel.visible = old_visible_value
|
396
396
|
end
|
397
397
|
end
|
398
|
-
|
398
|
+
|
399
399
|
public
|
400
400
|
|
401
401
|
# closes the workbook, if it is alive
|
@@ -403,15 +403,15 @@ module RobustExcelOle
|
|
403
403
|
# @option opts [Symbol] :if_unsaved :raise (default), :save, :forget, :keep_open, or :alert
|
404
404
|
# options:
|
405
405
|
# :if_unsaved if the workbook is unsaved
|
406
|
-
# :raise -> raises an exception
|
407
|
-
# :save -> saves the workbook before it is closed
|
408
|
-
# :forget -> closes the workbook
|
406
|
+
# :raise -> raises an exception
|
407
|
+
# :save -> saves the workbook before it is closed
|
408
|
+
# :forget -> closes the workbook
|
409
409
|
# :keep_open -> keep the workbook open
|
410
410
|
# :alert or :excel -> gives control to excel
|
411
411
|
# @raise WorkbookNotSaved if the option :if_unsaved is :raise and the workbook is unsaved
|
412
412
|
# @raise OptionInvalid if the options is invalid
|
413
413
|
def close(opts = {:if_unsaved => :raise})
|
414
|
-
if
|
414
|
+
if alive? && !@ole_workbook.Saved && writable
|
415
415
|
case opts[:if_unsaved]
|
416
416
|
when :raise
|
417
417
|
raise WorkbookNotSaved, "workbook is unsaved: #{File.basename(self.stored_filename).inspect}"
|
@@ -430,13 +430,13 @@ module RobustExcelOle
|
|
430
430
|
else
|
431
431
|
close_workbook
|
432
432
|
end
|
433
|
-
#trace "close: canceled by user" if alive? &&
|
433
|
+
# trace "close: canceled by user" if alive? &&
|
434
434
|
# (opts[:if_unsaved] == :alert || opts[:if_unsaved] == :excel) && (not @ole_workbook.Saved)
|
435
435
|
end
|
436
436
|
|
437
437
|
private
|
438
438
|
|
439
|
-
def close_workbook
|
439
|
+
def close_workbook
|
440
440
|
@ole_workbook.Close if alive?
|
441
441
|
@ole_workbook = nil unless alive?
|
442
442
|
end
|
@@ -447,7 +447,7 @@ module RobustExcelOle
|
|
447
447
|
def retain_saved
|
448
448
|
saved = self.Saved
|
449
449
|
begin
|
450
|
-
|
450
|
+
yield self
|
451
451
|
ensure
|
452
452
|
self.Saved = saved
|
453
453
|
end
|
@@ -470,25 +470,25 @@ module RobustExcelOle
|
|
470
470
|
end
|
471
471
|
|
472
472
|
# allows to read or modify a workbook such that its state remains unchanged
|
473
|
-
# state comprises: open, saved, writable, visible, calculation mode, check compatibility
|
473
|
+
# state comprises: open, saved, writable, visible, calculation mode, check compatibility
|
474
474
|
# remarks: works only for workbooks opened with RobustExcelOle
|
475
475
|
# @param [String] file the file name
|
476
476
|
# @param [Hash] opts the options
|
477
477
|
# @option opts [Variant] :if_closed :current (default), :new or an Excel instance
|
478
478
|
# @option opts [Boolean] :read_only true/false, open the workbook in read-only/read-write modus (save changes)
|
479
|
-
# @option opts [Boolean] :writable true/false changes of the workbook shall be saved/not saved
|
479
|
+
# @option opts [Boolean] :writable true/false changes of the workbook shall be saved/not saved
|
480
480
|
# @option opts [Boolean] :rw_change_excel Excel instance in which the workbook with the new
|
481
|
-
# write permissions shall be opened :current (default), :new or an Excel instance
|
482
|
-
# @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening
|
481
|
+
# write permissions shall be opened :current (default), :new or an Excel instance
|
482
|
+
# @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening
|
483
483
|
# @return [Workbook] a workbook
|
484
|
-
def self.unobtrusively(file, opts = { }
|
484
|
+
def self.unobtrusively(file, opts = { })
|
485
485
|
opts = {:if_closed => :current,
|
486
486
|
:rw_change_excel => :current,
|
487
487
|
:keep_open => false}.merge(opts)
|
488
|
-
raise OptionInvalid,
|
489
|
-
prefer_writable = (((
|
490
|
-
(
|
491
|
-
do_not_write = (opts[:read_only]
|
488
|
+
raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
|
489
|
+
prefer_writable = ((!(opts[:read_only]) || opts[:writable] == true) &&
|
490
|
+
!(opts[:read_only].nil? && opts[:writable] == false))
|
491
|
+
do_not_write = (opts[:read_only] || (opts[:read_only].nil? && opts[:writable] == false))
|
492
492
|
book = bookstore.fetch(file, :prefer_writable => prefer_writable)
|
493
493
|
was_open = book && book.alive?
|
494
494
|
if was_open
|
@@ -497,17 +497,17 @@ module RobustExcelOle
|
|
497
497
|
was_visible = book.visible
|
498
498
|
was_calculation = book.calculation
|
499
499
|
was_check_compatibility = book.check_compatibility
|
500
|
-
if (
|
501
|
-
|
502
|
-
raise NotImplementedREOError,
|
500
|
+
if (opts[:writable] && !was_writable && !was_saved) ||
|
501
|
+
(opts[:read_only] && was_writable && !was_saved)
|
502
|
+
raise NotImplementedREOError, 'unsaved read-only workbook shall be written'
|
503
503
|
end
|
504
|
-
opts[:rw_change_excel] = book.excel if opts[:rw_change_excel]
|
505
|
-
end
|
506
|
-
change_rw_mode = ((opts[:read_only] && was_writable)
|
504
|
+
opts[:rw_change_excel] = book.excel if opts[:rw_change_excel] == :current
|
505
|
+
end
|
506
|
+
change_rw_mode = ((opts[:read_only] && was_writable) || (opts[:writable] && !was_writable))
|
507
507
|
begin
|
508
|
-
book =
|
509
|
-
if was_open
|
510
|
-
if change_rw_mode
|
508
|
+
book =
|
509
|
+
if was_open
|
510
|
+
if change_rw_mode
|
511
511
|
open(file, :force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write)
|
512
512
|
else
|
513
513
|
book
|
@@ -520,45 +520,45 @@ module RobustExcelOle
|
|
520
520
|
if book && book.alive?
|
521
521
|
book.save unless book.saved || do_not_write || book.ReadOnly
|
522
522
|
if was_open
|
523
|
-
if opts[:rw_change_excel]==book.excel && change_rw_mode
|
523
|
+
if opts[:rw_change_excel] == book.excel && change_rw_mode
|
524
524
|
book.close
|
525
|
-
book = open(file, :force => {:excel => opts[:rw_change_excel]}, :read_only =>
|
526
|
-
end
|
525
|
+
book = open(file, :force => {:excel => opts[:rw_change_excel]}, :read_only => !was_writable)
|
526
|
+
end
|
527
527
|
book.excel.calculation = was_calculation
|
528
528
|
book.CheckCompatibility = was_check_compatibility
|
529
|
-
#book.visible = was_visible # not necessary
|
529
|
+
# book.visible = was_visible # not necessary
|
530
530
|
end
|
531
|
-
book.Saved = (was_saved ||
|
531
|
+
book.Saved = (was_saved || !was_open)
|
532
532
|
book.close unless was_open || opts[:keep_open]
|
533
533
|
end
|
534
534
|
end
|
535
535
|
end
|
536
536
|
|
537
537
|
# reopens a closed workbook
|
538
|
-
# @options options
|
538
|
+
# @options options
|
539
539
|
def reopen(options = { })
|
540
540
|
book = self.class.open(@stored_filename, options)
|
541
|
-
raise WorkbookREOError(
|
541
|
+
raise WorkbookREOError('cannot reopen book') unless book && book.alive?
|
542
542
|
book
|
543
543
|
end
|
544
544
|
|
545
545
|
# simple save of a workbook.
|
546
546
|
# @option opts [Boolean] :discoloring states, whether colored ranges shall be discolored
|
547
547
|
# @return [Boolean] true, if successfully saved, nil otherwise
|
548
|
-
def save(opts = {:discoloring => false})
|
549
|
-
raise ObjectNotAlive,
|
550
|
-
raise WorkbookReadOnly,
|
548
|
+
def save(opts = {:discoloring => false})
|
549
|
+
raise ObjectNotAlive, 'workbook is not alive' unless alive?
|
550
|
+
raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
|
551
551
|
begin
|
552
|
-
discoloring if opts[:discoloring]
|
552
|
+
discoloring if opts[:discoloring]
|
553
553
|
@modified_cells = []
|
554
|
-
@ole_workbook.Save
|
554
|
+
@ole_workbook.Save
|
555
555
|
rescue WIN32OLERuntimeError => msg
|
556
|
-
if msg.message =~ /SaveAs/
|
557
|
-
raise WorkbookNotSaved,
|
556
|
+
if msg.message =~ /SaveAs/ && msg.message =~ /Workbook/
|
557
|
+
raise WorkbookNotSaved, 'workbook not saved'
|
558
558
|
else
|
559
559
|
raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
|
560
|
-
end
|
561
|
-
end
|
560
|
+
end
|
561
|
+
end
|
562
562
|
true
|
563
563
|
end
|
564
564
|
|
@@ -567,28 +567,28 @@ module RobustExcelOle
|
|
567
567
|
# @param [Hash] opts the options
|
568
568
|
# @option opts [Symbol] :if_exists :raise (default), :overwrite, or :alert, :excel
|
569
569
|
# @option opts [Symbol] :if_obstructed :raise (default), :forget, :save, or :close_if_saved
|
570
|
-
# options:
|
571
|
-
# :if_exists if a file with the same name exists, then
|
570
|
+
# options:
|
571
|
+
# :if_exists if a file with the same name exists, then
|
572
572
|
# :raise -> raises an exception, dont't write the file (default)
|
573
573
|
# :overwrite -> writes the file, delete the old file
|
574
574
|
# :alert or :excel -> gives control to Excel
|
575
575
|
# :if_obstructed if a workbook with the same name and different path is already open and blocks the saving, then
|
576
|
-
# :raise -> raises an exception
|
576
|
+
# :raise -> raises an exception
|
577
577
|
# :forget -> closes the blocking workbook
|
578
578
|
# :save -> saves the blocking workbook and closes it
|
579
|
-
# :close_if_saved -> closes the blocking workbook, if it is saved,
|
579
|
+
# :close_if_saved -> closes the blocking workbook, if it is saved,
|
580
580
|
# otherwise raises an exception
|
581
581
|
# :discoloring states, whether colored ranges shall be discolored
|
582
582
|
# @return [Workbook], the book itself, if successfully saved, raises an exception otherwise
|
583
|
-
def save_as(file, opts = { }
|
584
|
-
raise FileNameNotGiven,
|
585
|
-
raise ObjectNotAlive,
|
586
|
-
raise WorkbookReadOnly,
|
583
|
+
def save_as(file, opts = { })
|
584
|
+
raise FileNameNotGiven, 'filename is nil' if file.nil?
|
585
|
+
raise ObjectNotAlive, 'workbook is not alive' unless alive?
|
586
|
+
raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
|
587
587
|
options = {
|
588
588
|
:if_exists => :raise,
|
589
|
-
:if_obstructed => :raise
|
589
|
+
:if_obstructed => :raise
|
590
590
|
}.merge(opts)
|
591
|
-
if File.exist?(file)
|
591
|
+
if File.exist?(file)
|
592
592
|
case options[:if_exists]
|
593
593
|
when :overwrite
|
594
594
|
if file == self.filename
|
@@ -598,10 +598,10 @@ module RobustExcelOle
|
|
598
598
|
begin
|
599
599
|
File.delete(file)
|
600
600
|
rescue Errno::EACCES
|
601
|
-
raise WorkbookBeingUsed,
|
601
|
+
raise WorkbookBeingUsed, 'workbook is open and used in Excel'
|
602
602
|
end
|
603
603
|
end
|
604
|
-
when :alert, :excel
|
604
|
+
when :alert, :excel
|
605
605
|
@excel.with_displayalerts true do
|
606
606
|
save_as_workbook(file, options)
|
607
607
|
end
|
@@ -613,7 +613,7 @@ module RobustExcelOle
|
|
613
613
|
end
|
614
614
|
end
|
615
615
|
other_workbook = @excel.Workbooks.Item(File.basename(file)) rescue nil
|
616
|
-
if other_workbook &&
|
616
|
+
if other_workbook && self.filename != other_workbook.Fullname.tr('\\','/')
|
617
617
|
case options[:if_obstructed]
|
618
618
|
when :raise
|
619
619
|
raise WorkbookBlocked, "blocked by another workbook: #{other_workbook.Fullname.tr('\\','/')}"
|
@@ -635,29 +635,27 @@ module RobustExcelOle
|
|
635
635
|
private
|
636
636
|
|
637
637
|
def discoloring
|
638
|
-
@modified_cells.each{|cell| cell.Interior.ColorIndex = XlNone}
|
638
|
+
@modified_cells.each { |cell| cell.Interior.ColorIndex = XlNone }
|
639
639
|
end
|
640
640
|
|
641
641
|
def save_as_workbook(file, options) # :nodoc: #
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
if
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
raise UnexpectedREOError, "unknown WIN32OELERuntimeError:\n#{msg.message}"
|
660
|
-
end
|
642
|
+
dirname, basename = File.split(file)
|
643
|
+
file_format =
|
644
|
+
case File.extname(basename)
|
645
|
+
when '.xls' then RobustExcelOle::XlExcel8
|
646
|
+
when '.xlsx' then RobustExcelOle::XlOpenXMLWorkbook
|
647
|
+
when '.xlsm' then RobustExcelOle::XlOpenXMLWorkbookMacroEnabled
|
648
|
+
end
|
649
|
+
discoloring if options[:discoloring]
|
650
|
+
@modified_cells = []
|
651
|
+
@ole_workbook.SaveAs(General.absolute_path(file), file_format)
|
652
|
+
bookstore.store(self)
|
653
|
+
rescue WIN32OLERuntimeError => msg
|
654
|
+
if msg.message =~ /SaveAs/ && msg.message =~ /Workbook/
|
655
|
+
# trace "save: canceled by user" if options[:if_exists] == :alert || options[:if_exists] == :excel
|
656
|
+
# another possible semantics. raise WorkbookREOError, "could not save Workbook"
|
657
|
+
else
|
658
|
+
raise UnexpectedREOError, "unknown WIN32OELERuntimeError:\n#{msg.message}"
|
661
659
|
end
|
662
660
|
end
|
663
661
|
|
@@ -686,12 +684,10 @@ module RobustExcelOle
|
|
686
684
|
# @param [String] or [Number]
|
687
685
|
# @returns [Worksheet]
|
688
686
|
def sheet(name)
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
end
|
694
|
-
end
|
687
|
+
worksheet_class.new(@ole_workbook.Worksheets.Item(name))
|
688
|
+
rescue WIN32OLERuntimeError => msg
|
689
|
+
raise NameNotFound, "could not return a sheet with name #{name.inspect}"
|
690
|
+
end
|
695
691
|
|
696
692
|
def each
|
697
693
|
@ole_workbook.Worksheets.each do |sheet|
|
@@ -706,7 +702,7 @@ module RobustExcelOle
|
|
706
702
|
i += 1
|
707
703
|
end
|
708
704
|
end
|
709
|
-
|
705
|
+
|
710
706
|
# copies a sheet to another position
|
711
707
|
# default: copied sheet is appended
|
712
708
|
# @param [Worksheet] sheet a sheet that shall be copied
|
@@ -723,7 +719,7 @@ module RobustExcelOle
|
|
723
719
|
new_sheet = worksheet_class.new(@excel.Activesheet)
|
724
720
|
new_sheet.name = new_sheet_name if new_sheet_name
|
725
721
|
new_sheet
|
726
|
-
end
|
722
|
+
end
|
727
723
|
|
728
724
|
# adds an empty sheet
|
729
725
|
# default: empty sheet is appended
|
@@ -740,7 +736,7 @@ module RobustExcelOle
|
|
740
736
|
new_sheet = worksheet_class.new(@excel.Activesheet)
|
741
737
|
new_sheet.name = new_sheet_name if new_sheet_name
|
742
738
|
new_sheet
|
743
|
-
end
|
739
|
+
end
|
744
740
|
|
745
741
|
# copies a sheet to another position if a sheet is given, or adds an empty sheet
|
746
742
|
# default: copied or empty sheet is appended, i.e. added behind the last sheet
|
@@ -756,12 +752,12 @@ module RobustExcelOle
|
|
756
752
|
sheet = nil
|
757
753
|
end
|
758
754
|
sheet ? copy_sheet(sheet, opts) : add_empty_sheet(opts)
|
759
|
-
end
|
755
|
+
end
|
760
756
|
|
761
757
|
# for compatibility to older versions
|
762
758
|
def add_sheet(sheet = nil, opts = { })
|
763
759
|
add_or_copy_sheet(sheet, opts)
|
764
|
-
end
|
760
|
+
end
|
765
761
|
|
766
762
|
def last_sheet
|
767
763
|
worksheet_class.new(@ole_workbook.Worksheets.Item(@ole_workbook.Worksheets.Count))
|
@@ -794,12 +790,12 @@ module RobustExcelOle
|
|
794
790
|
check_compatibility_before = check_compatibility
|
795
791
|
unless opts[:read_only].nil?
|
796
792
|
# if the ReadOnly status shall be changed, then close and reopen it
|
797
|
-
if (
|
798
|
-
opts[:check_compatibility] = check_compatibility if opts[:check_compatibility].nil?
|
799
|
-
close(:if_unsaved => true)
|
793
|
+
if (!writable && !(opts[:read_only])) || (writable && opts[:read_only])
|
794
|
+
opts[:check_compatibility] = check_compatibility if opts[:check_compatibility].nil?
|
795
|
+
close(:if_unsaved => true)
|
800
796
|
open_or_create_workbook(@stored_filename, opts)
|
801
797
|
end
|
802
|
-
end
|
798
|
+
end
|
803
799
|
self.visible = opts[:force][:visible].nil? ? visible_before : opts[:force][:visible]
|
804
800
|
self.CheckCompatibility = opts[:check_compatibility].nil? ? check_compatibility_before : opts[:check_compatibility]
|
805
801
|
@excel.calculation = opts[:calculation] unless opts[:calculation].nil?
|
@@ -814,14 +810,12 @@ module RobustExcelOle
|
|
814
810
|
|
815
811
|
# returns true, if the workbook reacts to methods, false otherwise
|
816
812
|
def alive?
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
false
|
824
|
-
end
|
813
|
+
@ole_workbook.Name
|
814
|
+
true
|
815
|
+
rescue
|
816
|
+
@ole_workbook = nil # dead object won't be alive again
|
817
|
+
# t $!.message
|
818
|
+
false
|
825
819
|
end
|
826
820
|
|
827
821
|
# returns the full file name of the workbook
|
@@ -830,7 +824,7 @@ module RobustExcelOle
|
|
830
824
|
end
|
831
825
|
|
832
826
|
def writable # :nodoc: #
|
833
|
-
|
827
|
+
!@ole_workbook.ReadOnly if @ole_workbook
|
834
828
|
end
|
835
829
|
|
836
830
|
def saved # :nodoc: #
|
@@ -845,7 +839,7 @@ module RobustExcelOle
|
|
845
839
|
@ole_workbook.CheckCompatibility if @ole_workbook
|
846
840
|
end
|
847
841
|
|
848
|
-
|
842
|
+
# returns true, if the workbook is visible, false otherwise
|
849
843
|
def visible
|
850
844
|
@excel.visible && @ole_workbook.Windows(@ole_workbook.Name).Visible
|
851
845
|
end
|
@@ -859,7 +853,7 @@ module RobustExcelOle
|
|
859
853
|
|
860
854
|
# returns true, if the window of the workbook is set to visible, false otherwise
|
861
855
|
def window_visible
|
862
|
-
|
856
|
+
@ole_workbook.Windows(@ole_workbook.Name).Visible
|
863
857
|
end
|
864
858
|
|
865
859
|
# makes the window of the workbook visible or invisible
|
@@ -870,11 +864,11 @@ module RobustExcelOle
|
|
870
864
|
end
|
871
865
|
end
|
872
866
|
|
873
|
-
# @return [Boolean] true, if the full book names and excel Instances are identical, false otherwise
|
867
|
+
# @return [Boolean] true, if the full book names and excel Instances are identical, false otherwise
|
874
868
|
def == other_book
|
875
869
|
other_book.is_a?(Workbook) &&
|
876
|
-
|
877
|
-
|
870
|
+
@excel == other_book.excel &&
|
871
|
+
self.filename == other_book.filename
|
878
872
|
end
|
879
873
|
|
880
874
|
def self.books
|
@@ -887,14 +881,14 @@ module RobustExcelOle
|
|
887
881
|
|
888
882
|
def bookstore # :nodoc: #
|
889
883
|
self.class.bookstore
|
890
|
-
end
|
884
|
+
end
|
891
885
|
|
892
886
|
def to_s # :nodoc: #
|
893
|
-
|
887
|
+
self.filename.to_s
|
894
888
|
end
|
895
889
|
|
896
890
|
def inspect # :nodoc: #
|
897
|
-
|
891
|
+
'#<Workbook: ' + ('not alive ' unless alive?).to_s + (File.basename(self.filename) if alive?).to_s + " #{@ole_workbook} #{@excel}" + '>'
|
898
892
|
end
|
899
893
|
|
900
894
|
def self.excel_class # :nodoc: #
|
@@ -902,7 +896,7 @@ module RobustExcelOle
|
|
902
896
|
module_name = self.parent_name
|
903
897
|
"#{module_name}::Excel".constantize
|
904
898
|
rescue NameError => e
|
905
|
-
#trace "excel_class: NameError: #{e}"
|
899
|
+
# trace "excel_class: NameError: #{e}"
|
906
900
|
Excel
|
907
901
|
end
|
908
902
|
end
|
@@ -929,23 +923,23 @@ module RobustExcelOle
|
|
929
923
|
private
|
930
924
|
|
931
925
|
def method_missing(name, *args) # :nodoc: #
|
932
|
-
if name.to_s[0,1] =~ /[A-Z]/
|
926
|
+
if name.to_s[0,1] =~ /[A-Z]/
|
933
927
|
begin
|
934
|
-
raise ObjectNotAlive,
|
928
|
+
raise ObjectNotAlive, 'method missing: workbook not alive' unless alive?
|
935
929
|
@ole_workbook.send(name, *args)
|
936
930
|
rescue WIN32OLERuntimeError => msg
|
937
931
|
if msg.message =~ /unknown property or method/
|
938
932
|
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
939
|
-
else
|
933
|
+
else
|
940
934
|
raise msg
|
941
935
|
end
|
942
936
|
end
|
943
|
-
else
|
944
|
-
super
|
937
|
+
else
|
938
|
+
super
|
945
939
|
end
|
946
940
|
end
|
947
941
|
end
|
948
|
-
|
942
|
+
|
949
943
|
public
|
950
944
|
|
951
945
|
Book = Workbook
|