robust_excel_ole 1.31 → 1.32

Sign up to get free protection for your applications and to get access to all the features.
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