robust_excel_ole 1.11 → 1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog +11 -0
- data/README.rdoc +20 -8
- data/docs/README_open.rdoc +1 -0
- data/docs/README_ranges.rdoc +5 -11
- data/examples/{introducing_examples/example_introducing.rb → introductory_examples/example_introductory.rb} +10 -2
- data/examples/{introducing_examples → introductory_examples}/example_open.rb +18 -17
- data/examples/{introducing_examples → introductory_examples}/example_range.rb +29 -16
- data/examples/modifying_sheets/example_access_sheets_and_cells.rb +8 -7
- data/examples/modifying_sheets/example_add_names.rb +4 -8
- data/examples/modifying_sheets/example_adding_sheets.rb +7 -6
- data/examples/modifying_sheets/example_concating.rb +2 -2
- data/examples/modifying_sheets/example_copying.rb +3 -3
- data/examples/modifying_sheets/example_expanding.rb +2 -2
- data/examples/modifying_sheets/example_naming.rb +2 -2
- data/examples/modifying_sheets/example_ranges.rb +4 -4
- data/examples/modifying_sheets/example_saving.rb +3 -3
- data/examples/open_save_close/example_control_to_excel.rb +10 -11
- data/examples/open_save_close/example_default_excel.rb +13 -14
- data/examples/open_save_close/example_force_excel.rb +9 -10
- data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +7 -7
- data/examples/open_save_close/example_if_obstructed_forget.rb +5 -5
- data/examples/open_save_close/example_if_obstructed_save.rb +7 -7
- data/examples/open_save_close/example_if_unsaved_accept.rb +13 -13
- data/examples/open_save_close/example_if_unsaved_forget.rb +9 -10
- data/examples/open_save_close/example_if_unsaved_forget_more.rb +9 -10
- data/examples/open_save_close/example_read_only.rb +6 -6
- data/examples/open_save_close/example_rename_cells.rb +4 -5
- data/examples/open_save_close/example_reuse.rb +6 -6
- data/examples/open_save_close/example_simple.rb +5 -5
- data/examples/open_save_close/example_unobtrusively.rb +4 -4
- data/lib/robust_excel_ole/address.rb +0 -4
- data/lib/robust_excel_ole/bookstore.rb +4 -28
- data/lib/robust_excel_ole/excel.rb +17 -22
- data/lib/robust_excel_ole/general.rb +11 -18
- data/lib/robust_excel_ole/range_owners.rb +17 -27
- data/lib/robust_excel_ole/reo_common.rb +7 -3
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +178 -180
- data/lib/robust_excel_ole/worksheet.rb +7 -4
- data/robust_excel_ole.gemspec +6 -4
- data/spec/bookstore_spec.rb +38 -34
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/excel_spec.rb +89 -44
- data/spec/general_spec.rb +1 -0
- data/spec/range_spec.rb +7 -4
- data/spec/workbook_specs/workbook_close_spec.rb +2 -1
- data/spec/workbook_specs/workbook_misc_spec.rb +34 -18
- data/spec/workbook_specs/workbook_open_spec.rb +112 -71
- data/spec/workbook_specs/workbook_save_spec.rb +173 -5
- data/spec/workbook_specs/workbook_sheet_spec.rb +6 -42
- data/spec/workbook_specs/workbook_unobtr_spec.rb +9 -246
- data/spec/worksheet_spec.rb +21 -5
- metadata +12 -11
@@ -75,13 +75,17 @@ module RobustExcelOle
|
|
75
75
|
end
|
76
76
|
|
77
77
|
# @private
|
78
|
-
class
|
78
|
+
class WorkbookConnectingUnsavedError < WorkbookREOError
|
79
79
|
end
|
80
80
|
|
81
81
|
# @private
|
82
82
|
class WorkbookConnectingBlockingError < WorkbookREOError
|
83
83
|
end
|
84
84
|
|
85
|
+
# @private
|
86
|
+
class WorkbookConnectingUnknownError < WorkbookREOError
|
87
|
+
end
|
88
|
+
|
85
89
|
# @private
|
86
90
|
class FileNotFound < FileREOError
|
87
91
|
end
|
@@ -169,8 +173,8 @@ module RobustExcelOle
|
|
169
173
|
home = homes.find { |h| !ENV[h].nil? }
|
170
174
|
reo_log_dir = ENV[home]
|
171
175
|
else
|
172
|
-
reo_log_dir = REO_LOG_DIR
|
173
|
-
|
176
|
+
#reo_log_dir = REO_LOG_DIR
|
177
|
+
reo_log_dir = "C:/Users/User"
|
174
178
|
end
|
175
179
|
File.open(reo_log_dir + '/' + REO_LOG_FILE,'a') do |file|
|
176
180
|
file.puts text
|
@@ -14,7 +14,7 @@ module RobustExcelOle
|
|
14
14
|
attr_accessor :excel
|
15
15
|
attr_accessor :ole_workbook
|
16
16
|
attr_accessor :stored_filename
|
17
|
-
attr_accessor :
|
17
|
+
attr_accessor :color_if_modified
|
18
18
|
attr_reader :workbook
|
19
19
|
|
20
20
|
alias ole_object ole_workbook
|
@@ -25,9 +25,14 @@ module RobustExcelOle
|
|
25
25
|
:if_unsaved => :raise,
|
26
26
|
:if_obstructed => :raise,
|
27
27
|
:if_absent => :raise,
|
28
|
-
|
28
|
+
#:read_only => false,
|
29
29
|
:check_compatibility => false,
|
30
|
-
:update_links => :never
|
30
|
+
:update_links => :never,
|
31
|
+
:if_exists => :raise
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
CORE_DEFAULT_OPTEN_OPTS = {
|
35
|
+
:default => {:excel => :current}, :force => {}, :update_links => :never
|
31
36
|
}.freeze
|
32
37
|
|
33
38
|
ABBREVIATIONS = [[:default,:d], [:force, :f], [:excel, :e], [:visible, :v],
|
@@ -39,8 +44,8 @@ module RobustExcelOle
|
|
39
44
|
# @param [Hash] opts the options
|
40
45
|
# @option opts [Hash] :default or :d
|
41
46
|
# @option opts [Hash] :force or :f
|
42
|
-
# @option opts [Symbol] :if_unsaved :raise (default), :forget, :accept, :alert, :excel, or :new_excel
|
43
|
-
# @option opts [Symbol] :
|
47
|
+
# @option opts [Symbol] :if_unsaved :raise (default), :forget, :save, :accept, :alert, :excel, or :new_excel
|
48
|
+
# @option opts [Symbol] :if_blocked :raise (default), :forget, :save, :close_if_saved, or _new_excel
|
44
49
|
# @option opts [Symbol] :if_absent :raise (default) or :create
|
45
50
|
# @option opts [Boolean] :read_only true (default) or false
|
46
51
|
# @option opts [Boolean] :update_links :never (default), :always, :alert
|
@@ -49,7 +54,7 @@ module RobustExcelOle
|
|
49
54
|
# :default : if the workbook was already open before, then use (unchange) its properties,
|
50
55
|
# otherwise, i.e. if the workbook cannot be reopened, use the properties stated in :default
|
51
56
|
# :force : no matter whether the workbook was already open before, use the properties stated in :force
|
52
|
-
# :default and :force contain: :excel
|
57
|
+
# :default and :force contain: :excel
|
53
58
|
# :excel :current (or :active or :reuse)
|
54
59
|
# -> connects to a running (the first opened) Excel instance,
|
55
60
|
# excluding the hidden Excel instance, if it exists,
|
@@ -81,14 +86,13 @@ module RobustExcelOle
|
|
81
86
|
def self.open(file, opts = { }, &block)
|
82
87
|
raise(FileNameNotGiven, 'filename is nil') if file.nil?
|
83
88
|
raise(FileNotFound, "file #{General.absolute_path(file).inspect} is a directory") if File.directory?(file)
|
84
|
-
options = process_options(opts)
|
89
|
+
options = process_options(opts, :use_defaults => false)
|
85
90
|
book = nil
|
86
91
|
if options[:force][:excel] != :new
|
87
|
-
# if readonly is true, then prefer a book that is given in force_excel if this option is set
|
88
|
-
forced_excel =
|
92
|
+
# if readonly is true, then prefer a book that is given in force_excel if this option is set
|
93
|
+
forced_excel =
|
89
94
|
(options[:force][:excel].nil? || options[:force][:excel] == :current) ?
|
90
|
-
excel_class.new(:reuse => true) : excel_of(options[:force][:excel])
|
91
|
-
end
|
95
|
+
(excel_class.new(:reuse => true) if RUBY_PLATFORM !~ /java/) : excel_of(options[:force][:excel])
|
92
96
|
begin
|
93
97
|
book = if File.exists?(file)
|
94
98
|
bookstore.fetch(file, :prefer_writable => !(options[:read_only]),
|
@@ -97,22 +101,13 @@ module RobustExcelOle
|
|
97
101
|
rescue
|
98
102
|
trace "#{$!.message}"
|
99
103
|
end
|
100
|
-
if book
|
101
|
-
if
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
(book.writable && options[:read_only]))
|
108
|
-
book.save if book.writable && !book.saved
|
109
|
-
book.close(:if_unsaved => :forget)
|
110
|
-
end
|
111
|
-
# reopens the book if it was closed
|
112
|
-
book.ensure_workbook(file,options) unless book.alive?
|
113
|
-
book.visible = options[:force][:visible] unless options[:force][:visible].nil?
|
114
|
-
book.CheckCompatibility = options[:check_compatibility] unless options[:check_compatibility].nil?
|
115
|
-
book.excel.calculation = options[:calculation] unless options[:calculation].nil?
|
104
|
+
if book
|
105
|
+
# drop the fetched workbook if it shall be opened in another Excel instance
|
106
|
+
# or the workbook is an unsaved workbook that should not be accepted
|
107
|
+
if (options[:force][:excel].nil? || options[:force][:excel] == :current || forced_excel == book.excel) &&
|
108
|
+
!(book.alive? && !book.saved && (options[:if_unsaved] != :accept))
|
109
|
+
options[:force][:excel] = book.excel if book.excel && book.excel.alive?
|
110
|
+
book.ensure_workbook(file,options)
|
116
111
|
return book
|
117
112
|
end
|
118
113
|
end
|
@@ -134,11 +129,11 @@ module RobustExcelOle
|
|
134
129
|
# use the Excel instance where the workbook is opened
|
135
130
|
ole_excel = WIN32OLE.connect(workbook.Fullname).Application rescue nil
|
136
131
|
@excel = excel_class.new(ole_excel)
|
137
|
-
|
138
|
-
|
132
|
+
filename = @ole_workbook.Fullname.tr('\\','/')
|
133
|
+
set_options(filename, options)
|
139
134
|
else
|
140
|
-
filename = file_or_workbook
|
141
|
-
ensure_excel(options)
|
135
|
+
filename = file_or_workbook
|
136
|
+
#ensure_excel(options) #unless RUBY_PLATFORM =~ /java/ && (options[:force][:excel].nil? || options[:force][:excel] == :current)
|
142
137
|
ensure_workbook(filename, options)
|
143
138
|
end
|
144
139
|
bookstore.store(self)
|
@@ -175,8 +170,8 @@ module RobustExcelOle
|
|
175
170
|
erg[new_key] = value
|
176
171
|
end
|
177
172
|
end
|
178
|
-
erg[:default]
|
179
|
-
erg[:force]
|
173
|
+
erg[:default] ||= {}
|
174
|
+
erg[:force] ||= {}
|
180
175
|
force_list = [:visible, :excel]
|
181
176
|
erg.each { |key,value| erg[:force][key] = value if force_list.include?(key) }
|
182
177
|
erg[:default][:excel] = erg[:default_excel] unless erg[:default_excel].nil?
|
@@ -186,8 +181,7 @@ module RobustExcelOle
|
|
186
181
|
erg
|
187
182
|
end
|
188
183
|
opts = translator.call(options)
|
189
|
-
default_open_opts = proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS :
|
190
|
-
{:default => {:excel => :current}, :force => {}, :update_links => :never }
|
184
|
+
default_open_opts = proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS : CORE_DEFAULT_OPTEN_OPTS
|
191
185
|
default_opts = translator.call(default_open_opts)
|
192
186
|
opts = default_opts.merge(opts)
|
193
187
|
opts[:default] = default_opts[:default].merge(opts[:default]) unless opts[:default].nil?
|
@@ -198,21 +192,11 @@ module RobustExcelOle
|
|
198
192
|
# returns an Excel object when given Excel, Workbook or Win32ole object representing a Workbook or an Excel
|
199
193
|
# @private
|
200
194
|
def self.excel_of(object)
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
excel_class.new(object)
|
207
|
-
else
|
208
|
-
object.excel
|
209
|
-
end
|
210
|
-
else
|
211
|
-
begin
|
212
|
-
object.excel
|
213
|
-
rescue
|
214
|
-
raise TypeREOError, 'given object is neither an Excel, a Workbook, nor a Win32ole'
|
215
|
-
end
|
195
|
+
begin
|
196
|
+
object = object.to_reo if object.is_a? WIN32OLE
|
197
|
+
object.excel
|
198
|
+
rescue
|
199
|
+
raise TypeREOError, 'given object is neither an Excel, a Workbook, nor a Win32ole'
|
216
200
|
end
|
217
201
|
end
|
218
202
|
|
@@ -221,50 +205,58 @@ module RobustExcelOle
|
|
221
205
|
# @private
|
222
206
|
# ensures an excel but not for jruby if current Excel shall be used
|
223
207
|
def ensure_excel(options)
|
208
|
+
return if @excel && @excel.alive?
|
224
209
|
excel_option = options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
|
225
210
|
@excel = if excel_option == :new
|
226
211
|
excel_class.new(:reuse => false)
|
227
212
|
elsif excel_option.nil? || excel_option == :current
|
228
|
-
excel_class.new(:reuse => true) unless RUBY_PLATFORM =~ /java/
|
213
|
+
excel_class.new(:reuse => true) # unless RUBY_PLATFORM =~ /java/
|
229
214
|
else #elsif excel_options.is_a?(WIN32OLE) || excel_option.is_a?(Excel) #excel_option.respond_to?(:Hwnd)
|
230
215
|
self.class.excel_of(excel_option)
|
231
216
|
end
|
232
|
-
|
233
|
-
@excel.visible = options[:force][:visible] unless options[:force][:visible].nil?
|
234
|
-
@excel.calculation = options[:calculation] unless options[:calculation].nil?
|
235
|
-
end
|
217
|
+
raise ExcelREOError, "excel is not alive" unless @excel && @excel.alive?
|
236
218
|
end
|
237
219
|
|
238
|
-
|
239
220
|
# @private
|
240
221
|
# restriction for jruby: does not manage conflicts with blocking or unsaved workbooks
|
241
222
|
def ensure_workbook(filename, options)
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
"\nbecause it is being blocked by a workbook with the same name in a different path."
|
253
|
-
end
|
254
|
-
else
|
255
|
-
workbooks = @excel.Workbooks
|
256
|
-
@ole_workbook = workbooks.Item(File.basename(filename)) rescue nil if @ole_workbook.nil?
|
257
|
-
if @ole_workbook
|
258
|
-
manage_blocking_or_unsaved_workbook(filename,options)
|
259
|
-
else
|
260
|
-
if options[:force][:excel].nil? || options[:force][:excel] == :current
|
223
|
+
if options[:if_unsaved]==:accept && options[:read_only]==true
|
224
|
+
raise OptionInvalid, ":if_unsaved:accept and :read_only:true not possible"
|
225
|
+
end
|
226
|
+
unless @ole_workbook && alive?
|
227
|
+
filename = @stored_filename ? @stored_filename : filename
|
228
|
+
manage_nonexisting_file(filename,options)
|
229
|
+
excel_option = options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
|
230
|
+
if RUBY_PLATFORM =~ /java/ &&
|
231
|
+
(excel_option.nil? || excel_option == :current) && filename[0] != '/'
|
232
|
+
begin
|
261
233
|
connect(filename,options)
|
262
|
-
|
263
|
-
|
234
|
+
rescue WorkbookConnectingUnsavedError
|
235
|
+
raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(filename).inspect}"
|
236
|
+
rescue WorkbookConnectingBlockingError
|
237
|
+
raise WorkbookBlocked, "can't open workbook #{filename}"+
|
238
|
+
"\nbecause it is being blocked by a workbook with the same name in a different path."
|
239
|
+
rescue WorkbookConnectingUnknownError
|
240
|
+
raise WorkbookREOError, "can't connect to workbook #{filename}"
|
264
241
|
end
|
265
|
-
|
242
|
+
manage_unsaved_workbook(filename,options) unless @ole_workbook.Saved
|
243
|
+
else
|
244
|
+
ensure_excel(options)
|
245
|
+
workbooks = @excel.Workbooks
|
246
|
+
@ole_workbook = workbooks.Item(File.basename(filename)) rescue nil if @ole_workbook.nil?
|
247
|
+
if @ole_workbook
|
248
|
+
manage_blocking_or_unsaved_workbook(filename,options)
|
249
|
+
else
|
250
|
+
if excel_option.nil? || excel_option == :current &&
|
251
|
+
(RUBY_PLATFORM !~ /java/ || filename[0] != '/')
|
252
|
+
connect(filename,options)
|
253
|
+
else
|
254
|
+
open_or_create_workbook(filename,options)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
266
258
|
end
|
267
|
-
set_options(options)
|
259
|
+
set_options(filename, options)
|
268
260
|
end
|
269
261
|
|
270
262
|
private
|
@@ -272,16 +264,24 @@ module RobustExcelOle
|
|
272
264
|
# @private
|
273
265
|
# connects to an unknown workbook and returns true, if it exists
|
274
266
|
def connect(filename,options)
|
275
|
-
abs_filename = General.absolute_path(filename)
|
267
|
+
abs_filename = General.absolute_path(filename)
|
276
268
|
@ole_workbook = begin
|
277
269
|
WIN32OLE.connect(abs_filename)
|
278
270
|
rescue
|
279
|
-
|
271
|
+
if $!.message =~ /moniker/
|
272
|
+
raise WorkbookConnectingBlockingError
|
273
|
+
else
|
274
|
+
raise WorkbookConnectingUnknownError
|
275
|
+
end
|
280
276
|
end
|
281
277
|
ole_excel = begin
|
282
|
-
|
278
|
+
@ole_workbook.Application
|
283
279
|
rescue
|
284
|
-
|
280
|
+
if $!.message =~ /dispid/
|
281
|
+
raise WorkbookConnectingUnsavedError
|
282
|
+
else
|
283
|
+
raise WorkbookConnectingUnknownError
|
284
|
+
end
|
285
285
|
end
|
286
286
|
@excel = excel_class.new(ole_excel)
|
287
287
|
end
|
@@ -289,9 +289,9 @@ module RobustExcelOle
|
|
289
289
|
# @private
|
290
290
|
def manage_nonexisting_file(filename,options)
|
291
291
|
return if File.exist?(filename)
|
292
|
-
abs_filename = General.absolute_path(filename)
|
292
|
+
abs_filename = General.absolute_path(filename)
|
293
293
|
if options[:if_absent] == :create
|
294
|
-
ensure_excel(
|
294
|
+
ensure_excel(options) unless @excel && @excel.alive?
|
295
295
|
@excel.Workbooks.Add
|
296
296
|
empty_ole_workbook = excel.Workbooks.Item(excel.Workbooks.Count)
|
297
297
|
begin
|
@@ -308,16 +308,7 @@ module RobustExcelOle
|
|
308
308
|
# @private
|
309
309
|
def manage_blocking_or_unsaved_workbook(filename,options)
|
310
310
|
obstructed_by_other_book = if (File.basename(filename) == File.basename(@ole_workbook.Fullname))
|
311
|
-
|
312
|
-
p2 = @ole_workbook.Fullname
|
313
|
-
is_same_path = if p1[1..1] == ":" and p2[0..1] == '\\\\' # normal path starting with the drive letter and a path in network notation (starting with 2 backslashes)
|
314
|
-
# this is a Workaround for Excel2010 on WinXP: Network drives won't get translated to drive letters. So we'll do it manually:
|
315
|
-
p1_pure_path = p1[2..-1]
|
316
|
-
p2.end_with?(p1_pure_path) and p2[0, p2.rindex(p1_pure_path)] =~ /^\\\\[\w\d_]+(\\[\w\d_]+)*$/ # e.g. "\\\\server\\folder\\subfolder"
|
317
|
-
else
|
318
|
-
p1 == p2
|
319
|
-
end
|
320
|
-
not is_same_path
|
311
|
+
General.absolute_path(filename) != @ole_workbook.Fullname
|
321
312
|
end
|
322
313
|
if obstructed_by_other_book
|
323
314
|
# workbook is being obstructed by a workbook with same name and different path
|
@@ -340,25 +331,17 @@ module RobustExcelOle
|
|
340
331
|
to allow automatic closing of the old workbook (without or with saving before, respectively),
|
341
332
|
before the new workbook is being opened."
|
342
333
|
when :forget
|
343
|
-
|
344
|
-
@ole_workbook = nil
|
345
|
-
open_or_create_workbook(filename, options)
|
334
|
+
manage_forgetting_workbook(filename, options)
|
346
335
|
when :save
|
347
|
-
|
348
|
-
@ole_workbook.Close
|
349
|
-
@ole_workbook = nil
|
350
|
-
open_or_create_workbook(filename, options)
|
336
|
+
manage_saving_workbook(filename, options)
|
351
337
|
when :close_if_saved
|
352
338
|
if !@ole_workbook.Saved
|
353
339
|
raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{@ole_workbook.Fullname.tr('\\','/')}"
|
354
340
|
else
|
355
|
-
|
356
|
-
@ole_workbook = nil
|
357
|
-
open_or_create_workbook(filename, options)
|
341
|
+
manage_forgetting_workbook(filename, options)
|
358
342
|
end
|
359
343
|
when :new_excel
|
360
|
-
|
361
|
-
open_or_create_workbook(filename, options)
|
344
|
+
manage_new_excel(filename, options)
|
362
345
|
else
|
363
346
|
raise OptionInvalid, ":if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
|
364
347
|
"\nHint: Valid values are :raise, :forget, :save, :close_if_saved, :new_excel"
|
@@ -373,72 +356,88 @@ module RobustExcelOle
|
|
373
356
|
"\nHint: Save the workbook or open the workbook using option :if_unsaved with values :forget and :accept to
|
374
357
|
close the unsaved workbook and reopen it, or to let the unsaved workbook open, respectively"
|
375
358
|
when :forget
|
376
|
-
|
377
|
-
@ole_workbook = nil
|
378
|
-
open_or_create_workbook(filename, options)
|
359
|
+
manage_forgetting_workbook(filename,options)
|
379
360
|
when :accept
|
380
361
|
# do nothing
|
362
|
+
when :save
|
363
|
+
manage_saving_workbook(filename, options)
|
381
364
|
when :alert, :excel
|
382
365
|
@excel.with_displayalerts(true) { open_or_create_workbook(filename,options) }
|
383
366
|
when :new_excel
|
384
|
-
|
385
|
-
open_or_create_workbook(filename, options)
|
367
|
+
manage_new_excel(filename, options)
|
386
368
|
else
|
387
369
|
raise OptionInvalid, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}" +
|
388
|
-
"\nHint: Valid values are :raise, :forget, :accept, :alert, :excel, :new_excel"
|
370
|
+
"\nHint: Valid values are :raise, :forget, :save, :accept, :alert, :excel, :new_excel"
|
389
371
|
end
|
390
372
|
end
|
391
373
|
|
392
374
|
# @private
|
393
|
-
def
|
394
|
-
|
375
|
+
def manage_forgetting_workbook(filename, options)
|
376
|
+
@excel.with_displayalerts(false) { @ole_workbook.Close }
|
377
|
+
@ole_workbook = nil
|
378
|
+
open_or_create_workbook(filename, options)
|
379
|
+
end
|
380
|
+
|
381
|
+
# @private
|
382
|
+
def manage_saving_workbook(filename, options)
|
383
|
+
save unless @ole_workbook.Saved
|
384
|
+
manage_forgetting_workbook(filename, options)
|
385
|
+
end
|
386
|
+
|
387
|
+
# @private
|
388
|
+
def manage_new_excel(filename, options)
|
389
|
+
@excel = excel_class.new(:reuse => false)
|
390
|
+
@ole_workbook = nil
|
391
|
+
open_or_create_workbook(filename, options)
|
392
|
+
end
|
393
|
+
|
394
|
+
# @private
|
395
|
+
def open_or_create_workbook(filename, options)
|
396
|
+
return if @ole_workbook && options[:if_unsaved] != :alert && options[:if_unsaved] != :excel
|
397
|
+
begin
|
398
|
+
abs_filename = General.absolute_path(filename)
|
395
399
|
begin
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
updatelinks_vba(options[:update_links]),
|
407
|
-
options[:read_only] )
|
408
|
-
end
|
409
|
-
rescue WIN32OLERuntimeError => msg
|
410
|
-
# for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
|
411
|
-
# if yes: distinguish these events
|
412
|
-
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
|
400
|
+
workbooks = @excel.Workbooks
|
401
|
+
rescue WIN32OLERuntimeError => msg
|
402
|
+
raise UnexpectedREOError, "cannot access workbooks: #{msg.message} #{msg.backtrace}"
|
403
|
+
end
|
404
|
+
begin
|
405
|
+
with_workaround_linked_workbooks_excel2007(options) do
|
406
|
+
# temporary workaround until jruby-win32ole implements named parameters (Java::JavaLang::RuntimeException (createVariant() not implemented for class org.jruby.RubyHash)
|
407
|
+
workbooks.Open(abs_filename,
|
408
|
+
updatelinks_vba(options[:update_links]),
|
409
|
+
options[:read_only] )
|
413
410
|
end
|
411
|
+
rescue WIN32OLERuntimeError => msg
|
412
|
+
# for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
|
413
|
+
# if yes: distinguish these events
|
414
|
+
raise UnexpectedREOError, "cannot open workbook: #{msg.message} #{msg.backtrace}"
|
415
|
+
end
|
416
|
+
begin
|
417
|
+
# workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
|
414
418
|
begin
|
415
|
-
|
416
|
-
begin
|
417
|
-
@ole_workbook = workbooks.Item(File.basename(filename))
|
418
|
-
rescue WIN32OLERuntimeError => msg
|
419
|
-
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
|
420
|
-
end
|
419
|
+
@ole_workbook = workbooks.Item(File.basename(filename))
|
421
420
|
rescue WIN32OLERuntimeError => msg
|
422
|
-
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}
|
421
|
+
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
|
423
422
|
end
|
423
|
+
rescue WIN32OLERuntimeError => msg
|
424
|
+
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
|
424
425
|
end
|
425
426
|
end
|
426
427
|
end
|
427
428
|
|
428
429
|
# @private
|
429
|
-
def set_options(options)
|
430
|
-
if options[:
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
430
|
+
def set_options(filename, options)
|
431
|
+
if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
|
432
|
+
@excel.with_displayalerts(false) { @ole_workbook.Close }
|
433
|
+
@ole_workbook = nil
|
434
|
+
open_or_create_workbook(filename, options)
|
435
|
+
end
|
436
|
+
retain_saved do
|
437
|
+
self.visible = options[:force][:visible].nil? ? @excel.Visible : options[:force][:visible]
|
438
|
+
@excel.calculation = options[:calculation] unless options[:calculation].nil?
|
439
|
+
@ole_workbook.CheckCompatibility = options[:check_compatibility] unless options[:check_compatibility].nil?
|
438
440
|
end
|
439
|
-
@excel.calculation = options[:calculation] unless options[:calculation].nil?
|
440
|
-
@ole_workbook.CheckCompatibility = options[:check_compatibility]
|
441
|
-
@ole_workbook.Saved = true # unless self.Saved # ToDo: this is too hard
|
442
441
|
end
|
443
442
|
|
444
443
|
# @private
|
@@ -571,6 +570,7 @@ module RobustExcelOle
|
|
571
570
|
# @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening
|
572
571
|
# @return [Workbook] a workbook
|
573
572
|
def self.unobtrusively(file, opts = { })
|
573
|
+
opts = process_options(opts, :use_defaults => false)
|
574
574
|
opts = {:if_closed => :current,
|
575
575
|
:rw_change_excel => :current,
|
576
576
|
:keep_open => false}.merge(opts)
|
@@ -597,12 +597,16 @@ module RobustExcelOle
|
|
597
597
|
book =
|
598
598
|
if was_open
|
599
599
|
if change_rw_mode
|
600
|
-
|
600
|
+
opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write})
|
601
|
+
open(file, opts)
|
602
|
+
#open(file, :force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write)
|
601
603
|
else
|
602
604
|
book
|
603
605
|
end
|
604
606
|
else
|
605
|
-
|
607
|
+
opts = opts.merge({:force => {:excel => opts[:if_closed]}, :read_only => do_not_write})
|
608
|
+
open(file, opts)
|
609
|
+
#open(file, :force => {:excel => opts[:if_closed]}, :read_only => do_not_write)
|
606
610
|
end
|
607
611
|
yield book
|
608
612
|
ensure
|
@@ -611,7 +615,8 @@ module RobustExcelOle
|
|
611
615
|
if was_open
|
612
616
|
if opts[:rw_change_excel] == book.excel && change_rw_mode
|
613
617
|
book.close
|
614
|
-
|
618
|
+
opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => !was_writable})
|
619
|
+
book = open(file, opts)
|
615
620
|
end
|
616
621
|
book.excel.calculation = was_calculation
|
617
622
|
book.CheckCompatibility = was_check_compatibility
|
@@ -632,9 +637,8 @@ module RobustExcelOle
|
|
632
637
|
end
|
633
638
|
|
634
639
|
# simple save of a workbook.
|
635
|
-
# @option opts [Boolean] :discoloring states, whether colored ranges shall be discolored
|
636
640
|
# @return [Boolean] true, if successfully saved, nil otherwise
|
637
|
-
def save(opts = {
|
641
|
+
def save(opts = { }) # option opts is deprecated #
|
638
642
|
raise ObjectNotAlive, 'workbook is not alive' unless alive?
|
639
643
|
raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
|
640
644
|
# if you have open the workbook with :read_only => true,
|
@@ -642,7 +646,6 @@ module RobustExcelOle
|
|
642
646
|
# otherwise the workbook may already be open writable in an another Excel instance
|
643
647
|
# then you could use this workbook or close the workbook there
|
644
648
|
begin
|
645
|
-
discoloring if opts[:discoloring]
|
646
649
|
@modified_cells = []
|
647
650
|
@ole_workbook.Save
|
648
651
|
rescue WIN32OLERuntimeError => msg
|
@@ -666,26 +669,23 @@ module RobustExcelOle
|
|
666
669
|
# :overwrite -> writes the file, delete the old file
|
667
670
|
# :alert or :excel -> gives control to Excel
|
668
671
|
# :if_obstructed if a workbook with the same name and different path is already open and blocks the saving, then
|
669
|
-
#
|
670
|
-
#
|
672
|
+
# or :raise -> raises an exception
|
673
|
+
# :if_blocked :forget -> closes the blocking workbook
|
671
674
|
# :save -> saves the blocking workbook and closes it
|
672
675
|
# :close_if_saved -> closes the blocking workbook, if it is saved,
|
673
|
-
# otherwise raises an exception
|
674
|
-
# :discoloring states, whether colored ranges shall be discolored
|
676
|
+
# otherwise raises an exception
|
675
677
|
# @return [Workbook], the book itself, if successfully saved, raises an exception otherwise
|
676
678
|
def save_as(file, opts = { })
|
677
679
|
raise FileNameNotGiven, 'filename is nil' if file.nil?
|
678
680
|
raise ObjectNotAlive, 'workbook is not alive' unless alive?
|
679
681
|
raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
|
680
|
-
|
681
|
-
|
682
|
-
:if_obstructed => :raise
|
683
|
-
}.merge(opts)
|
682
|
+
raise(FileNotFound, "file #{General.absolute_path(file).inspect} is a directory") if File.directory?(file)
|
683
|
+
options = self.class.process_options(opts)
|
684
684
|
if File.exist?(file)
|
685
685
|
case options[:if_exists]
|
686
686
|
when :overwrite
|
687
687
|
if file == self.filename
|
688
|
-
save
|
688
|
+
save
|
689
689
|
return self
|
690
690
|
else
|
691
691
|
begin
|
@@ -712,7 +712,7 @@ module RobustExcelOle
|
|
712
712
|
case options[:if_obstructed]
|
713
713
|
when :raise
|
714
714
|
raise WorkbookBlocked, "blocked by another workbook: #{other_workbook.Fullname.tr('\\','/')}" +
|
715
|
-
"\nHint: Use the option :
|
715
|
+
"\nHint: Use the option :if_blocked with values :forget or :save to
|
716
716
|
close or save and close the blocking workbook"
|
717
717
|
when :forget
|
718
718
|
# nothing
|
@@ -721,25 +721,20 @@ module RobustExcelOle
|
|
721
721
|
when :close_if_saved
|
722
722
|
unless other_workbook.Saved
|
723
723
|
raise WorkbookBlocked, "blocking workbook is unsaved: #{File.basename(file).inspect}" +
|
724
|
-
"\nHint: Use option :
|
724
|
+
"\nHint: Use option :if_blocked => :save to save the blocking workbooks"
|
725
725
|
end
|
726
726
|
else
|
727
|
-
raise OptionInvalid, ":
|
727
|
+
raise OptionInvalid, ":if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
|
728
728
|
"\nHint: Valid values are :raise, :forget, :save, :close_if_saved"
|
729
729
|
end
|
730
730
|
other_workbook.Close
|
731
731
|
end
|
732
732
|
save_as_workbook(file, options)
|
733
733
|
self
|
734
|
-
end
|
734
|
+
end
|
735
735
|
|
736
736
|
private
|
737
737
|
|
738
|
-
# @private
|
739
|
-
def discoloring
|
740
|
-
@modified_cells.each { |cell| cell.Interior.ColorIndex = XlNone }
|
741
|
-
end
|
742
|
-
|
743
738
|
# @private
|
744
739
|
def save_as_workbook(file, options)
|
745
740
|
dirname, basename = File.split(file)
|
@@ -749,7 +744,6 @@ module RobustExcelOle
|
|
749
744
|
when '.xlsx' then RobustExcelOle::XlOpenXMLWorkbook
|
750
745
|
when '.xlsm' then RobustExcelOle::XlOpenXMLWorkbookMacroEnabled
|
751
746
|
end
|
752
|
-
discoloring if options[:discoloring]
|
753
747
|
@modified_cells = []
|
754
748
|
@ole_workbook.SaveAs(General.absolute_path(file), file_format)
|
755
749
|
bookstore.store(self)
|
@@ -832,7 +826,7 @@ module RobustExcelOle
|
|
832
826
|
after_or_before, base_sheet = opts.to_a.first || [:after, last_sheet_local]
|
833
827
|
begin
|
834
828
|
if RUBY_PLATFORM !~ /java/
|
835
|
-
if sheet
|
829
|
+
if sheet
|
836
830
|
sheet.Copy({ after_or_before.to_s => base_sheet.ole_worksheet })
|
837
831
|
else
|
838
832
|
@ole_workbook.Worksheets.Add({ after_or_before.to_s => base_sheet.ole_worksheet })
|
@@ -862,11 +856,12 @@ module RobustExcelOle
|
|
862
856
|
ole_workbook.Worksheets.Add(base_sheet.ole_worksheet)
|
863
857
|
end
|
864
858
|
base_sheet.Move(ole_workbook.Worksheets.Item(ole_workbook.Worksheets.Count-1))
|
859
|
+
ole_workbook.Worksheets.Item(ole_workbook.Worksheets.Count).Activate
|
865
860
|
end
|
866
861
|
end
|
867
862
|
end
|
868
863
|
rescue #WIN32OLERuntimeError
|
869
|
-
trace "#{$!.message}"
|
864
|
+
#trace "#{$!.message}"
|
870
865
|
raise WorksheetREOError, "could not add given worksheet #{sheet.inspect}"
|
871
866
|
end
|
872
867
|
#ole_sheet = @excel.Activesheet
|
@@ -906,7 +901,10 @@ module RobustExcelOle
|
|
906
901
|
# @param [String] name the name of the range
|
907
902
|
# @param [Variant] value the contents of the range
|
908
903
|
def []= (name, value)
|
909
|
-
|
904
|
+
old_color_if_modified = @color_if_modified
|
905
|
+
workbook.color_if_modified = 42 # 42 - aqua-marin, 4-green
|
906
|
+
set_namevalue_glob(name,value)
|
907
|
+
workbook.color_if_modified = old_color_if_modified
|
910
908
|
end
|
911
909
|
|
912
910
|
# sets options
|
@@ -981,7 +979,7 @@ module RobustExcelOle
|
|
981
979
|
def visible= visible_value
|
982
980
|
return if visible_value.nil?
|
983
981
|
@excel.visible = true if visible_value
|
984
|
-
self.window_visible = visible_value
|
982
|
+
self.window_visible = @excel.Visible ? visible_value : true
|
985
983
|
end
|
986
984
|
|
987
985
|
# returns true, if the window of the workbook is set to visible, false otherwise
|