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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +20 -1
  3. data/README.rdoc +118 -18
  4. data/___dummy_workbook.xls +0 -0
  5. data/benchmarking/creek_example.rb +1 -1
  6. data/benchmarking/roo_example.rb +1 -1
  7. data/benchmarking/simple_xlsx_reader_example.rb +1 -1
  8. data/benchmarking/spreadsheet_example.rb +1 -1
  9. data/docs/README_excel.rdoc +16 -24
  10. data/docs/README_listobjects.rdoc +176 -0
  11. data/docs/README_open.rdoc +12 -12
  12. data/docs/README_ranges.rdoc +72 -55
  13. data/docs/README_save_close.rdoc +3 -3
  14. data/docs/README_sheet.rdoc +18 -13
  15. data/examples/example_ruby_library.rb +2 -2
  16. data/examples/introductory_examples/example_range.rb +2 -2
  17. data/examples/modifying_sheets/example_access_sheets_and_cells.rb +6 -6
  18. data/examples/modifying_sheets/example_add_names.rb +1 -1
  19. data/examples/modifying_sheets/example_concating.rb +1 -1
  20. data/examples/modifying_sheets/example_copying.rb +2 -2
  21. data/examples/modifying_sheets/example_listobjects.rb +86 -0
  22. data/examples/modifying_sheets/example_naming.rb +1 -1
  23. data/examples/modifying_sheets/example_ranges.rb +1 -1
  24. data/examples/open_save_close/example_control_to_excel.rb +1 -1
  25. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
  26. data/examples/open_save_close/example_if_obstructed_save.rb +3 -3
  27. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  28. data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
  29. data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -4
  30. data/examples/open_save_close/example_read_only.rb +1 -1
  31. data/examples/open_save_close/example_simple.rb +1 -1
  32. data/examples/open_save_close/example_unobtrusively.rb +3 -3
  33. data/lib/robust_excel_ole/address_tool.rb +54 -44
  34. data/lib/robust_excel_ole/base.rb +4 -6
  35. data/lib/robust_excel_ole/bookstore.rb +2 -16
  36. data/lib/robust_excel_ole/cell.rb +16 -21
  37. data/lib/robust_excel_ole/excel.rb +131 -186
  38. data/lib/robust_excel_ole/general.rb +82 -55
  39. data/lib/robust_excel_ole/list_object.rb +182 -109
  40. data/lib/robust_excel_ole/list_row.rb +65 -38
  41. data/lib/robust_excel_ole/range.rb +125 -93
  42. data/lib/robust_excel_ole/range_owners.rb +52 -66
  43. data/lib/robust_excel_ole/version.rb +1 -1
  44. data/lib/robust_excel_ole/workbook.rb +168 -176
  45. data/lib/robust_excel_ole/worksheet.rb +177 -141
  46. data/robust_excel_ole.gemspec +4 -3
  47. data/spec/bookstore_spec.rb +2 -3
  48. data/spec/cell_spec.rb +9 -9
  49. data/spec/data/more_data/workbook.xls +0 -0
  50. data/spec/excel_spec.rb +132 -85
  51. data/spec/general_spec.rb +47 -15
  52. data/spec/list_object_spec.rb +258 -145
  53. data/spec/list_row_spec.rb +218 -0
  54. data/spec/range_spec.rb +76 -29
  55. data/spec/spec_helper.rb +15 -1
  56. data/spec/workbook_spec.rb +75 -34
  57. data/spec/workbook_specs/workbook_all_spec.rb +2 -1
  58. data/spec/workbook_specs/workbook_misc_spec.rb +20 -13
  59. data/spec/workbook_specs/workbook_open_spec.rb +47 -45
  60. data/spec/workbook_specs/workbook_save_spec.rb +21 -22
  61. data/spec/workbook_specs/workbook_sheet_spec.rb +3 -3
  62. data/spec/workbook_specs/workbook_unobtr_spec.rb +303 -303
  63. data/spec/worksheet_spec.rb +522 -318
  64. 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 = { :default => :__not_provided })
17
+ def namevalue_global(name, opts = { default: :__not_provided })
18
18
  name_obj = begin
19
- name_object(name)
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
- elsif self.is_a?(Workbook) then self.sheet(1)
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
- name_obj = begin
69
- name_object(name)
70
- rescue NameNotFound => msg
71
- raise
72
- end
73
- ole_range = name_object(name).RefersToRange
74
- ole_range.Interior.ColorIndex = opts[:color] unless opts[:color].nil?
75
- if !::RANGES_JRUBY_BUG
76
- ole_range.Value = value
77
- else
78
- address_r1c1 = ole_range.AddressLocal(true,true,XlR1C1)
79
- row, col = address_tool.as_integer_ranges(address_r1c1)
80
- row.each_with_index do |r,i|
81
- col.each_with_index do |c,j|
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 = { :default => :__not_provided }) # :deprecated: #
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 = { :default => :__not_provided }) # :deprecated: #
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
- begin
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
- def set_name(name,row,column) # :deprecated :#
137
- add_name(name,row,column)
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
- # renames a range
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 a range
157
- # @param [String] name the previous range 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
- begin
161
- item = self.Names.Item(name)
162
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
163
- raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
164
- end
165
- begin
166
- item.Delete
167
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
168
- raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
169
- end
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 name_object(name)
160
+ def get_name_object(name)
175
161
  self.Names.Item(name)
176
162
  rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException, VBAMethodMissingError
177
163
  begin
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "1.31"
2
+ VERSION = "1.32"
3
3
  end
