robust_excel_ole 1.36 → 1.37
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +5 -0
- data/docs/README_open.rdoc +9 -6
- data/examples/modifying_sheets/example_access_sheets_and_cells.rb +1 -1
- data/examples/modifying_sheets/example_add_names.rb +1 -1
- data/examples/modifying_sheets/example_listobjects.rb +2 -2
- data/examples/modifying_sheets/example_ranges.rb +1 -1
- data/lib/robust_excel_ole/excel.rb +31 -17
- data/lib/robust_excel_ole/general.rb +1 -1
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +80 -64
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/excel_spec.rb +10 -0
- data/spec/workbook_specs/workbook_misc_spec.rb +9 -2
- data/spec/workbook_specs/workbook_open_spec.rb +58 -2
- data/spec/workbook_specs/workbook_unobtr_spec.rb +43 -32
- data/spec/worksheet_spec.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 646308c3740c8716d9fb91b9c0288edc6e915c060bdd713401980608d412119a
|
4
|
+
data.tar.gz: 379a3bd099724e9cd6adc2542d804d4f4737eafc47742892dbc1ba4914f7b7c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz: '
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '02308d88008a6e7d5362b0c793f1ab4e55025b7df0d19741dbe66775450f672b654c9766ac3e49d6ec840d497b9fa39befd1f66e6d20e5a5539d6cf3eefbc276'
|
7
|
+
data.tar.gz: 874eaf4089951eb94ac101699198e35981b72084036eb171ec70eb69dc2c135951138f773844b945ff7f4ebbd8d3a4020f3bdaf3f4286f165334e270ada23118
|
data/Changelog
CHANGED
data/docs/README_open.rdoc
CHANGED
@@ -42,7 +42,7 @@ The options are the following:
|
|
42
42
|
+:if_blocked+:: specifies behaviour if the workbook is blocked by another book (default: +:raise+)
|
43
43
|
|
44
44
|
|
45
|
-
+:read_only+:: opens in read-only mode (default: +false+)
|
45
|
+
+:read_only+:: opens in read-only (+true+) or read-write mode (+false+) (default: +false+)
|
46
46
|
|
47
47
|
+:check_compatibility+:: checks compatibility when saving
|
48
48
|
|
@@ -137,14 +137,11 @@ If a workbook is open and a workbook with the same name but in different path sh
|
|
137
137
|
|
138
138
|
The methods +open+ and +new+ connect to workbooks opened outside of RobustExcelOle as well.
|
139
139
|
|
140
|
-
Opening linked workbooks for EXCEL 2007 is supported
|
140
|
+
Opening linked workbooks for EXCEL 2007 is supported. Changing linked, unsaved workbooks from read-only to read-write causes a query whether to save the changes. This case cannot be
|
141
|
+
controlled (e.g. with help of some options) so far.
|
141
142
|
|
142
143
|
Doing updating links seems to be dependent on calculation mode: updates happen, if the calcultion mode is automatic, and does not happen, if calculation mode is manual.
|
143
144
|
|
144
|
-
Once we have a workbook, we can set some options, e.g.
|
145
|
-
|
146
|
-
workbook.for_this_workbook(visible: true, read_only: false)
|
147
|
-
|
148
145
|
|
149
146
|
=== Reopening a workbook
|
150
147
|
|
@@ -270,6 +267,8 @@ If the workbook is unsaved, use the option +if_unsaved+ as desribed above.
|
|
270
267
|
|
271
268
|
workbook.writable = false, {if_unsaved: :forget}
|
272
269
|
|
270
|
+
Changing from read-only to read-write for linked, unsaved workbooks is not being supported yet.
|
271
|
+
|
273
272
|
=== Checking whether the workbook is alive
|
274
273
|
|
275
274
|
The method +alive?+ finds out whether the Excel workbook that is referenced by the Workbook object responds to methods. For example
|
@@ -277,4 +276,8 @@ The method +alive?+ finds out whether the Excel workbook that is referenced by t
|
|
277
276
|
workbook.alive?
|
278
277
|
# => true
|
279
278
|
|
279
|
+
=== Setting options
|
280
|
+
|
281
|
+
Once we have a workbook, we can set some options, e.g.
|
280
282
|
|
283
|
+
workbook.for_this_workbook(visible: true, read_only: false)
|
@@ -9,7 +9,7 @@ include RobustExcelOle
|
|
9
9
|
|
10
10
|
using StringRefinement
|
11
11
|
|
12
|
-
Excel.close_all
|
12
|
+
Excel.close_all(if_unsaved: :forget)
|
13
13
|
begin
|
14
14
|
dir = create_tmpdir
|
15
15
|
table_file = dir + 'workbook_listobjects.xlsx'
|
@@ -79,7 +79,7 @@ begin
|
|
79
79
|
book.close(:if_unsaved => :forget)
|
80
80
|
|
81
81
|
ensure
|
82
|
-
Excel.close_all
|
82
|
+
Excel.close_all(if_unsaved: forget)
|
83
83
|
rm_tmp(dir)
|
84
84
|
end
|
85
85
|
|
@@ -18,14 +18,18 @@ module User32
|
|
18
18
|
typealias 'LPCSTR', 'const char*'
|
19
19
|
typealias 'LPCWSTR', 'const wchar_t*'
|
20
20
|
typealias 'UINT', 'unsigned int'
|
21
|
-
typealias 'HANDLE', 'void*'
|
22
21
|
typealias 'ppvObject', 'void**'
|
23
22
|
typealias 'DWORD', 'unsigned long'
|
24
23
|
typealias 'LPDWORD', 'DWORD*'
|
24
|
+
typealias 'PDWORD_PTR', 'DWORD**'
|
25
|
+
typealias 'WPARAM', 'UINT*'
|
26
|
+
typealias 'LPARAM', 'INT*'
|
27
|
+
typealias 'LRESULT', 'DWORD'
|
25
28
|
# Import C functions from loaded libraries and set them as module functions
|
26
29
|
extern 'DWORD GetWindowThreadProcessId(HWND, LPDWORD)'
|
27
30
|
extern 'HWND FindWindowExA(HWND, HWND, LPCSTR, LPCSTR)'
|
28
31
|
extern 'DWORD SetForegroundWindow(HWND)'
|
32
|
+
extern 'LRESULT SendMessageTimeoutA(HWND, UINT, WPARAM, LPARAM, UINT, UINT, PDWORD_PTR)'
|
29
33
|
end
|
30
34
|
|
31
35
|
module Oleacc
|
@@ -68,6 +72,7 @@ module RobustExcelOle
|
|
68
72
|
attr_reader :ole_excel
|
69
73
|
attr_reader :properties
|
70
74
|
attr_reader :address_tool
|
75
|
+
attr_reader :hwnd
|
71
76
|
|
72
77
|
alias ole_object ole_excel
|
73
78
|
|
@@ -127,15 +132,16 @@ module RobustExcelOle
|
|
127
132
|
end
|
128
133
|
connected = (not ole_xl.nil?) && win32ole_excel.nil?
|
129
134
|
ole_xl ||= WIN32OLE.new('Excel.Application')
|
130
|
-
|
131
|
-
stored = hwnd2excel(
|
135
|
+
hwnd_xl = ole_xl.Hwnd
|
136
|
+
stored = hwnd2excel(hwnd_xl)
|
132
137
|
if stored && stored.alive?
|
133
138
|
result = stored
|
134
139
|
else
|
135
140
|
result = super(options)
|
136
141
|
result.instance_variable_set(:@ole_excel, ole_xl)
|
142
|
+
result.instance_variable_set(:@hwnd, hwnd_xl)
|
137
143
|
WIN32OLE.const_load(ole_xl, RobustExcelOle) unless RobustExcelOle.const_defined?(:CONSTANTS)
|
138
|
-
@@hwnd2excel[
|
144
|
+
@@hwnd2excel[hwnd_xl] = WeakRef.new(result)
|
139
145
|
end
|
140
146
|
reused = options[:reuse] && stored && stored.alive?
|
141
147
|
options = { displayalerts: :if_visible, visible: false, screenupdating: true }.merge(options) unless reused || connected
|
@@ -159,6 +165,7 @@ module RobustExcelOle
|
|
159
165
|
opts = {visible: false, displayalerts: :if_visible}.merge(
|
160
166
|
{visible: @properties[:visible], displayalerts: @properties[:displayalerts]}).merge(opts)
|
161
167
|
@ole_excel = WIN32OLE.new('Excel.Application')
|
168
|
+
@hwnd = @ole_excel.Hwnd
|
162
169
|
set_options(opts)
|
163
170
|
if opts[:reopen_workbooks]
|
164
171
|
workbook_class.books.each{ |book| book.open if !book.alive? && book.excel.alive? && book.excel == self }
|
@@ -531,13 +538,6 @@ module RobustExcelOle
|
|
531
538
|
self
|
532
539
|
end
|
533
540
|
|
534
|
-
# @private
|
535
|
-
def hwnd
|
536
|
-
self.Hwnd
|
537
|
-
rescue
|
538
|
-
nil
|
539
|
-
end
|
540
|
-
|
541
541
|
# @private
|
542
542
|
def self.print_hwnd2excel
|
543
543
|
@@hwnd2excel.each do |hwnd,wr_excel|
|
@@ -554,11 +554,15 @@ module RobustExcelOle
|
|
554
554
|
|
555
555
|
# returns true, if the Excel instances responds to VBA methods, false otherwise
|
556
556
|
def alive?
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
#
|
561
|
-
|
557
|
+
msg = 0x2008
|
558
|
+
wparam = 0
|
559
|
+
lparam = 0
|
560
|
+
flags = 0x0000 # 0x0002
|
561
|
+
duration = 5000
|
562
|
+
lpdw_result_puffer = ' ' * 32
|
563
|
+
status = User32::SendMessageTimeoutA(hwnd, msg, wparam, lparam, flags, duration, lpdw_result_puffer)
|
564
|
+
result = lpdw_result_puffer.unpack('L')[0]
|
565
|
+
status != 0
|
562
566
|
end
|
563
567
|
|
564
568
|
# returns unsaved workbooks in known (not opened by user) Excel instances
|
@@ -589,6 +593,11 @@ module RobustExcelOle
|
|
589
593
|
end
|
590
594
|
end
|
591
595
|
|
596
|
+
# returns, whether the current Excel instance is visible
|
597
|
+
def visible
|
598
|
+
@ole_excel.Visible
|
599
|
+
end
|
600
|
+
|
592
601
|
# makes the current Excel instance visible or invisible
|
593
602
|
def visible= visible_value
|
594
603
|
return if visible_value.nil?
|
@@ -596,6 +605,11 @@ module RobustExcelOle
|
|
596
605
|
@ole_excel.DisplayAlerts = @properties[:visible] if @properties[:displayalerts] == :if_visible
|
597
606
|
end
|
598
607
|
|
608
|
+
# returns, wheter DisplayAlerts is enabled
|
609
|
+
def displayalerts
|
610
|
+
@ole_excel.DisplayAlerts
|
611
|
+
end
|
612
|
+
|
599
613
|
# enables DisplayAlerts in the current Excel instance
|
600
614
|
def displayalerts= displayalerts_value
|
601
615
|
return if displayalerts_value.nil?
|
@@ -726,7 +740,7 @@ module RobustExcelOle
|
|
726
740
|
|
727
741
|
# @private
|
728
742
|
def to_s
|
729
|
-
"#<Excel: #{hwnd}#{ ("not alive" unless alive?)}>"
|
743
|
+
"#<Excel: #{hwnd}#{ (" not alive" unless alive?)}>"
|
730
744
|
end
|
731
745
|
|
732
746
|
# @private
|
@@ -237,7 +237,7 @@ module General
|
|
237
237
|
return filename unless filename[0,2] == "//"
|
238
238
|
hostname = filename[0,filename[3,filename.length].index('/')+3]
|
239
239
|
filename_wo_hostname = filename[hostname.length+1,filename.length]
|
240
|
-
abs_filename = absolute_path(filename_wo_hostname).tr('\\','/').
|
240
|
+
abs_filename = absolute_path(filename_wo_hostname).tr('\\','/').sub('C:/','c$/')
|
241
241
|
adapted_filename = hostname + "/" + abs_filename
|
242
242
|
NetworkDrive.get_all_drives.each do |d|
|
243
243
|
new_filename = filename.sub(/#{(Regexp.escape(d.network_name))}/i,d.drive_letter)
|
@@ -164,12 +164,12 @@ module RobustExcelOle
|
|
164
164
|
raise ExcelREOError, "could not determine the Excel instance\n#{$!.message}"
|
165
165
|
end
|
166
166
|
@excel = excel_class.new(ole_excel)
|
167
|
-
|
167
|
+
file_name = @ole_workbook.Fullname.tr('\\','/')
|
168
168
|
else
|
169
|
-
|
170
|
-
ensure_workbook(
|
169
|
+
file_name = file_or_workbook
|
170
|
+
ensure_workbook(file_name, opts)
|
171
171
|
end
|
172
|
-
apply_options(
|
172
|
+
apply_options(file_name, opts)
|
173
173
|
store_myself
|
174
174
|
if block_given?
|
175
175
|
begin
|
@@ -244,7 +244,7 @@ module RobustExcelOle
|
|
244
244
|
end
|
245
245
|
|
246
246
|
# @private
|
247
|
-
def ensure_workbook(
|
247
|
+
def ensure_workbook(file_name, options)
|
248
248
|
set_was_open options, true
|
249
249
|
return if (@ole_workbook && alive? && (options[:read_only].nil? || @ole_workbook.ReadOnly == options[:read_only]))
|
250
250
|
set_was_open options, false
|
@@ -252,35 +252,35 @@ module RobustExcelOle
|
|
252
252
|
((options[:read_only]==true && self.ReadOnly==false) || (options[:read_only]==false && self.ReadOnly==true))
|
253
253
|
raise OptionInvalid, ":if_unsaved:accept and change of read-only mode is not possible"
|
254
254
|
end
|
255
|
-
|
256
|
-
manage_nonexisting_file(
|
255
|
+
file_name = @stored_filename ? @stored_filename : file_name
|
256
|
+
manage_nonexisting_file(file_name,options)
|
257
257
|
excel_option = options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
|
258
258
|
ensure_excel(options)
|
259
259
|
workbooks = @excel.Workbooks
|
260
|
-
@ole_workbook = workbooks.Item(File.basename(
|
260
|
+
@ole_workbook = workbooks.Item(File.basename(file_name)) rescue nil if @ole_workbook.nil?
|
261
261
|
if @ole_workbook && alive?
|
262
262
|
set_was_open options, true
|
263
|
-
#open_or_create_workbook(
|
264
|
-
manage_changing_readonly_mode(options) if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
|
265
|
-
manage_blocking_or_unsaved_workbook(
|
263
|
+
#open_or_create_workbook(file_name,options) if (!options[:read_only].nil?) && options[:read_only]
|
264
|
+
manage_changing_readonly_mode(file_name, options) if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
|
265
|
+
manage_blocking_or_unsaved_workbook(file_name,options)
|
266
266
|
else
|
267
267
|
if (excel_option.nil? || excel_option == :current) &&
|
268
|
-
!(::CONNECT_JRUBY_BUG &&
|
269
|
-
connect(
|
268
|
+
!(::CONNECT_JRUBY_BUG && file_name[0] == '/')
|
269
|
+
connect(file_name,options)
|
270
270
|
else
|
271
|
-
open_or_create_workbook(
|
271
|
+
open_or_create_workbook(file_name,options)
|
272
272
|
end
|
273
273
|
end
|
274
274
|
end
|
275
275
|
|
276
276
|
private
|
277
277
|
|
278
|
-
# applies options to workbook named with
|
279
|
-
def apply_options(
|
278
|
+
# applies options to workbook named with file_name
|
279
|
+
def apply_options(file_name, options)
|
280
280
|
# changing read-only mode
|
281
281
|
if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
|
282
|
-
# ensure_workbook(
|
283
|
-
manage_changing_readonly_mode(options)
|
282
|
+
# ensure_workbook(file_name, options)
|
283
|
+
manage_changing_readonly_mode(file_name, options)
|
284
284
|
end
|
285
285
|
retain_saved do
|
286
286
|
self.visible = options[:force][:visible].nil? ? @excel.Visible : options[:force][:visible]
|
@@ -290,10 +290,10 @@ module RobustExcelOle
|
|
290
290
|
end
|
291
291
|
|
292
292
|
# connects to an unknown workbook
|
293
|
-
def connect(
|
293
|
+
def connect(file_name, options)
|
294
294
|
workbooks_number = excel_class.instance_count==0 ? 0 : excel_class.current.Workbooks.Count
|
295
295
|
@ole_workbook = begin
|
296
|
-
WIN32OLE.connect(General.absolute_path(
|
296
|
+
WIN32OLE.connect(General.absolute_path(file_name))
|
297
297
|
rescue
|
298
298
|
if $!.message =~ /moniker/
|
299
299
|
raise WorkbookConnectingBlockingError, "some workbook is blocking when connecting"
|
@@ -314,21 +314,37 @@ module RobustExcelOle
|
|
314
314
|
@excel = excel_class.new(ole_excel)
|
315
315
|
end
|
316
316
|
|
317
|
-
def manage_changing_readonly_mode(options)
|
317
|
+
def manage_changing_readonly_mode(file_name, options)
|
318
318
|
if !ole_workbook.Saved && options[:read_only]
|
319
319
|
manage_unsaved_workbook_when_changing_readonly_mode(options)
|
320
320
|
end
|
321
|
-
change_readonly_mode(options)
|
321
|
+
change_readonly_mode(file_name, options)
|
322
322
|
end
|
323
323
|
|
324
|
-
def change_readonly_mode(options)
|
324
|
+
def change_readonly_mode(file_name, options)
|
325
325
|
read_write_value = options[:read_only] ? RobustExcelOle::XlReadOnly : RobustExcelOle::XlReadWrite
|
326
326
|
give_control_to_excel = !ole_workbook.Saved && options[:read_only] &&
|
327
327
|
(options[:if_unsaved] == :excel || options[:if_unsaved] == :alert)
|
328
328
|
displayalerts = give_control_to_excel ? true : @excel.Displayalerts
|
329
|
+
# applying ChangeFileAccess to a linked unsaved workbook to change to read-write
|
330
|
+
# causes a query
|
331
|
+
# how to check whether the workbook contains links?
|
332
|
+
#if options[:read_only]==false && !@ole_workbook.Saved # && @ole_workbook.LinkSources(RobustExcelOle::XlExcelLinks) # workbook linked
|
333
|
+
# # @ole_workbook.Saved = true
|
334
|
+
# raise WorkbookNotSaved, "linked workbook cannot be changed to read-write if it is unsaved"
|
335
|
+
#end
|
329
336
|
@excel.with_displayalerts(displayalerts) {
|
330
|
-
|
337
|
+
begin
|
338
|
+
@ole_workbook.ChangeFileAccess('Mode' => read_write_value)
|
339
|
+
rescue WIN32OLERuntimeError
|
340
|
+
raise WorkbookReadOnly, "cannot change read-only mode"
|
341
|
+
end
|
331
342
|
}
|
343
|
+
# managing Excel bug:
|
344
|
+
# if the workbook is linked, then ChangeFileAccess to read-write kills the workbook
|
345
|
+
# if the linked workbook is unsaved, then ChangeFileAccess causes a query
|
346
|
+
# this query cannot be avoided or controlled so far (see above)
|
347
|
+
open_or_create_workbook(file_name, options) unless alive?
|
332
348
|
end
|
333
349
|
|
334
350
|
def manage_unsaved_workbook_when_changing_readonly_mode(options)
|
@@ -353,9 +369,9 @@ module RobustExcelOle
|
|
353
369
|
end
|
354
370
|
end
|
355
371
|
|
356
|
-
def manage_nonexisting_file(
|
357
|
-
return if File.exist?(
|
358
|
-
abs_filename = General.absolute_path(
|
372
|
+
def manage_nonexisting_file(file_name, options)
|
373
|
+
return if File.exist?(file_name)
|
374
|
+
abs_filename = General.absolute_path(file_name)
|
359
375
|
if options[:if_absent] == :create
|
360
376
|
ensure_excel(options) unless @excel && @excel.alive?
|
361
377
|
@excel.Workbooks.Add
|
@@ -363,7 +379,7 @@ module RobustExcelOle
|
|
363
379
|
begin
|
364
380
|
empty_ole_workbook.SaveAs(abs_filename)
|
365
381
|
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
366
|
-
raise FileNotFound, "could not save workbook with filename #{
|
382
|
+
raise FileNotFound, "could not save workbook with filename #{file_name.inspect}"
|
367
383
|
end
|
368
384
|
else
|
369
385
|
raise FileNotFound, "file #{abs_filename.inspect} not found" +
|
@@ -371,90 +387,90 @@ module RobustExcelOle
|
|
371
387
|
end
|
372
388
|
end
|
373
389
|
|
374
|
-
def manage_blocking_or_unsaved_workbook(
|
375
|
-
|
376
|
-
|
390
|
+
def manage_blocking_or_unsaved_workbook(file_name, options)
|
391
|
+
file_name = General.absolute_path(file_name)
|
392
|
+
file_name = General.canonize(file_name)
|
377
393
|
previous_file = General.canonize(@ole_workbook.Fullname.gsub('\\','/'))
|
378
|
-
obstructed_by_other_book = (File.basename(
|
379
|
-
(File.dirname(
|
394
|
+
obstructed_by_other_book = (File.basename(file_name) == File.basename(previous_file)) &&
|
395
|
+
(File.dirname(file_name) != File.dirname(previous_file))
|
380
396
|
if obstructed_by_other_book
|
381
397
|
# workbook is being obstructed by a workbook with same name and different path
|
382
|
-
manage_blocking_workbook(
|
398
|
+
manage_blocking_workbook(file_name,options)
|
383
399
|
else
|
384
400
|
unless @ole_workbook.Saved
|
385
401
|
# workbook open and writable, not obstructed by another workbook, but not saved
|
386
|
-
manage_unsaved_workbook(
|
402
|
+
manage_unsaved_workbook(file_name,options)
|
387
403
|
end
|
388
404
|
end
|
389
405
|
end
|
390
406
|
|
391
|
-
def manage_blocking_workbook(
|
407
|
+
def manage_blocking_workbook(file_name, options)
|
392
408
|
blocked_filename = -> { General.canonize(@ole_workbook.Fullname.tr('\\','/')) }
|
393
409
|
case options[:if_obstructed]
|
394
410
|
when :raise
|
395
|
-
raise WorkbookBlocked, "can't open workbook #{
|
411
|
+
raise WorkbookBlocked, "can't open workbook #{file_name},
|
396
412
|
because it is being blocked by #{blocked_filename.call} with the same name in a different path." +
|
397
413
|
"\nHint: Use the option :if_blocked with values :forget or :save,
|
398
414
|
to allow automatic closing of the blocking workbook (without or with saving before, respectively),
|
399
415
|
before reopening the workbook."
|
400
416
|
when :forget
|
401
|
-
manage_forgetting_workbook(
|
417
|
+
manage_forgetting_workbook(file_name, options)
|
402
418
|
when :accept
|
403
419
|
# do nothing
|
404
420
|
when :save
|
405
|
-
manage_saving_workbook(
|
421
|
+
manage_saving_workbook(file_name, options)
|
406
422
|
when :close_if_saved
|
407
423
|
if !@ole_workbook.Saved
|
408
424
|
raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{blocked_filename.call}" +
|
409
425
|
"\nHint: Use the option if_blocked: :save to save the workbook"
|
410
426
|
else
|
411
|
-
manage_forgetting_workbook(
|
427
|
+
manage_forgetting_workbook(file_name, options)
|
412
428
|
end
|
413
429
|
when :new_excel
|
414
|
-
manage_new_excel(
|
430
|
+
manage_new_excel(file_name, options)
|
415
431
|
else
|
416
432
|
raise OptionInvalid, ":if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
|
417
433
|
"\nHint: Valid values are :raise, :forget, :save, :close_if_saved, :new_excel"
|
418
434
|
end
|
419
435
|
end
|
420
436
|
|
421
|
-
def manage_unsaved_workbook(
|
437
|
+
def manage_unsaved_workbook(file_name, options)
|
422
438
|
case options[:if_unsaved]
|
423
439
|
when :raise
|
424
|
-
raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(
|
440
|
+
raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(file_name).inspect}" +
|
425
441
|
"\nHint: Use the option :if_unsaved with values :forget to close the unsaved workbook,
|
426
442
|
:accept to let it open, or :save to save it, respectivly"
|
427
443
|
when :forget
|
428
|
-
manage_forgetting_workbook(
|
444
|
+
manage_forgetting_workbook(file_name, options)
|
429
445
|
when :accept
|
430
446
|
# do nothing
|
431
447
|
when :save
|
432
|
-
manage_saving_workbook(
|
448
|
+
manage_saving_workbook(file_name, options)
|
433
449
|
when :alert, :excel
|
434
|
-
@excel.with_displayalerts(true) { open_or_create_workbook(
|
450
|
+
@excel.with_displayalerts(true) { open_or_create_workbook(file_name,options) }
|
435
451
|
when :new_excel
|
436
|
-
manage_new_excel(
|
452
|
+
manage_new_excel(file_name, options)
|
437
453
|
else
|
438
454
|
raise OptionInvalid, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}" +
|
439
455
|
"\nHint: Valid values are :raise, :forget, :save, :accept, :alert, :excel, :new_excel"
|
440
456
|
end
|
441
457
|
end
|
442
458
|
|
443
|
-
def manage_forgetting_workbook(
|
459
|
+
def manage_forgetting_workbook(file_name, options)
|
444
460
|
@excel.with_displayalerts(false) { @ole_workbook.Close }
|
445
461
|
@ole_workbook = nil
|
446
|
-
open_or_create_workbook(
|
462
|
+
open_or_create_workbook(file_name, options)
|
447
463
|
end
|
448
464
|
|
449
|
-
def manage_saving_workbook(
|
465
|
+
def manage_saving_workbook(file_name, options)
|
450
466
|
save unless @ole_workbook.Saved
|
451
|
-
manage_forgetting_workbook(
|
467
|
+
manage_forgetting_workbook(file_name, options) if options[:if_obstructed] == :save
|
452
468
|
end
|
453
469
|
|
454
|
-
def manage_new_excel(
|
470
|
+
def manage_new_excel(file_name, options)
|
455
471
|
@excel = excel_class.new(reuse: false)
|
456
472
|
@ole_workbook = nil
|
457
|
-
open_or_create_workbook(
|
473
|
+
open_or_create_workbook(file_name, options)
|
458
474
|
end
|
459
475
|
|
460
476
|
def explore_workbook_error(msg, want_change_readonly = nil)
|
@@ -473,10 +489,10 @@ module RobustExcelOle
|
|
473
489
|
end
|
474
490
|
end
|
475
491
|
|
476
|
-
def open_or_create_workbook(
|
492
|
+
def open_or_create_workbook(file_name, options)
|
477
493
|
return if @ole_workbook && options[:if_unsaved] != :alert && options[:if_unsaved] != :excel &&
|
478
494
|
(options[:read_only].nil? || options[:read_only]==@ole_workbook.ReadOnly )
|
479
|
-
abs_filename = General.absolute_path(
|
495
|
+
abs_filename = General.absolute_path(file_name)
|
480
496
|
workbooks = begin
|
481
497
|
@excel.Workbooks
|
482
498
|
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
@@ -493,7 +509,7 @@ module RobustExcelOle
|
|
493
509
|
end
|
494
510
|
# workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
|
495
511
|
@ole_workbook = begin
|
496
|
-
workbooks.Item(File.basename(
|
512
|
+
workbooks.Item(File.basename(file_name))
|
497
513
|
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
498
514
|
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
|
499
515
|
end
|
@@ -536,8 +552,8 @@ module RobustExcelOle
|
|
536
552
|
# creates, i.e., opens a new, empty workbook, and saves it under a given filename
|
537
553
|
# @param [String] filename the filename under which the new workbook should be saved
|
538
554
|
# @param [Hash] opts the options as in Workbook::open
|
539
|
-
def self.create(
|
540
|
-
open(
|
555
|
+
def self.create(file_name, opts = { })
|
556
|
+
open(file_name, if_absent: :create)
|
541
557
|
end
|
542
558
|
|
543
559
|
# closes the workbook, if it is alive
|
@@ -656,11 +672,10 @@ module RobustExcelOle
|
|
656
672
|
open_opts[:was_open] = nil
|
657
673
|
book = open(file, open_opts)
|
658
674
|
was_visible = book.visible
|
659
|
-
was_writable = book.writable
|
660
675
|
was_saved = book.saved
|
661
676
|
was_check_compatibility = book.check_compatibility
|
662
677
|
was_calculation = book.excel.properties[:calculation]
|
663
|
-
|
678
|
+
was_writable = book.writable
|
664
679
|
if (opts[:read_only].nil? && !opts[:writable].nil? && !open_opts[:was_open] && (was_saved || opts[:if_unsaved]==:save))
|
665
680
|
opts[:read_only] = !opts[:writable]
|
666
681
|
end
|
@@ -668,13 +683,14 @@ module RobustExcelOle
|
|
668
683
|
yield book
|
669
684
|
ensure
|
670
685
|
if book && book.alive?
|
686
|
+
was_open = open_opts[:was_open]
|
671
687
|
do_not_write = opts[:read_only] || opts[:writable]==false
|
672
688
|
book.save unless book.saved || do_not_write || !book.writable
|
673
|
-
if
|
689
|
+
if was_open && ((opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable))
|
674
690
|
book.send :apply_options, file, opts.merge({read_only: !was_writable,
|
675
691
|
if_unsaved: (opts[:writable]==false ? :forget : :save)})
|
676
692
|
end
|
677
|
-
was_open = open_opts[:was_open]
|
693
|
+
#was_open = open_opts[:was_open]
|
678
694
|
if was_open
|
679
695
|
book.visible = was_visible
|
680
696
|
book.CheckCompatibility = was_check_compatibility
|
@@ -1018,7 +1034,7 @@ module RobustExcelOle
|
|
1018
1034
|
|
1019
1035
|
# returns the full file name of the workbook
|
1020
1036
|
def filename
|
1021
|
-
|
1037
|
+
General.canonize(@ole_workbook.Fullname.tr('\\','/')) rescue nil
|
1022
1038
|
end
|
1023
1039
|
|
1024
1040
|
# @returns true, if the workbook is not in read-only mode
|
@@ -1040,7 +1056,7 @@ module RobustExcelOle
|
|
1040
1056
|
options = options.merge(unsaved_opts) if unsaved_opts
|
1041
1057
|
options = {:read_only => !writable_value}.merge(options)
|
1042
1058
|
if options[:read_only] != @ole_workbook.ReadOnly
|
1043
|
-
manage_changing_readonly_mode(options)
|
1059
|
+
manage_changing_readonly_mode(filename, options)
|
1044
1060
|
manage_unsaved_workbook(filename,options) if !@ole_workbook.Saved && unsaved_opts
|
1045
1061
|
end
|
1046
1062
|
end
|
Binary file
|
data/spec/excel_spec.rb
CHANGED
@@ -75,6 +75,7 @@ module RobustExcelOle
|
|
75
75
|
it "should set visible true" do
|
76
76
|
@ole_excel.Visible.should be false
|
77
77
|
excel = Excel.current(:visible => true)
|
78
|
+
excel.visible.should be true
|
78
79
|
excel.Visible.should be true
|
79
80
|
end
|
80
81
|
|
@@ -88,6 +89,7 @@ module RobustExcelOle
|
|
88
89
|
@ole_excel.Visible.should be true
|
89
90
|
excel = Excel.current(:visible => false)
|
90
91
|
excel.Visible.should be false
|
92
|
+
excel.visible.should be false
|
91
93
|
end
|
92
94
|
|
93
95
|
it "should preserve displayalerts true" do
|
@@ -454,7 +456,9 @@ module RobustExcelOle
|
|
454
456
|
@excel1.recreate
|
455
457
|
@excel1.should be_a Excel
|
456
458
|
@excel1.should be_alive
|
459
|
+
@excel1.visible.should be false
|
457
460
|
@excel1.Visible.should be false
|
461
|
+
@excel1.displayalerts.should be false
|
458
462
|
@excel1.DisplayAlerts.should be false
|
459
463
|
@book1.should_not be_alive
|
460
464
|
@book1.open
|
@@ -465,14 +469,20 @@ module RobustExcelOle
|
|
465
469
|
|
466
470
|
it "should recreate an Excel instance with old visible and displayalerts values" do
|
467
471
|
@excel1.visible = true
|
472
|
+
@excel1.Visible.should be true
|
473
|
+
@excel1.visible.should be true
|
468
474
|
@excel1.displayalerts = true
|
475
|
+
@excel1.DisplayAlerts.should be true
|
476
|
+
@excel1.displayalerts.should be true
|
469
477
|
@excel1.close
|
470
478
|
@excel1.should_not be_alive
|
471
479
|
@excel1.recreate
|
472
480
|
@excel1.should be_a Excel
|
473
481
|
@excel1.should be_alive
|
474
482
|
@excel1.Visible.should be true
|
483
|
+
@excel1.visible.should be true
|
475
484
|
@excel1.DisplayAlerts.should be true
|
485
|
+
@excel1.displayalerts.should be true
|
476
486
|
@book1.open
|
477
487
|
@book1.should be_alive
|
478
488
|
@excel1.close
|
@@ -27,7 +27,9 @@ describe Workbook do
|
|
27
27
|
@another_simple_file = @dir + '/another_workbook.xls'
|
28
28
|
@simple_file_xlsm = @dir + '/workbook.xls'
|
29
29
|
@simple_file_xlsx = @dir + '/workbook.xlsx'
|
30
|
+
@simple_file_copy = @dir + '/workbook_copy.xls'
|
30
31
|
@simple_file1 = @simple_file
|
32
|
+
@simple_file_copy1 = @simple_file_copy
|
31
33
|
end
|
32
34
|
|
33
35
|
after do
|
@@ -1083,7 +1085,7 @@ describe Workbook do
|
|
1083
1085
|
context "with filename" do
|
1084
1086
|
|
1085
1087
|
before do
|
1086
|
-
@book = Workbook.open(@
|
1088
|
+
@book = Workbook.open(@simple_file1)
|
1087
1089
|
end
|
1088
1090
|
|
1089
1091
|
after do
|
@@ -1091,7 +1093,7 @@ describe Workbook do
|
|
1091
1093
|
end
|
1092
1094
|
|
1093
1095
|
it "should return full file name" do
|
1094
|
-
@book.filename.should == @
|
1096
|
+
@book.filename.should == @simple_file1
|
1095
1097
|
end
|
1096
1098
|
|
1097
1099
|
it "should return nil for dead book" do
|
@@ -1099,6 +1101,11 @@ describe Workbook do
|
|
1099
1101
|
@book.filename.should == nil
|
1100
1102
|
end
|
1101
1103
|
|
1104
|
+
it "should return right filename when saved as another file name" do
|
1105
|
+
@book.save_as(@simple_file_copy1)
|
1106
|
+
@book.filename.should == @simple_file_copy1
|
1107
|
+
end
|
1108
|
+
|
1102
1109
|
end
|
1103
1110
|
|
1104
1111
|
context "with ==" do
|
@@ -370,11 +370,63 @@ describe Workbook do
|
|
370
370
|
}.to raise_error(WorkbookLinked)
|
371
371
|
end
|
372
372
|
|
373
|
-
it "should
|
373
|
+
it "should change read-only mode of the linked workbook to read-only" do
|
374
374
|
book2 = Workbook.open(@sub_file, :read_only => true)
|
375
375
|
book2.ReadOnly.should be true
|
376
376
|
end
|
377
|
+
|
378
|
+
it "should change read-only mode of the main workbook to read-only" do
|
379
|
+
book1 = Workbook.open(@main_file, :read_only => true)
|
380
|
+
book1.ReadOnly.should be true
|
381
|
+
end
|
382
|
+
|
377
383
|
end
|
384
|
+
|
385
|
+
context "with read-only linked workbook" do
|
386
|
+
|
387
|
+
before do
|
388
|
+
@book1 = Workbook.open(@main_file, read_only: true, visible: true)
|
389
|
+
end
|
390
|
+
|
391
|
+
it "should change read-only mode of the main workbook to read-write" do
|
392
|
+
book2 = Workbook.open(@main_file, :read_only => false)
|
393
|
+
book2.ReadOnly.should be false
|
394
|
+
end
|
395
|
+
|
396
|
+
# here an query occurs whether to save the changes
|
397
|
+
# so far this case is not being controlled with help of options
|
398
|
+
it "should change read-only mode of the unsaved main workbook to read-write" do
|
399
|
+
book1.sheet(1)[1,1] = "foo"
|
400
|
+
book1 = Workbook.open(@main_file, :read_only => false)
|
401
|
+
book1.ReadOnly.should be false
|
402
|
+
book1.Saved.should be false
|
403
|
+
end
|
404
|
+
|
405
|
+
it "should change read-only mode of the main unsaved workbook to read-write" do
|
406
|
+
book2 = Workbook.open(@sub_file, :read_only => false)
|
407
|
+
book2.ReadOnly.should be false
|
408
|
+
end
|
409
|
+
|
410
|
+
it "should change read-only mode of the main workbook to read-write" do
|
411
|
+
book1 = Workbook.open(@main_file, :read_only => false)
|
412
|
+
book1.ReadOnly.should be false
|
413
|
+
end
|
414
|
+
|
415
|
+
end
|
416
|
+
|
417
|
+
context "with read-only" do
|
418
|
+
|
419
|
+
before do
|
420
|
+
@book1 = Workbook.open(@main_file, :read_only => true)
|
421
|
+
end
|
422
|
+
|
423
|
+
it "should change read-only mode from read-only to read-write" do
|
424
|
+
expect{
|
425
|
+
Workbook.open(@main_file, :read_only => false)
|
426
|
+
}.to_not raise_error
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
378
430
|
end
|
379
431
|
|
380
432
|
describe "basic tests with xlsx-workbooks" do
|
@@ -3165,9 +3217,11 @@ describe Workbook do
|
|
3165
3217
|
before do
|
3166
3218
|
@new_book = Workbook.open(@simple_file)
|
3167
3219
|
end
|
3220
|
+
|
3168
3221
|
after do
|
3169
3222
|
@new_book.close
|
3170
3223
|
end
|
3224
|
+
|
3171
3225
|
it "should provide the excel instance of the book" do
|
3172
3226
|
excel = @new_book.excel
|
3173
3227
|
excel.class.should == Excel
|
@@ -3329,6 +3383,7 @@ describe Workbook do
|
|
3329
3383
|
end
|
3330
3384
|
|
3331
3385
|
it "should open xlsx file" do
|
3386
|
+
|
3332
3387
|
book = Workbook.open(@simple_file_xlsx, :visible => true)
|
3333
3388
|
book.close
|
3334
3389
|
end
|
@@ -3380,4 +3435,5 @@ describe Workbook do
|
|
3380
3435
|
end
|
3381
3436
|
end
|
3382
3437
|
end
|
3383
|
-
end
|
3438
|
+
end
|
3439
|
+
|
@@ -311,6 +311,24 @@ describe Workbook do
|
|
311
311
|
|
312
312
|
context "with no books" do
|
313
313
|
|
314
|
+
it "should open the linked workbook" do
|
315
|
+
Workbook.unobtrusively(@sub_file, :writable => true) do |book|
|
316
|
+
book.ReadOnly.should be false
|
317
|
+
book.filename.should == @sub_file
|
318
|
+
end
|
319
|
+
Excel.current.workbooks.should == []
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should open the workbook and its linked workbook" do
|
323
|
+
Workbook.unobtrusively(@main_file, :writable => true) do |book|
|
324
|
+
book.ReadOnly.should be false
|
325
|
+
book.filename.should == @main_file
|
326
|
+
book.excel.workbooks.map{|b| b.filename}.should == [@main_file, @sub_file]
|
327
|
+
end
|
328
|
+
Excel.current.workbooks.map{|b| b.filename}.should == [@sub_file]
|
329
|
+
end
|
330
|
+
|
331
|
+
|
314
332
|
it "should open the linked workbook in read-only" do
|
315
333
|
Workbook.unobtrusively(@sub_file, :writable => false) do |book|
|
316
334
|
book.ReadOnly.should be true
|
@@ -323,14 +341,14 @@ describe Workbook do
|
|
323
341
|
Workbook.unobtrusively(@main_file, :writable => false) do |book|
|
324
342
|
book.ReadOnly.should be true
|
325
343
|
book.filename.should == @main_file
|
326
|
-
book.excel.workbooks.map{|b| b.filename}.should == [@
|
344
|
+
book.excel.workbooks.map{|b| b.filename}.should == [@main_file, @sub_file]
|
327
345
|
end
|
328
346
|
Excel.current.workbooks.map{|b| b.filename}.should == [@sub_file]
|
329
347
|
end
|
330
348
|
|
331
349
|
it "should not write the linked workbook and close it" do
|
332
|
-
Workbook.unobtrusively(@sub_file
|
333
|
-
book.ReadOnly.should be
|
350
|
+
Workbook.unobtrusively(@sub_file) do |book|
|
351
|
+
book.ReadOnly.should be false
|
334
352
|
book.filename.should == @sub_file
|
335
353
|
@old_value = book.sheet(1)[1,1]
|
336
354
|
book.sheet(1)[1,1] = book.sheet(1)[1,1] == "foo" ? "bar" : "foo"
|
@@ -339,8 +357,8 @@ describe Workbook do
|
|
339
357
|
end
|
340
358
|
|
341
359
|
it "should not write the main workbook and close the linked file as well" do
|
342
|
-
Workbook.unobtrusively(@main_file
|
343
|
-
book.ReadOnly.should be
|
360
|
+
Workbook.unobtrusively(@main_file) do |book|
|
361
|
+
book.ReadOnly.should be false
|
344
362
|
book.filename.should == @main_file
|
345
363
|
@old_value = book.sheet(1)[1,1]
|
346
364
|
book.sheet(1)[1,1] = book.sheet(1)[1,1] == "foo" ? "bar" : "foo"
|
@@ -362,7 +380,7 @@ describe Workbook do
|
|
362
380
|
end
|
363
381
|
end
|
364
382
|
|
365
|
-
it "should
|
383
|
+
it "should raise error, when forcing the linked workbook to read-only" do
|
366
384
|
expect{
|
367
385
|
Workbook.unobtrusively(@sub_file, :read_only => true) do
|
368
386
|
end
|
@@ -946,20 +964,6 @@ describe Workbook do
|
|
946
964
|
}.to raise_error(WorkbookNotSaved)
|
947
965
|
end
|
948
966
|
|
949
|
-
=begin
|
950
|
-
it "should remain writable" do
|
951
|
-
Workbook.unobtrusively(@unsaved_file, :read_only => true, :if_unsaved => :save) do |book|
|
952
|
-
book.saved.should be true
|
953
|
-
book.visible.should be true
|
954
|
-
book.writable.should be false
|
955
|
-
end
|
956
|
-
ole_wb = WIN32OLE.connect(@abs_filename)
|
957
|
-
ole_wb.Saved.should be false
|
958
|
-
@ole_e1.Visible.should be true
|
959
|
-
ole_wb.ReadOnly.should be false
|
960
|
-
end
|
961
|
-
=end
|
962
|
-
|
963
967
|
it "should remain unsaved when modifying" do
|
964
968
|
Workbook.unobtrusively(@unsaved_file) do |book|
|
965
969
|
book.sheet(1)[1,1] = "bar" #book.sheet(1)[1,1] == "foo" ? "bar" : "foo"
|
@@ -1386,15 +1390,13 @@ describe Workbook do
|
|
1386
1390
|
end
|
1387
1391
|
|
1388
1392
|
it "should force to read-only" do
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
end
|
1397
|
-
}.to raise_error(WorkbookReadOnly)
|
1393
|
+
Workbook.unobtrusively(@simple_file1, :if_unsaved => :forget, :read_only => true) do |book|
|
1394
|
+
book.ReadOnly.should be true
|
1395
|
+
book.should == @book
|
1396
|
+
book.filename.should == @book.filename
|
1397
|
+
book.excel.should == @book.excel
|
1398
|
+
book.sheet(1)[1,1] = book.sheet(1)[1,1] == "foo" ? "bar" : "foo"
|
1399
|
+
end
|
1398
1400
|
end
|
1399
1401
|
|
1400
1402
|
|
@@ -1696,9 +1698,18 @@ describe Workbook do
|
|
1696
1698
|
end
|
1697
1699
|
|
1698
1700
|
it "should open as read-only" do
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1701
|
+
Workbook.unobtrusively(@simple_file1, :if_unsaved => :accept, :read_only => false, :writable => false) do |book|
|
1702
|
+
book.Readonly.should be false
|
1703
|
+
book.Saved.should be false
|
1704
|
+
book.sheet(1)[1,1].should == @old_value
|
1705
|
+
book.sheet(1)[1,1] = book.sheet(1)[1,1] == "foo" ? "bar" : "foo"
|
1706
|
+
end
|
1707
|
+
@book.ReadOnly.should be true
|
1708
|
+
@book.Saved.should be false
|
1709
|
+
@book.sheet(1)[1,1].should_not == @old_value
|
1710
|
+
@book.close(if_unsaved: :forget)
|
1711
|
+
book2 = Workbook.open(@simple_file1)
|
1712
|
+
book2.sheet(1)[1,1].should_not == @old_value
|
1702
1713
|
end
|
1703
1714
|
|
1704
1715
|
it "should remain read-only and not write, even with :writable => true" do
|
data/spec/worksheet_spec.rb
CHANGED
@@ -315,13 +315,13 @@ describe Worksheet do
|
|
315
315
|
it "should raise an error if name not defined" do
|
316
316
|
expect {
|
317
317
|
@sheet1.namevalue_global("foo")
|
318
|
-
}.to raise_error(NameNotFound, /name "foo"
|
318
|
+
}.to raise_error(NameNotFound, /cannot find name "foo"/)
|
319
319
|
end
|
320
320
|
|
321
321
|
it "should raise an error of coordinates are given instead of a defined name" do
|
322
322
|
expect {
|
323
323
|
@sheet1.namevalue_global("A1")
|
324
|
-
}.to raise_error(NameNotFound, /name "A1"
|
324
|
+
}.to raise_error(NameNotFound, /cannot find name "A1"/)
|
325
325
|
end
|
326
326
|
|
327
327
|
it "should return default value for a range with empty contents" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: robust_excel_ole
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.37'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- traths
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|