robust_excel_ole 1.31 → 1.32
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 +20 -1
- data/README.rdoc +118 -18
- data/___dummy_workbook.xls +0 -0
- data/benchmarking/creek_example.rb +1 -1
- data/benchmarking/roo_example.rb +1 -1
- data/benchmarking/simple_xlsx_reader_example.rb +1 -1
- data/benchmarking/spreadsheet_example.rb +1 -1
- data/docs/README_excel.rdoc +16 -24
- data/docs/README_listobjects.rdoc +176 -0
- data/docs/README_open.rdoc +12 -12
- data/docs/README_ranges.rdoc +72 -55
- data/docs/README_save_close.rdoc +3 -3
- data/docs/README_sheet.rdoc +18 -13
- data/examples/example_ruby_library.rb +2 -2
- data/examples/introductory_examples/example_range.rb +2 -2
- data/examples/modifying_sheets/example_access_sheets_and_cells.rb +6 -6
- data/examples/modifying_sheets/example_add_names.rb +1 -1
- data/examples/modifying_sheets/example_concating.rb +1 -1
- data/examples/modifying_sheets/example_copying.rb +2 -2
- data/examples/modifying_sheets/example_listobjects.rb +86 -0
- data/examples/modifying_sheets/example_naming.rb +1 -1
- data/examples/modifying_sheets/example_ranges.rb +1 -1
- data/examples/open_save_close/example_control_to_excel.rb +1 -1
- data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
- data/examples/open_save_close/example_if_obstructed_save.rb +3 -3
- data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
- data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
- data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -4
- data/examples/open_save_close/example_read_only.rb +1 -1
- data/examples/open_save_close/example_simple.rb +1 -1
- data/examples/open_save_close/example_unobtrusively.rb +3 -3
- data/lib/robust_excel_ole/address_tool.rb +54 -44
- data/lib/robust_excel_ole/base.rb +4 -6
- data/lib/robust_excel_ole/bookstore.rb +2 -16
- data/lib/robust_excel_ole/cell.rb +16 -21
- data/lib/robust_excel_ole/excel.rb +131 -186
- data/lib/robust_excel_ole/general.rb +82 -55
- data/lib/robust_excel_ole/list_object.rb +182 -109
- data/lib/robust_excel_ole/list_row.rb +65 -38
- data/lib/robust_excel_ole/range.rb +125 -93
- data/lib/robust_excel_ole/range_owners.rb +52 -66
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +168 -176
- data/lib/robust_excel_ole/worksheet.rb +177 -141
- data/robust_excel_ole.gemspec +4 -3
- data/spec/bookstore_spec.rb +2 -3
- data/spec/cell_spec.rb +9 -9
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/excel_spec.rb +132 -85
- data/spec/general_spec.rb +47 -15
- data/spec/list_object_spec.rb +258 -145
- data/spec/list_row_spec.rb +218 -0
- data/spec/range_spec.rb +76 -29
- data/spec/spec_helper.rb +15 -1
- data/spec/workbook_spec.rb +75 -34
- data/spec/workbook_specs/workbook_all_spec.rb +2 -1
- data/spec/workbook_specs/workbook_misc_spec.rb +20 -13
- data/spec/workbook_specs/workbook_open_spec.rb +47 -45
- data/spec/workbook_specs/workbook_save_spec.rb +21 -22
- data/spec/workbook_specs/workbook_sheet_spec.rb +3 -3
- data/spec/workbook_specs/workbook_unobtr_spec.rb +303 -303
- data/spec/worksheet_spec.rb +522 -318
- metadata +37 -2
@@ -14,17 +14,15 @@ module RobustExcelOle
|
|
14
14
|
# @param [Hash] opts the options
|
15
15
|
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
16
16
|
# @return [Variant] the contents of a range with given name
|
17
|
-
def namevalue_global(name, opts = { :
|
17
|
+
def namevalue_global(name, opts = { default: :__not_provided })
|
18
18
|
name_obj = begin
|
19
|
-
|
19
|
+
get_name_object(name)
|
20
20
|
rescue NameNotFound => msg
|
21
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
22
21
|
raise
|
23
22
|
end
|
24
23
|
ole_range = name_obj.RefersToRange
|
25
24
|
worksheet = self if self.is_a?(Worksheet)
|
26
25
|
value = begin
|
27
|
-
#name_obj.RefersToRange.Value
|
28
26
|
if !::RANGES_JRUBY_BUG
|
29
27
|
ole_range.Value
|
30
28
|
else
|
@@ -33,11 +31,9 @@ module RobustExcelOle
|
|
33
31
|
end
|
34
32
|
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
|
35
33
|
sheet = if self.is_a?(Worksheet) then self
|
36
|
-
|
37
|
-
elsif self.is_a?(Excel) then self.workbook.sheet(1)
|
34
|
+
elsif self.is_a?(Workbook) then self.sheet(1)
|
38
35
|
end
|
39
36
|
begin
|
40
|
-
#sheet.Evaluate(name_obj.Name).Value
|
41
37
|
# does it result in a range?
|
42
38
|
ole_range = sheet.Evaluate(name_obj.Name)
|
43
39
|
if !::RANGES_JRUBY_BUG
|
@@ -64,33 +60,31 @@ module RobustExcelOle
|
|
64
60
|
# @param [Variant] value the contents of the range
|
65
61
|
# @option opts [Symbol] :color the color of the range when set
|
66
62
|
def set_namevalue_global(name, value, opts = { })
|
67
|
-
begin
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
ole_range.Cells(i+1,j+1).Value = (value.respond_to?(:first) ? value[i][j] : value )
|
83
|
-
end
|
63
|
+
name_obj = begin
|
64
|
+
get_name_object(name)
|
65
|
+
rescue NameNotFound => msg
|
66
|
+
raise
|
67
|
+
end
|
68
|
+
ole_range = name_obj.RefersToRange
|
69
|
+
ole_range.Interior.ColorIndex = opts[:color] unless opts[:color].nil?
|
70
|
+
if !::RANGES_JRUBY_BUG
|
71
|
+
ole_range.Value = value
|
72
|
+
else
|
73
|
+
address_r1c1 = ole_range.AddressLocal(true,true,XlR1C1)
|
74
|
+
row, col = address_tool.as_integer_ranges(address_r1c1)
|
75
|
+
row.each_with_index do |r,i|
|
76
|
+
col.each_with_index do |c,j|
|
77
|
+
ole_range.Cells(i+1,j+1).Value = (value.respond_to?(:pop) ? value[i][j] : value )
|
84
78
|
end
|
85
79
|
end
|
86
|
-
value
|
87
|
-
rescue #WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
|
88
|
-
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
89
80
|
end
|
81
|
+
value
|
82
|
+
rescue #WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
|
83
|
+
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}\n#{$!.message}"
|
90
84
|
end
|
91
85
|
|
92
86
|
# @private
|
93
|
-
def nameval(name, opts = { :
|
87
|
+
def nameval(name, opts = { default: :__not_provided }) # :deprecated: #
|
94
88
|
namevalue_global(name, opts)
|
95
89
|
end
|
96
90
|
|
@@ -100,7 +94,7 @@ module RobustExcelOle
|
|
100
94
|
end
|
101
95
|
|
102
96
|
# @private
|
103
|
-
def rangeval(name, opts = { :
|
97
|
+
def rangeval(name, opts = { default: :__not_provided }) # :deprecated: #
|
104
98
|
namevalue(name, opts)
|
105
99
|
end
|
106
100
|
|
@@ -125,53 +119,45 @@ module RobustExcelOle
|
|
125
119
|
# @params [Address] address of the range
|
126
120
|
def add_name(name, addr, addr_deprecated = :__not_provided)
|
127
121
|
addr = [addr,addr_deprecated] unless addr_deprecated == :__not_provided
|
128
|
-
|
129
|
-
self.Names.Add(name, nil, true, nil, nil, nil, nil, nil, nil, '=' + address_tool.as_r1c1(addr))
|
130
|
-
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
|
131
|
-
raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}"
|
132
|
-
end
|
122
|
+
self.Names.Add(name, nil, true, nil, nil, nil, nil, nil, nil, '=' + address_tool.as_r1c1(addr))
|
133
123
|
name
|
124
|
+
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
|
125
|
+
raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}\n#{$!.message}"
|
134
126
|
end
|
135
127
|
|
136
|
-
|
137
|
-
|
128
|
+
alias set_name add_name # :deprecated :#
|
129
|
+
|
130
|
+
# renames an Excel object
|
131
|
+
# @param [String] old_name the previous name of the Excel object
|
132
|
+
# @param [String] new_name the new name of the Excel object
|
133
|
+
def rename_name(old_name, new_name)
|
134
|
+
item = get_name_object(old_name)
|
135
|
+
item.Name = new_name
|
136
|
+
rescue RobustExcelOle::NameNotFound
|
137
|
+
raise
|
138
|
+
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
139
|
+
raise UnexpectedREOError, "name error with name #{old_name.inspect} in #{File.basename(self.stored_filename).inspect}\n#{$!.message}"
|
138
140
|
end
|
139
141
|
|
140
|
-
#
|
141
|
-
# @param [String] name the previous range name
|
142
|
-
# @param [String] new_name the new range name
|
143
|
-
def rename_range(name, new_name)
|
144
|
-
begin
|
145
|
-
item = self.Names.Item(name)
|
146
|
-
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
147
|
-
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
148
|
-
end
|
149
|
-
begin
|
150
|
-
item.Name = new_name
|
151
|
-
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
152
|
-
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
153
|
-
end
|
154
|
-
end
|
142
|
+
alias rename_range rename_name # :deprecated :#
|
155
143
|
|
156
|
-
# deletes a name of
|
157
|
-
# @param [String] name
|
158
|
-
# @param [String] new_name the new range name
|
144
|
+
# deletes a name of an Excel object
|
145
|
+
# @param [String] name the name of the Excel object
|
159
146
|
def delete_name(name)
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
147
|
+
item = get_name_object(name)
|
148
|
+
item.Delete
|
149
|
+
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
|
150
|
+
raise UnexpectedREOError, "name error with name #{name.inspect} in #{File.basename(self.stored_filename).inspect}\n#{$!.message}"
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [Array] defined names
|
154
|
+
def names
|
155
|
+
self.Names.to_a.map(&:name)
|
156
|
+
end
|
171
157
|
|
172
158
|
private
|
173
159
|
|
174
|
-
def
|
160
|
+
def get_name_object(name)
|
175
161
|
self.Names.Item(name)
|
176
162
|
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException, VBAMethodMissingError
|
177
163
|
begin
|
@@ -11,6 +11,8 @@ module RobustExcelOle
|
|
11
11
|
|
12
12
|
class Workbook < RangeOwners
|
13
13
|
|
14
|
+
include Enumerable
|
15
|
+
|
14
16
|
attr_reader :ole_workbook
|
15
17
|
attr_reader :excel
|
16
18
|
attr_reader :stored_filename
|
@@ -20,16 +22,16 @@ module RobustExcelOle
|
|
20
22
|
using ToReoRefinement
|
21
23
|
|
22
24
|
CORE_DEFAULT_OPEN_OPTS = {
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
25
|
+
default: {excel: :current},
|
26
|
+
force: {},
|
27
|
+
update_links: :never
|
26
28
|
}.freeze
|
27
29
|
|
28
30
|
DEFAULT_OPEN_OPTS = {
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
31
|
+
if_unsaved: :raise,
|
32
|
+
if_obstructed: :raise,
|
33
|
+
if_absent: :raise,
|
34
|
+
if_exists: :raise
|
33
35
|
}.merge(CORE_DEFAULT_OPEN_OPTS).freeze
|
34
36
|
|
35
37
|
ABBREVIATIONS = [
|
@@ -89,7 +91,7 @@ module RobustExcelOle
|
|
89
91
|
process_options(opts)
|
90
92
|
case file_or_workbook
|
91
93
|
when NilClass
|
92
|
-
raise FileNameNotGiven,
|
94
|
+
raise FileNameNotGiven, "filename is nil"
|
93
95
|
when WIN32OLE
|
94
96
|
file = file_or_workbook.Fullname.tr('\\','/')
|
95
97
|
when Workbook
|
@@ -101,7 +103,7 @@ module RobustExcelOle
|
|
101
103
|
file = file_or_workbook.to_path
|
102
104
|
raise FileNotFound, "file #{General.absolute_path(file).inspect} is a directory" if File.directory?(file)
|
103
105
|
else
|
104
|
-
raise TypeREOError,
|
106
|
+
raise TypeREOError, "given object is neither a filename, a Win32ole, nor a Workbook object"
|
105
107
|
end
|
106
108
|
# try to fetch the workbook from the bookstore
|
107
109
|
set_was_open opts, file_or_workbook.is_a?(WIN32OLE)
|
@@ -110,14 +112,14 @@ module RobustExcelOle
|
|
110
112
|
# if readonly is true, then prefer a book that is given in force_excel if this option is set
|
111
113
|
forced_excel = begin
|
112
114
|
(opts[:force][:excel].nil? || opts[:force][:excel] == :current) ?
|
113
|
-
(excel_class.new(:
|
115
|
+
(excel_class.new(reuse: true) if !::CONNECT_JRUBY_BUG) : opts[:force][:excel].to_reo.excel
|
114
116
|
rescue NoMethodError
|
115
117
|
raise TypeREOError, "provided Excel option value is neither an Excel object nor a valid option"
|
116
118
|
end
|
117
119
|
begin
|
118
120
|
book = if File.exists?(file)
|
119
|
-
bookstore.fetch(file, :
|
120
|
-
:
|
121
|
+
bookstore.fetch(file, prefer_writable: !(opts[:read_only]),
|
122
|
+
prefer_excel: (opts[:read_only] ? forced_excel : nil))
|
121
123
|
end
|
122
124
|
rescue
|
123
125
|
raise
|
@@ -153,7 +155,7 @@ module RobustExcelOle
|
|
153
155
|
ole_excel = begin
|
154
156
|
@ole_workbook.Application
|
155
157
|
rescue WIN32OLERuntimeError
|
156
|
-
raise ExcelREOError,
|
158
|
+
raise ExcelREOError, "could not determine the Excel instance\n#{$!.message}"
|
157
159
|
end
|
158
160
|
@excel = excel_class.new(ole_excel)
|
159
161
|
filename = @ole_workbook.Fullname.tr('\\','/')
|
@@ -182,7 +184,7 @@ module RobustExcelOle
|
|
182
184
|
self.class.set_was_open(hash, value)
|
183
185
|
end
|
184
186
|
|
185
|
-
def self.process_options(opts, proc_opts = {:
|
187
|
+
def self.process_options(opts, proc_opts = {use_defaults: true})
|
186
188
|
translate(opts)
|
187
189
|
default_opts = (proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS : CORE_DEFAULT_OPEN_OPTS).dup
|
188
190
|
translate(default_opts)
|
@@ -224,9 +226,9 @@ module RobustExcelOle
|
|
224
226
|
return if @excel && @excel.alive?
|
225
227
|
excel_option = options[:force][:excel] || options[:default][:excel] || :current
|
226
228
|
@excel = if excel_option == :new
|
227
|
-
excel_class.new(:
|
229
|
+
excel_class.new(reuse: false)
|
228
230
|
elsif excel_option == :current
|
229
|
-
excel_class.new(:
|
231
|
+
excel_class.new(reuse: true)
|
230
232
|
elsif excel_option.respond_to?(:to_reo)
|
231
233
|
excel_option.to_reo.excel
|
232
234
|
else
|
@@ -281,14 +283,14 @@ module RobustExcelOle
|
|
281
283
|
|
282
284
|
# connects to an unknown workbook
|
283
285
|
def connect(filename, options)
|
284
|
-
workbooks_number = excel_class.
|
286
|
+
workbooks_number = excel_class.instance_count==0 ? 0 : excel_class.current.Workbooks.Count
|
285
287
|
@ole_workbook = begin
|
286
288
|
WIN32OLE.connect(General.absolute_path(filename))
|
287
289
|
rescue
|
288
290
|
if $!.message =~ /moniker/
|
289
291
|
raise WorkbookConnectingBlockingError, "some workbook is blocking when connecting"
|
290
292
|
else
|
291
|
-
raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook"
|
293
|
+
raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook\n#{$!.message}"
|
292
294
|
end
|
293
295
|
end
|
294
296
|
ole_excel = begin
|
@@ -297,7 +299,7 @@ module RobustExcelOle
|
|
297
299
|
if $!.message =~ /dispid/
|
298
300
|
raise WorkbookConnectingUnsavedError, "workbook is unsaved when connecting"
|
299
301
|
else
|
300
|
-
raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook"
|
302
|
+
raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook\n#{$!.message}"
|
301
303
|
end
|
302
304
|
end
|
303
305
|
set_was_open options, (ole_excel.Workbooks.Count == workbooks_number)
|
@@ -355,7 +357,7 @@ module RobustExcelOle
|
|
355
357
|
when :close_if_saved
|
356
358
|
if !@ole_workbook.Saved
|
357
359
|
raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{blocked_filename.call}" +
|
358
|
-
"\nHint: Use the option :
|
360
|
+
"\nHint: Use the option if_blocked: :save to save the workbook"
|
359
361
|
else
|
360
362
|
manage_forgetting_workbook(filename, options)
|
361
363
|
end
|
@@ -406,7 +408,7 @@ module RobustExcelOle
|
|
406
408
|
end
|
407
409
|
|
408
410
|
def manage_new_excel(filename, options)
|
409
|
-
@excel = excel_class.new(:
|
411
|
+
@excel = excel_class.new(reuse: false)
|
410
412
|
@ole_workbook = nil
|
411
413
|
open_or_create_workbook(filename, options)
|
412
414
|
end
|
@@ -420,50 +422,39 @@ module RobustExcelOle
|
|
420
422
|
raise WorkbookLinked, "read-only mode of this workbook cannot be changed, because it is being used by another workbook"
|
421
423
|
elsif want_change_readonly.nil?
|
422
424
|
raise WorkbookLinked, "workbook is being used by another workbook"
|
423
|
-
else
|
424
|
-
raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
|
425
425
|
end
|
426
|
-
|
426
|
+
end
|
427
|
+
if msg.message !~ /800A03EC/ || msg.message !~ /0x80020009/ || want_change_readonly==false
|
427
428
|
raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
|
428
429
|
end
|
429
430
|
end
|
430
431
|
|
431
432
|
def open_or_create_workbook(filename, options)
|
432
433
|
return if @ole_workbook && options[:if_unsaved] != :alert && options[:if_unsaved] != :excel &&
|
433
|
-
|
434
|
+
(options[:read_only].nil? || options[:read_only]==@ole_workbook.ReadOnly )
|
435
|
+
abs_filename = General.absolute_path(filename)
|
436
|
+
workbooks = begin
|
437
|
+
@excel.Workbooks
|
438
|
+
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
439
|
+
raise UnexpectedREOError, "cannot access workbooks: #{msg.message} #{msg.backtrace}"
|
440
|
+
end
|
434
441
|
begin
|
435
|
-
|
436
|
-
|
437
|
-
workbooks
|
438
|
-
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
439
|
-
raise UnexpectedREOError, "cannot access workbooks: #{msg.message} #{msg.backtrace}"
|
440
|
-
end
|
441
|
-
begin
|
442
|
-
with_workaround_linked_workbooks_excel2007(options) do
|
443
|
-
# temporary workaround until jruby-win32ole implements named parameters (Java::JavaLang::RuntimeException (createVariant() not implemented for class org.jruby.RubyHash)
|
444
|
-
workbooks.Open(abs_filename,
|
445
|
-
updatelinks_vba(options[:update_links]),
|
446
|
-
options[:read_only] )
|
447
|
-
end
|
448
|
-
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
449
|
-
# for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
|
450
|
-
# if yes: distinguish these events
|
451
|
-
want_change_readonly = !options[:read_only].nil? && (options[:read_only] != @ole_workbook.ReadOnly)
|
452
|
-
explore_workbook_error(msg, want_change_readonly)
|
453
|
-
end
|
454
|
-
begin
|
455
|
-
# workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
|
456
|
-
begin
|
457
|
-
@ole_workbook = workbooks.Item(File.basename(filename))
|
458
|
-
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
459
|
-
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
|
460
|
-
end
|
461
|
-
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
462
|
-
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
|
442
|
+
with_workaround_linked_workbooks_excel2007(options) do
|
443
|
+
# temporary workaround until jruby-win32ole implements named parameters (Java::JavaLang::RuntimeException (createVariant() not implemented for class org.jruby.RubyHash)
|
444
|
+
workbooks.Open(abs_filename, updatelinks_vba(options[:update_links]), options[:read_only] )
|
463
445
|
end
|
446
|
+
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
447
|
+
# for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?; distinguish these events
|
448
|
+
want_change_readonly = !options[:read_only].nil? && (options[:read_only] != @ole_workbook.ReadOnly)
|
464
449
|
end
|
465
|
-
|
466
|
-
|
450
|
+
# workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
|
451
|
+
@ole_workbook = begin
|
452
|
+
workbooks.Item(File.basename(filename))
|
453
|
+
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
454
|
+
raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
467
458
|
# translating the option UpdateLinks from REO to VBA
|
468
459
|
# setting UpdateLinks works only if calculation mode is automatic,
|
469
460
|
# parameter 'UpdateLinks' has no effect
|
@@ -502,7 +493,7 @@ module RobustExcelOle
|
|
502
493
|
# @param [String] filename the filename under which the new workbook should be saved
|
503
494
|
# @param [Hash] opts the options as in Workbook::open
|
504
495
|
def self.create(filename, opts = { })
|
505
|
-
open(filename, :
|
496
|
+
open(filename, if_absent: :create)
|
506
497
|
end
|
507
498
|
|
508
499
|
# closes the workbook, if it is alive
|
@@ -517,34 +508,30 @@ module RobustExcelOle
|
|
517
508
|
# :alert or :excel -> gives control to excel
|
518
509
|
# @raise WorkbookNotSaved if the option :if_unsaved is :raise and the workbook is unsaved
|
519
510
|
# @raise OptionInvalid if the options is invalid
|
520
|
-
def close(opts = {:
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
close_workbook
|
529
|
-
when :forget
|
530
|
-
@excel.with_displayalerts(false) { close_workbook }
|
531
|
-
when :keep_open
|
532
|
-
# nothing
|
533
|
-
when :alert, :excel
|
534
|
-
@excel.with_displayalerts(true) { close_workbook }
|
535
|
-
else
|
536
|
-
raise OptionInvalid, ":if_unsaved: invalid option: #{opts[:if_unsaved].inspect}" +
|
537
|
-
"\nHint: Valid values are :raise, :save, :keep_open, :alert, :excel"
|
538
|
-
end
|
539
|
-
else
|
511
|
+
def close(opts = {if_unsaved: :raise})
|
512
|
+
return close_workbook unless (alive? && !@ole_workbook.Saved && writable)
|
513
|
+
case opts[:if_unsaved]
|
514
|
+
when :raise
|
515
|
+
raise WorkbookNotSaved, "workbook is unsaved: #{File.basename(self.stored_filename).inspect}" +
|
516
|
+
"\nHint: Use option :save or :forget to close the workbook with or without saving"
|
517
|
+
when :save
|
518
|
+
save
|
540
519
|
close_workbook
|
520
|
+
when :forget
|
521
|
+
@excel.with_displayalerts(false) { close_workbook }
|
522
|
+
when :keep_open
|
523
|
+
# nothing
|
524
|
+
when :alert, :excel
|
525
|
+
@excel.with_displayalerts(true) { close_workbook }
|
526
|
+
else
|
527
|
+
raise OptionInvalid, ":if_unsaved: invalid option: #{opts[:if_unsaved].inspect}" +
|
528
|
+
"\nHint: Valid values are :raise, :save, :keep_open, :alert, :excel"
|
541
529
|
end
|
542
530
|
end
|
543
531
|
|
544
532
|
private
|
545
533
|
|
546
534
|
def close_workbook
|
547
|
-
#@ole_workbook.Close if alive?
|
548
535
|
if alive?
|
549
536
|
begin
|
550
537
|
@ole_workbook.Close
|
@@ -568,19 +555,19 @@ module RobustExcelOle
|
|
568
555
|
end
|
569
556
|
|
570
557
|
def for_reading(opts = { }, &block)
|
571
|
-
unobtrusively({:
|
558
|
+
unobtrusively({writable: false}.merge(opts), &block)
|
572
559
|
end
|
573
560
|
|
574
561
|
def for_modifying(opts = { }, &block)
|
575
|
-
unobtrusively({:
|
562
|
+
unobtrusively({writable: true}.merge(opts), &block)
|
576
563
|
end
|
577
564
|
|
578
565
|
def self.for_reading(arg, opts = { }, &block)
|
579
|
-
unobtrusively(arg, {:
|
566
|
+
unobtrusively(arg, {writable: false}.merge(opts), &block)
|
580
567
|
end
|
581
568
|
|
582
569
|
def self.for_modifying(arg, opts = { }, &block)
|
583
|
-
unobtrusively(arg, {:
|
570
|
+
unobtrusively(arg, {writable: true}.merge(opts), &block)
|
584
571
|
end
|
585
572
|
|
586
573
|
# allows to read or modify a workbook such that its state remains unchanged
|
@@ -608,29 +595,27 @@ module RobustExcelOle
|
|
608
595
|
|
609
596
|
def self.unobtrusively_opening(file, opts, book_is_alive, &block)
|
610
597
|
process_options(opts)
|
611
|
-
opts = {:
|
612
|
-
raise OptionInvalid,
|
598
|
+
opts = {if_closed: :current, keep_open: false}.merge(opts)
|
599
|
+
raise OptionInvalid, "contradicting options" if opts[:writable] && opts[:read_only]
|
613
600
|
if book_is_alive.nil?
|
614
601
|
prefer_writable = ((!(opts[:read_only]) || opts[:writable] == true) &&
|
615
602
|
!(opts[:read_only].nil? && opts[:writable] == false))
|
616
|
-
known_book = bookstore.fetch(file, :
|
603
|
+
known_book = bookstore.fetch(file, prefer_writable: prefer_writable)
|
617
604
|
end
|
618
605
|
excel_opts = if (book_is_alive==false || (book_is_alive.nil? && (known_book.nil? || !known_book.alive?)))
|
619
|
-
{:
|
606
|
+
{force: {excel: opts[:if_closed]}}
|
620
607
|
else
|
621
|
-
{:
|
608
|
+
{force: {excel: opts[:force][:excel]}, default: {excel: opts[:default][:excel]}}
|
622
609
|
end
|
623
|
-
open_opts = excel_opts.merge({:
|
610
|
+
open_opts = excel_opts.merge({if_unsaved: :accept})
|
624
611
|
begin
|
625
612
|
open_opts[:was_open] = nil
|
626
613
|
book = open(file, open_opts)
|
627
|
-
#book = open(file, :read_only => !opts[:writable]) if !opts[:writable].nil? && !open_opts[:was_open]
|
628
614
|
was_visible = book.visible
|
629
615
|
was_writable = book.writable
|
630
616
|
was_saved = book.saved
|
631
617
|
was_check_compatibility = book.check_compatibility
|
632
618
|
was_calculation = book.excel.properties[:calculation]
|
633
|
-
#opts[:read_only] = !opts[:writable] unless (opts[:writable].nil? || open_opts[:was_open])
|
634
619
|
opts[:read_only] = !opts[:writable] unless (!opts[:read_only].nil? || opts[:writable].nil? || open_opts[:was_open])
|
635
620
|
book.send :apply_options, file, opts
|
636
621
|
yield book
|
@@ -638,10 +623,9 @@ module RobustExcelOle
|
|
638
623
|
if book && book.alive?
|
639
624
|
do_not_write = opts[:read_only] || opts[:writable]==false
|
640
625
|
book.save unless book.saved || do_not_write || !book.writable
|
641
|
-
#if opts[:writable].nil? && opts[:was_open] &&
|
642
626
|
if ((opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable))
|
643
|
-
book.send :apply_options, file, opts.merge({:
|
644
|
-
:
|
627
|
+
book.send :apply_options, file, opts.merge({read_only: !was_writable,
|
628
|
+
if_unsaved: (opts[:writable]==false ? :forget : :save)})
|
645
629
|
end
|
646
630
|
was_open = open_opts[:was_open]
|
647
631
|
if was_open
|
@@ -661,20 +645,20 @@ module RobustExcelOle
|
|
661
645
|
# @options options
|
662
646
|
def reopen(options = { })
|
663
647
|
book = self.class.open(@stored_filename, options)
|
664
|
-
raise WorkbookREOError(
|
648
|
+
raise WorkbookREOError("cannot reopen workbook\n#{$!.message}") unless book && book.alive?
|
665
649
|
book
|
666
650
|
end
|
667
651
|
|
668
652
|
# simple save of a workbook.
|
669
653
|
# @return [Boolean] true, if successfully saved, nil otherwise
|
670
654
|
def save(opts = { }) # option opts is deprecated #
|
671
|
-
raise ObjectNotAlive,
|
672
|
-
raise WorkbookReadOnly,
|
655
|
+
raise ObjectNotAlive, "workbook is not alive" unless alive?
|
656
|
+
raise WorkbookReadOnly, "Not opened for writing (opened with :read_only option)" if @ole_workbook.ReadOnly
|
673
657
|
begin
|
674
658
|
@ole_workbook.Save
|
675
659
|
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
676
660
|
if msg.message =~ /SaveAs/ && msg.message =~ /Workbook/
|
677
|
-
raise WorkbookNotSaved,
|
661
|
+
raise WorkbookNotSaved, "workbook not saved"
|
678
662
|
else
|
679
663
|
raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
|
680
664
|
end
|
@@ -700,64 +684,72 @@ module RobustExcelOle
|
|
700
684
|
# otherwise raises an exception
|
701
685
|
# @return [Workbook], the book itself, if successfully saved, raises an exception otherwise
|
702
686
|
def save_as(file, options = { })
|
703
|
-
raise FileNameNotGiven,
|
704
|
-
raise ObjectNotAlive,
|
705
|
-
raise WorkbookReadOnly,
|
687
|
+
raise FileNameNotGiven, "filename is nil" if file.nil?
|
688
|
+
raise ObjectNotAlive, "workbook is not alive" unless alive?
|
689
|
+
raise WorkbookReadOnly, "Not opened for writing (opened with :read_only option)" if @ole_workbook.ReadOnly
|
706
690
|
raise(FileNotFound, "file #{General.absolute_path(file).inspect} is a directory") if File.directory?(file)
|
707
|
-
self.class.process_options(options)
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
raise FileAlreadyExists, "file already exists: #{File.basename(file).inspect}" +
|
728
|
-
"\nHint: Use option :if_exists => :overwrite, if you want to overwrite the file"
|
691
|
+
self.class.process_options(options)
|
692
|
+
begin
|
693
|
+
saveas_manage_if_exists(file, options)
|
694
|
+
saveas_manage_if_blocked(file, options)
|
695
|
+
save_as_workbook(file, options)
|
696
|
+
rescue AlreadyManaged
|
697
|
+
nil
|
698
|
+
end
|
699
|
+
self
|
700
|
+
end
|
701
|
+
|
702
|
+
private
|
703
|
+
|
704
|
+
def saveas_manage_if_exists(file, options)
|
705
|
+
return unless File.exist?(file)
|
706
|
+
case options[:if_exists]
|
707
|
+
when :overwrite
|
708
|
+
if file == self.filename
|
709
|
+
save
|
710
|
+
raise AlreadyManaged
|
729
711
|
else
|
730
|
-
|
731
|
-
|
712
|
+
begin
|
713
|
+
File.delete(file)
|
714
|
+
rescue Errno::EACCES
|
715
|
+
raise WorkbookBeingUsed, "workbook is open and being used in an Excel instance"
|
716
|
+
end
|
732
717
|
end
|
718
|
+
when :alert, :excel
|
719
|
+
@excel.with_displayalerts(true){ save_as_workbook(file, options) }
|
720
|
+
raise AlreadyManaged
|
721
|
+
when :raise
|
722
|
+
raise FileAlreadyExists, "file already exists: #{File.basename(file).inspect}" +
|
723
|
+
"\nHint: Use option if_exists: :overwrite, if you want to overwrite the file"
|
724
|
+
else
|
725
|
+
raise OptionInvalid, ":if_exists: invalid option: #{options[:if_exists].inspect}" +
|
726
|
+
"\nHint: Valid values are :raise, :overwrite, :alert, :excel"
|
733
727
|
end
|
728
|
+
end
|
729
|
+
|
730
|
+
def saveas_manage_if_blocked(file, options)
|
734
731
|
other_workbook = @excel.Workbooks.Item(File.basename(file)) rescue nil
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
end
|
750
|
-
else
|
751
|
-
raise OptionInvalid, ":if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
|
752
|
-
"\nHint: Valid values are :raise, :forget, :save, :close_if_saved"
|
732
|
+
return unless other_workbook && self.filename != other_workbook.Fullname.tr('\\','/')
|
733
|
+
case options[:if_obstructed]
|
734
|
+
when :raise
|
735
|
+
raise WorkbookBlocked, "blocked by another workbook: #{other_workbook.Fullname.tr('\\','/')}" +
|
736
|
+
"\nHint: Use the option :if_blocked with values :forget or :save to
|
737
|
+
close or save and close the blocking workbook"
|
738
|
+
when :forget
|
739
|
+
# nothing
|
740
|
+
when :save
|
741
|
+
other_workbook.Save
|
742
|
+
when :close_if_saved
|
743
|
+
unless other_workbook.Saved
|
744
|
+
raise WorkbookBlocked, "blocking workbook is unsaved: #{File.basename(file).inspect}" +
|
745
|
+
"\nHint: Use option if_blocked: :save to save the blocking workbooks"
|
753
746
|
end
|
754
|
-
|
747
|
+
else
|
748
|
+
raise OptionInvalid, "if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
|
749
|
+
"\nHint: Valid values are :raise, :forget, :save, :close_if_saved"
|
755
750
|
end
|
756
|
-
|
757
|
-
|
758
|
-
end
|
759
|
-
|
760
|
-
private
|
751
|
+
other_workbook.Close
|
752
|
+
end
|
761
753
|
|
762
754
|
def save_as_workbook(file, options)
|
763
755
|
dirname, basename = File.split(file)
|
@@ -778,6 +770,9 @@ module RobustExcelOle
|
|
778
770
|
end
|
779
771
|
end
|
780
772
|
|
773
|
+
class AlreadyManaged < Exception
|
774
|
+
end
|
775
|
+
|
781
776
|
def store_myself
|
782
777
|
bookstore.store(self)
|
783
778
|
@stored_filename = filename
|
@@ -787,7 +782,7 @@ module RobustExcelOle
|
|
787
782
|
|
788
783
|
# closes a given file if it is open
|
789
784
|
# @options opts [Symbol] :if_unsaved
|
790
|
-
def self.close(file, opts = {:
|
785
|
+
def self.close(file, opts = {if_unsaved: :raise})
|
791
786
|
book = begin
|
792
787
|
bookstore.fetch(file)
|
793
788
|
rescue
|
@@ -825,21 +820,20 @@ module RobustExcelOle
|
|
825
820
|
@ole_workbook.Worksheets.Count
|
826
821
|
end
|
827
822
|
|
823
|
+
# @return [Enumerator] traversing all worksheet objects
|
828
824
|
def each
|
829
|
-
|
830
|
-
|
825
|
+
if block_given?
|
826
|
+
@ole_workbook.Worksheets.lazy.each do |ole_worksheet|
|
827
|
+
yield worksheet_class.new(ole_worksheet)
|
828
|
+
end
|
829
|
+
else
|
830
|
+
to_enum(:each).lazy
|
831
831
|
end
|
832
832
|
end
|
833
833
|
|
834
|
-
def worksheets
|
835
|
-
result = []
|
836
|
-
each { |worksheet| result << worksheet }
|
837
|
-
result
|
838
|
-
end
|
839
|
-
|
840
834
|
def each_with_index(offset = 0)
|
841
835
|
i = offset
|
842
|
-
@ole_workbook.Worksheets.each do |sheet|
|
836
|
+
@ole_workbook.Worksheets.lazy.each do |sheet|
|
843
837
|
yield worksheet_class.new(sheet), i
|
844
838
|
i += 1
|
845
839
|
end
|
@@ -880,7 +874,7 @@ module RobustExcelOle
|
|
880
874
|
end
|
881
875
|
end
|
882
876
|
rescue # WIN32OLERuntimeError, NameNotFound, Java::OrgRacobCom::ComFailException
|
883
|
-
raise WorksheetREOError, "could not add given worksheet #{sheet.inspect}"
|
877
|
+
raise WorksheetREOError, "could not add given worksheet #{sheet.inspect}\n#{$!.message}"
|
884
878
|
end
|
885
879
|
new_sheet = worksheet_class.new(ole_workbook.Activesheet)
|
886
880
|
new_sheet.name = new_sheet_name if new_sheet_name
|
@@ -924,7 +918,7 @@ module RobustExcelOle
|
|
924
918
|
def range(name_or_worksheet, name_or_address = :__not_provided, address2 = :__not_provided)
|
925
919
|
if name_or_worksheet.respond_to?(:gsub)
|
926
920
|
name = name_or_worksheet
|
927
|
-
RobustExcelOle::Range.new(
|
921
|
+
RobustExcelOle::Range.new(get_name_object(name).RefersToRange)
|
928
922
|
else
|
929
923
|
begin
|
930
924
|
worksheet = name_or_worksheet.to_reo
|
@@ -953,7 +947,7 @@ module RobustExcelOle
|
|
953
947
|
# @param [Hash] opts
|
954
948
|
def for_this_workbook(opts)
|
955
949
|
return unless alive?
|
956
|
-
self.class.process_options(opts, :
|
950
|
+
self.class.process_options(opts, use_defaults: false)
|
957
951
|
self.send :apply_options, @stored_filename, opts
|
958
952
|
end
|
959
953
|
|
@@ -1058,7 +1052,8 @@ module RobustExcelOle
|
|
1058
1052
|
|
1059
1053
|
# @private
|
1060
1054
|
def inspect
|
1061
|
-
|
1055
|
+
#{}"#<Workbook: #{("not alive " unless alive?)} #{(File.basename(self.filename) if alive?)} #{@excel}>"
|
1056
|
+
"#<Workbook: #{(alive? ? File.basename(self.filename) : "not alive")} #{@excel} >"
|
1062
1057
|
end
|
1063
1058
|
|
1064
1059
|
using ParentRefinement
|
@@ -1100,23 +1095,20 @@ module RobustExcelOle
|
|
1100
1095
|
private
|
1101
1096
|
|
1102
1097
|
def method_missing(name, *args)
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
end
|
1111
|
-
else
|
1112
|
-
begin
|
1113
|
-
@ole_workbook.send(name, *args)
|
1114
|
-
rescue NoMethodError
|
1115
|
-
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
1116
|
-
end
|
1098
|
+
super unless name.to_s[0,1] =~ /[A-Z]/
|
1099
|
+
raise ObjectNotAlive, 'method missing: workbook not alive' unless alive?
|
1100
|
+
if ::ERRORMESSAGE_JRUBY_BUG
|
1101
|
+
begin
|
1102
|
+
@ole_workbook.send(name, *args)
|
1103
|
+
rescue Java::OrgRacobCom::ComFailException
|
1104
|
+
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
1117
1105
|
end
|
1118
1106
|
else
|
1119
|
-
|
1107
|
+
begin
|
1108
|
+
@ole_workbook.send(name, *args)
|
1109
|
+
rescue NoMethodError
|
1110
|
+
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
1111
|
+
end
|
1120
1112
|
end
|
1121
1113
|
end
|
1122
1114
|
|