@@ -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
- :default => {:excel => :current},
24
- :force => {},
25
- :update_links => :never
25
+ default: {excel: :current},
26
+ force: {},
27
+ update_links: :never
26
28
  }.freeze
27
29
 
28
30
  DEFAULT_OPEN_OPTS = {
29
- :if_unsaved => :raise,
30
- :if_obstructed => :raise,
31
- :if_absent => :raise,
32
- :if_exists => :raise
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, 'filename is nil'
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, 'given object is neither a filename, a Win32ole, nor a Workbook object'
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(:reuse => true) if !::CONNECT_JRUBY_BUG) : opts[:force][:excel].to_reo.excel
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, :prefer_writable => !(opts[:read_only]),
120
- :prefer_excel => (opts[:read_only] ? forced_excel : nil))
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, 'could not determine the Excel instance'
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 = {:use_defaults => true})
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(:reuse => false)
229
+ excel_class.new(reuse: false)
228
230
  elsif excel_option == :current
229
- excel_class.new(:reuse => true)
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.excels_number==0 ? 0 : excel_class.current.Workbooks.Count
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 :if_blocked => :save to save the workbook"
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(:reuse => false)
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
- else
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
- (options[:read_only].nil? || options[:read_only]==@ole_workbook.ReadOnly )
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
- abs_filename = General.absolute_path(filename)
436
- begin
437
- workbooks = @excel.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
- end
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, :if_absent => :create)
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 = {:if_unsaved => :raise})
521
- if alive? && !@ole_workbook.Saved && writable
522
- case opts[:if_unsaved]
523
- when :raise
524
- raise WorkbookNotSaved, "workbook is unsaved: #{File.basename(self.stored_filename).inspect}" +
525
- "\nHint: Use option :save or :forget to close the workbook with or without saving"
526
- when :save
527
- save
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({:writable => false}.merge(opts), &block)
558
+ unobtrusively({writable: false}.merge(opts), &block)
572
559
  end
573
560
 
574
561
  def for_modifying(opts = { }, &block)
575
- unobtrusively({:writable => true}.merge(opts), &block)
562
+ unobtrusively({writable: true}.merge(opts), &block)
576
563
  end
577
564
 
578
565
  def self.for_reading(arg, opts = { }, &block)
579
- unobtrusively(arg, {:writable => false}.merge(opts), &block)
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, {:writable => true}.merge(opts), &block)
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 = {:if_closed => :current, :keep_open => false}.merge(opts)
612
- raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
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, :prefer_writable => prefer_writable)
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
- {:force => {:excel => opts[:if_closed]}}
606
+ {force: {excel: opts[:if_closed]}}
620
607
  else
621
- {:force => {:excel => opts[:force][:excel]}, :default => {:excel => opts[:default][:excel]}}
608
+ {force: {excel: opts[:force][:excel]}, default: {excel: opts[:default][:excel]}}
622
609
  end
623
- open_opts = excel_opts.merge({:if_unsaved => :accept})
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({:read_only => !was_writable,
644
- :if_unsaved => (opts[:writable]==false ? :forget : :save)})
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('cannot reopen book') unless book && book.alive?
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, 'workbook is not alive' unless alive?
672
- raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
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, 'workbook not saved'
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, 'filename is nil' if file.nil?
704
- raise ObjectNotAlive, 'workbook is not alive' unless alive?
705
- raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
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
- if File.exist?(file)
709
- case options[:if_exists]
710
- when :overwrite
711
- if file == self.filename
712
- save
713
- return self
714
- else
715
- begin
716
- File.delete(file)
717
- rescue Errno::EACCES
718
- raise WorkbookBeingUsed, 'workbook is open and being used in an Excel instance'
719
- end
720
- end
721
- when :alert, :excel
722
- @excel.with_displayalerts true do
723
- save_as_workbook(file, options)
724
- end
725
- return self
726
- when :raise
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
- raise OptionInvalid, ":if_exists: invalid option: #{options[:if_exists].inspect}" +
731
- "\nHint: Valid values are :raise, :overwrite, :alert, :excel"
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
- if other_workbook && self.filename != other_workbook.Fullname.tr('\\','/')
736
- case options[:if_obstructed]
737
- when :raise
738
- raise WorkbookBlocked, "blocked by another workbook: #{other_workbook.Fullname.tr('\\','/')}" +
739
- "\nHint: Use the option :if_blocked with values :forget or :save to
740
- close or save and close the blocking workbook"
741
- when :forget
742
- # nothing
743
- when :save
744
- other_workbook.Save
745
- when :close_if_saved
746
- unless other_workbook.Saved
747
- raise WorkbookBlocked, "blocking workbook is unsaved: #{File.basename(file).inspect}" +
748
- "\nHint: Use option :if_blocked => :save to save the blocking workbooks"
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
- other_workbook.Close
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
- save_as_workbook(file, options)
757
- self
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 = {:if_unsaved => :raise})
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
- @ole_workbook.Worksheets.each do |sheet|
830
- yield worksheet_class.new(sheet)
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(name_object(name).RefersToRange)
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, :use_defaults => false)
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
- '#<Workbook: ' + ('not alive ' unless alive?).to_s + (File.basename(self.filename) if alive?).to_s + " #{@excel}" + '>'
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
- if name.to_s[0,1] =~ /[A-Z]/
1104
- raise ObjectNotAlive, 'method missing: workbook not alive' unless alive?
1105
- if ::ERRORMESSAGE_JRUBY_BUG
1106
- begin
1107
- @ole_workbook.send(name, *args)
1108
- rescue Java::OrgRacobCom::ComFailException
1109
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
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
- super
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