robust_excel_ole 1.27 → 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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +36 -2
  3. data/README.rdoc +121 -19
  4. data/___dummy_workbook.xls +0 -0
  5. data/benchmarking/creek_example.rb +1 -1
  6. data/benchmarking/reo_example.rb +1 -1
  7. data/benchmarking/reo_example1.rb +1 -1
  8. data/benchmarking/reo_example2.rb +1 -1
  9. data/benchmarking/roo_example.rb +1 -1
  10. data/benchmarking/simple_xlsx_reader_example.rb +1 -1
  11. data/benchmarking/spreadsheet_example.rb +1 -1
  12. data/bin/jreo +19 -0
  13. data/bin/reo +19 -0
  14. data/docs/README_excel.rdoc +16 -24
  15. data/docs/README_listobjects.rdoc +176 -0
  16. data/docs/README_open.rdoc +20 -16
  17. data/docs/README_ranges.rdoc +72 -55
  18. data/docs/README_save_close.rdoc +3 -3
  19. data/docs/README_sheet.rdoc +19 -20
  20. data/examples/example_ruby_library.rb +2 -2
  21. data/examples/introductory_examples/example_open.rb +11 -0
  22. data/examples/introductory_examples/example_range.rb +2 -2
  23. data/examples/modifying_sheets/example_access_sheets_and_cells.rb +6 -6
  24. data/examples/modifying_sheets/example_add_names.rb +1 -1
  25. data/examples/modifying_sheets/example_concating.rb +1 -1
  26. data/examples/modifying_sheets/example_copying.rb +2 -2
  27. data/examples/modifying_sheets/example_listobjects.rb +86 -0
  28. data/examples/modifying_sheets/example_naming.rb +1 -1
  29. data/examples/modifying_sheets/example_ranges.rb +1 -1
  30. data/examples/open_save_close/example_control_to_excel.rb +1 -1
  31. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
  32. data/examples/open_save_close/example_if_obstructed_save.rb +3 -3
  33. data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
  34. data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
  35. data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -4
  36. data/examples/open_save_close/example_read_only.rb +1 -1
  37. data/examples/open_save_close/example_simple.rb +1 -1
  38. data/examples/open_save_close/example_unobtrusively.rb +3 -3
  39. data/lib/robust_excel_ole.rb +19 -16
  40. data/lib/robust_excel_ole/address_tool.rb +54 -44
  41. data/lib/robust_excel_ole/base.rb +9 -6
  42. data/lib/robust_excel_ole/bookstore.rb +3 -17
  43. data/lib/robust_excel_ole/cell.rb +17 -22
  44. data/lib/robust_excel_ole/cygwin.rb +2 -0
  45. data/lib/robust_excel_ole/excel.rb +136 -201
  46. data/lib/robust_excel_ole/general.rb +249 -238
  47. data/lib/robust_excel_ole/list_object.rb +186 -210
  48. data/lib/robust_excel_ole/list_row.rb +155 -0
  49. data/lib/robust_excel_ole/range.rb +130 -94
  50. data/lib/robust_excel_ole/range_owners.rb +54 -135
  51. data/lib/robust_excel_ole/version.rb +1 -1
  52. data/lib/robust_excel_ole/workbook.rb +230 -196
  53. data/lib/robust_excel_ole/worksheet.rb +254 -133
  54. data/lib/spec_helper.rb +1 -1
  55. data/robust_excel_ole.gemspec +4 -3
  56. data/spec/address_tool_spec.rb +2 -2
  57. data/spec/base_spec.rb +19 -17
  58. data/spec/bookstore_spec.rb +3 -4
  59. data/spec/cell_spec.rb +10 -10
  60. data/spec/cygwin_spec.rb +1 -1
  61. data/spec/data/more_data/workbook.xls +0 -0
  62. data/spec/excel_spec.rb +133 -86
  63. data/spec/general_spec.rb +79 -18
  64. data/spec/list_object_spec.rb +259 -81
  65. data/spec/list_row_spec.rb +218 -0
  66. data/spec/range_spec.rb +75 -41
  67. data/spec/spec_helper.rb +16 -2
  68. data/spec/workbook_spec.rb +87 -46
  69. data/spec/workbook_specs/workbook_all_spec.rb +9 -28
  70. data/spec/workbook_specs/workbook_close_spec.rb +1 -1
  71. data/spec/workbook_specs/workbook_misc_spec.rb +52 -45
  72. data/spec/workbook_specs/workbook_open_spec.rb +103 -50
  73. data/spec/workbook_specs/workbook_save_spec.rb +22 -23
  74. data/spec/workbook_specs/workbook_sheet_spec.rb +4 -4
  75. data/spec/workbook_specs/workbook_subclass_spec.rb +1 -1
  76. data/spec/workbook_specs/workbook_unobtr_spec.rb +553 -395
  77. data/spec/worksheet_spec.rb +544 -308
  78. metadata +38 -3
  79. data/lib/reo_console.rb +0 -42
@@ -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_glob(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
@@ -63,111 +59,42 @@ module RobustExcelOle
63
59
  # @param [String] name the name of a range
64
60
  # @param [Variant] value the contents of the range
65
61
  # @option opts [Symbol] :color the color of the range when set
66
- def set_namevalue_glob(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
62
+ def set_namevalue_global(name, value, opts = { })
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
- end
90
- end
91
-
92
- # returns the contents of a range with a locally defined name
93
- # evaluates the formula if the contents is a formula
94
- # if the name could not be found or the range or value could not be determined,
95
- # then return default value, if provided, raise error otherwise
96
- # @param [String] name the name of a range
97
- # @param [Hash] opts the options
98
- # @option opts [Symbol] :default the default value that is provided if no contents could be returned
99
- # @return [Variant] the contents of a range with given name
100
- def namevalue(name, opts = { :default => :__not_provided })
101
- return namevalue_glob(name, opts) if self.is_a?(Workbook)
102
- begin
103
- ole_range = self.Range(name)
104
- rescue # WIN32OLERuntimeError, VBAMethodMissingError, Java::OrgRacobCom::ComFailException
105
- return opts[:default] unless opts[:default] == :__not_provided
106
- raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
107
- end
108
- begin
109
- worksheet = self if self.is_a?(Worksheet)
110
- #value = ole_range.Value
111
- value = if !::RANGES_JRUBY_BUG
112
- ole_range.Value
113
- else
114
- values = RobustExcelOle::Range.new(ole_range, worksheet).v
115
- (values.size==1 && values.first.size==1) ? values.first.first : values
116
- end
117
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
118
- return opts[:default] unless opts[:default] == :__not_provided
119
- raise RangeNotEvaluatable, "cannot determine value of range named #{name.inspect} in #{self.inspect}"
120
- end
121
- if value == -2146828288 + RobustExcelOle::XlErrName
122
- return opts[:default] unless opts[:default] == __not_provided
123
- raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
124
80
  end
125
- return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
126
81
  value
127
- end
128
-
129
- # assigns a value to a range given a locally defined name
130
- # @param [String] name the name of a range
131
- # @param [Variant] value the assigned value
132
- # @option opts [Symbol] :color the color of the cell when set
133
- def set_namevalue(name, value, opts = { })
134
- begin
135
- return set_namevalue_glob(name, value, opts) if self.is_a?(Workbook)
136
- ole_range = self.Range(name)
137
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException, VBAMethodMissingError
138
- raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
139
- end
140
- begin
141
- ole_range.Interior.ColorIndex = opts[:color] unless opts[:color].nil?
142
- if !::RANGES_JRUBY_BUG
143
- ole_range.Value = value
144
- else
145
- address_r1c1 = ole_range.AddressLocal(true,true,XlR1C1)
146
- row, col = address_tool.as_integer_ranges(address_r1c1)
147
- row.each_with_index do |r,i|
148
- col.each_with_index do |c,j|
149
- ole_range.Cells(i+1,j+1).Value = (value.respond_to?(:first) ? value[i][j] : value)
150
- end
151
- end
152
- end
153
- value
154
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
155
- raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
156
- end
82
+ rescue #WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
83
+ raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}\n#{$!.message}"
157
84
  end
158
85
 
159
86
  # @private
160
- def nameval(name, opts = { :default => :__not_provided }) # :deprecated: #
161
- namevalue_glob(name, opts)
87
+ def nameval(name, opts = { default: :__not_provided }) # :deprecated: #
88
+ namevalue_global(name, opts)
162
89
  end
163
90
 
164
91
  # @private
165
92
  def set_nameval(name, value) # :deprecated: #
166
- set_namevalue_glob(name, value)
93
+ set_namevalue_global(name, value)
167
94
  end
168
95
 
169
96
  # @private
170
- def rangeval(name, opts = { :default => :__not_provided }) # :deprecated: #
97
+ def rangeval(name, opts = { default: :__not_provided }) # :deprecated: #
171
98
  namevalue(name, opts)
172
99
  end
173
100
 
@@ -192,53 +119,45 @@ module RobustExcelOle
192
119
  # @params [Address] address of the range
193
120
  def add_name(name, addr, addr_deprecated = :__not_provided)
194
121
  addr = [addr,addr_deprecated] unless addr_deprecated == :__not_provided
195
- begin
196
- self.Names.Add(name, nil, true, nil, nil, nil, nil, nil, nil, '=' + address_tool.as_r1c1(addr))
197
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
198
- raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}"
199
- end
122
+ self.Names.Add(name, nil, true, nil, nil, nil, nil, nil, nil, '=' + address_tool.as_r1c1(addr))
200
123
  name
124
+ rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
125
+ raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}\n#{$!.message}"
201
126
  end
202
127
 
203
- def set_name(name,row,column) # :deprecated :#
204
- 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}"
205
140
  end
206
141
 
207
- # renames a range
208
- # @param [String] name the previous range name
209
- # @param [String] new_name the new range name
210
- def rename_range(name, new_name)
211
- begin
212
- item = self.Names.Item(name)
213
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
214
- raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
215
- end
216
- begin
217
- item.Name = new_name
218
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
219
- raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
220
- end
221
- end
142
+ alias rename_range rename_name # :deprecated :#
222
143
 
223
- # deletes a name of a range
224
- # @param [String] name the previous range name
225
- # @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
226
146
  def delete_name(name)
227
- begin
228
- item = self.Names.Item(name)
229
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
230
- raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
231
- end
232
- begin
233
- item.Delete
234
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException
235
- raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
236
- end
237
- 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
238
157
 
239
158
  private
240
159
 
241
- def name_object(name)
160
+ def get_name_object(name)
242
161
  self.Names.Item(name)
243
162
  rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException, VBAMethodMissingError
244
163
  begin
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "1.27"
2
+ VERSION = "1.32"
3
3
  end
@@ -11,25 +11,27 @@ module RobustExcelOle
11
11
 
12
12
  class Workbook < RangeOwners
13
13
 
14
- #include General
15
-
14
+ include Enumerable
15
+
16
16
  attr_reader :ole_workbook
17
17
  attr_reader :excel
18
18
  attr_reader :stored_filename
19
19
 
20
20
  alias ole_object ole_workbook
21
21
 
22
+ using ToReoRefinement
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 = [
@@ -42,7 +44,7 @@ module RobustExcelOle
42
44
 
43
45
 
44
46
  # opens a workbook.
45
- # @param [String] file_or_workbook a file name or WIN32OLE workbook
47
+ # @param [String,Pathname] file_or_workbook a file name (string or pathname) or WIN32OLE workbook
46
48
  # @param [Hash] opts the options
47
49
  # @option opts [Hash] :default or :d
48
50
  # @option opts [Hash] :force or :f
@@ -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,12 +155,12 @@ 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('\\','/')
160
162
  else
161
- filename = file_or_workbook
163
+ filename = file_or_workbook
162
164
  ensure_workbook(filename, opts)
163
165
  end
164
166
  apply_options(filename, opts)
@@ -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
@@ -235,13 +237,12 @@ module RobustExcelOle
235
237
  raise ExcelREOError, "Excel is not alive" unless @excel && @excel.alive?
236
238
  end
237
239
 
238
-
239
240
  # @private
240
- def ensure_workbook(filename, options)
241
+ def ensure_workbook(filename, options)
241
242
  set_was_open options, true
242
243
  return if (@ole_workbook && alive? && (options[:read_only].nil? || @ole_workbook.ReadOnly == options[:read_only]))
243
244
  set_was_open options, false
244
- if options[:if_unsaved]==:accept &&
245
+ if options[:if_unsaved]==:accept && alive? &&
245
246
  ((options[:read_only]==true && self.ReadOnly==false) || (options[:read_only]==false && self.ReadOnly==true))
246
247
  raise OptionInvalid, ":if_unsaved:accept and change of read-only mode is not possible"
247
248
  end
@@ -254,7 +255,7 @@ module RobustExcelOle
254
255
  if @ole_workbook && alive?
255
256
  set_was_open options, true
256
257
  manage_blocking_or_unsaved_workbook(filename,options)
257
- open_or_create_workbook(filename,options) if @ole_workbook.ReadOnly != options[:read_only]
258
+ open_or_create_workbook(filename,options) if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
258
259
  else
259
260
  if (excel_option.nil? || excel_option == :current) &&
260
261
  !(::CONNECT_JRUBY_BUG && filename[0] == '/')
@@ -270,7 +271,6 @@ module RobustExcelOle
270
271
  # applies options to workbook named with filename
271
272
  def apply_options(filename, options)
272
273
  # changing read-only mode
273
- #ensure_workbook(filename, options) if options[:read_only] && options[:read_only] != @ole_workbook.ReadOnly
274
274
  if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
275
275
  ensure_workbook(filename, options)
276
276
  end
@@ -283,14 +283,14 @@ module RobustExcelOle
283
283
 
284
284
  # connects to an unknown workbook
285
285
  def connect(filename, options)
286
- 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
287
287
  @ole_workbook = begin
288
288
  WIN32OLE.connect(General.absolute_path(filename))
289
289
  rescue
290
290
  if $!.message =~ /moniker/
291
291
  raise WorkbookConnectingBlockingError, "some workbook is blocking when connecting"
292
292
  else
293
- raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook"
293
+ raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook\n#{$!.message}"
294
294
  end
295
295
  end
296
296
  ole_excel = begin
@@ -299,7 +299,7 @@ module RobustExcelOle
299
299
  if $!.message =~ /dispid/
300
300
  raise WorkbookConnectingUnsavedError, "workbook is unsaved when connecting"
301
301
  else
302
- raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook"
302
+ raise WorkbookConnectingUnknownError, "unknown error when connecting to a workbook\n#{$!.message}"
303
303
  end
304
304
  end
305
305
  set_was_open options, (ole_excel.Workbooks.Count == workbooks_number)
@@ -327,7 +327,7 @@ module RobustExcelOle
327
327
  def manage_blocking_or_unsaved_workbook(filename, options)
328
328
  filename = General.absolute_path(filename)
329
329
  filename = General.canonize(filename)
330
- previous_file = General.canonize(@ole_workbook.Fullname)
330
+ previous_file = General.canonize(@ole_workbook.Fullname.gsub('\\','/'))
331
331
  obstructed_by_other_book = (File.basename(filename) == File.basename(previous_file)) &&
332
332
  (File.dirname(filename) != File.dirname(previous_file))
333
333
  if obstructed_by_other_book
@@ -341,11 +341,12 @@ module RobustExcelOle
341
341
  end
342
342
  end
343
343
 
344
- def manage_blocking_workbook(filename, options)
344
+ def manage_blocking_workbook(filename, options)
345
+ blocked_filename = -> { General.canonize(@ole_workbook.Fullname.tr('\\','/')) }
345
346
  case options[:if_obstructed]
346
347
  when :raise
347
348
  raise WorkbookBlocked, "can't open workbook #{filename},
348
- because it is being blocked by #{@ole_workbook.Fullname.tr('\\','/')} with the same name in a different path." +
349
+ because it is being blocked by #{blocked_filename.call} with the same name in a different path." +
349
350
  "\nHint: Use the option :if_blocked with values :forget or :save,
350
351
  to allow automatic closing of the old workbook (without or with saving before, respectively),
351
352
  before the new workbook is being opened."
@@ -355,8 +356,8 @@ module RobustExcelOle
355
356
  manage_saving_workbook(filename, options)
356
357
  when :close_if_saved
357
358
  if !@ole_workbook.Saved
358
- raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{@ole_workbook.Fullname.tr('\\','/')}" +
359
- "\nHint: Use the option :if_blocked => :save to save the workbook"
359
+ raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{blocked_filename.call}" +
360
+ "\nHint: Use the option if_blocked: :save to save the workbook"
360
361
  else
361
362
  manage_forgetting_workbook(filename, options)
362
363
  end
@@ -371,9 +372,14 @@ module RobustExcelOle
371
372
  def manage_unsaved_workbook(filename, options)
372
373
  case options[:if_unsaved]
373
374
  when :raise
374
- raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(filename).inspect}" +
375
- "\nHint: Save the workbook or open the workbook using option :if_unsaved with values :forget and :accept to
376
- close the unsaved workbook and reopen it, or to let the unsaved workbook open, respectively"
375
+ msg = if !options[:read_only].nil? && @ole_workbook.ReadOnly != options[:read_only]
376
+ "cannot change read-only mode of the workbook #{File.basename(filename).inspect}, because it contains unsaved changes"
377
+ else
378
+ "workbook is already open but not saved: #{File.basename(filename).inspect}"
379
+ end
380
+ raise WorkbookNotSaved, msg +
381
+ "\nHint: Use the option :if_unsaved with values :forget to close the unsaved workbook,
382
+ :accept to let it open, or :save to save it, respectivly"
377
383
  when :forget
378
384
  manage_forgetting_workbook(filename, options)
379
385
  when :accept
@@ -402,46 +408,53 @@ module RobustExcelOle
402
408
  end
403
409
 
404
410
  def manage_new_excel(filename, options)
405
- @excel = excel_class.new(:reuse => false)
411
+ @excel = excel_class.new(reuse: false)
406
412
  @ole_workbook = nil
407
413
  open_or_create_workbook(filename, options)
408
414
  end
409
-
415
+
416
+ def explore_workbook_error(msg, want_change_readonly = nil)
417
+ if msg.message =~ /800A03EC/ && msg.message =~ /0x80020009/
418
+ # error message:
419
+ # 'This workbook is currently referenced by another workbook and cannot be closed'
420
+ # 'Diese Arbeitsmappe wird momentan von einer anderen Arbeitsmappe verwendet und kann nicht geschlossen werden.'
421
+ if want_change_readonly==true
422
+ raise WorkbookLinked, "read-only mode of this workbook cannot be changed, because it is being used by another workbook"
423
+ elsif want_change_readonly.nil?
424
+ raise WorkbookLinked, "workbook is being used by another workbook"
425
+ end
426
+ end
427
+ if msg.message !~ /800A03EC/ || msg.message !~ /0x80020009/ || want_change_readonly==false
428
+ raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
429
+ end
430
+ end
431
+
410
432
  def open_or_create_workbook(filename, options)
411
433
  return if @ole_workbook && options[:if_unsaved] != :alert && options[:if_unsaved] != :excel &&
412
- (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
413
441
  begin
414
- abs_filename = General.absolute_path(filename)
415
- begin
416
- workbooks = @excel.Workbooks
417
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
418
- raise UnexpectedREOError, "cannot access workbooks: #{msg.message} #{msg.backtrace}"
419
- end
420
- begin
421
- with_workaround_linked_workbooks_excel2007(options) do
422
- # temporary workaround until jruby-win32ole implements named parameters (Java::JavaLang::RuntimeException (createVariant() not implemented for class org.jruby.RubyHash)
423
- workbooks.Open(abs_filename,
424
- updatelinks_vba(options[:update_links]),
425
- options[:read_only] )
426
- end
427
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
428
- # for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
429
- # if yes: distinguish these events
430
- raise UnexpectedREOError, "cannot open workbook: #{msg.message} #{msg.backtrace}"
431
- end
432
- begin
433
- # workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
434
- begin
435
- @ole_workbook = workbooks.Item(File.basename(filename))
436
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
437
- raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
438
- end
439
- rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
440
- 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] )
441
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)
449
+ end
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}"
442
455
  end
443
- end
444
-
456
+ end
457
+
445
458
  # translating the option UpdateLinks from REO to VBA
446
459
  # setting UpdateLinks works only if calculation mode is automatic,
447
460
  # parameter 'UpdateLinks' has no effect
@@ -480,7 +493,7 @@ module RobustExcelOle
480
493
  # @param [String] filename the filename under which the new workbook should be saved
481
494
  # @param [Hash] opts the options as in Workbook::open
482
495
  def self.create(filename, opts = { })
483
- open(filename, :if_absent => :create)
496
+ open(filename, if_absent: :create)
484
497
  end
485
498
 
486
499
  # closes the workbook, if it is alive
@@ -495,34 +508,37 @@ module RobustExcelOle
495
508
  # :alert or :excel -> gives control to excel
496
509
  # @raise WorkbookNotSaved if the option :if_unsaved is :raise and the workbook is unsaved
497
510
  # @raise OptionInvalid if the options is invalid
498
- def close(opts = {:if_unsaved => :raise})
499
- if alive? && !@ole_workbook.Saved && writable
500
- case opts[:if_unsaved]
501
- when :raise
502
- raise WorkbookNotSaved, "workbook is unsaved: #{File.basename(self.stored_filename).inspect}" +
503
- "\nHint: Use option :save or :forget to close the workbook with or without saving"
504
- when :save
505
- save
506
- close_workbook
507
- when :forget
508
- @excel.with_displayalerts(false) { close_workbook }
509
- when :keep_open
510
- # nothing
511
- when :alert, :excel
512
- @excel.with_displayalerts(true) { close_workbook }
513
- else
514
- raise OptionInvalid, ":if_unsaved: invalid option: #{opts[:if_unsaved].inspect}" +
515
- "\nHint: Valid values are :raise, :save, :keep_open, :alert, :excel"
516
- end
517
- 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
518
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"
519
529
  end
520
530
  end
521
531
 
522
532
  private
523
533
 
524
534
  def close_workbook
525
- @ole_workbook.Close if alive?
535
+ if alive?
536
+ begin
537
+ @ole_workbook.Close
538
+ rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
539
+ explore_workbook_error(msg)
540
+ end
541
+ end
526
542
  @ole_workbook = nil unless alive?
527
543
  end
528
544
 
@@ -539,29 +555,31 @@ module RobustExcelOle
539
555
  end
540
556
 
541
557
  def for_reading(opts = { }, &block)
542
- unobtrusively({:writable => false}.merge(opts), &block)
558
+ unobtrusively({writable: false}.merge(opts), &block)
543
559
  end
544
560
 
545
561
  def for_modifying(opts = { }, &block)
546
- unobtrusively({:writable => true}.merge(opts), &block)
562
+ unobtrusively({writable: true}.merge(opts), &block)
547
563
  end
548
564
 
549
565
  def self.for_reading(arg, opts = { }, &block)
550
- unobtrusively(arg, {:writable => false}.merge(opts), &block)
566
+ unobtrusively(arg, {writable: false}.merge(opts), &block)
551
567
  end
552
568
 
553
569
  def self.for_modifying(arg, opts = { }, &block)
554
- unobtrusively(arg, {:writable => true}.merge(opts), &block)
570
+ unobtrusively(arg, {writable: true}.merge(opts), &block)
555
571
  end
556
572
 
557
573
  # allows to read or modify a workbook such that its state remains unchanged
558
574
  # state comprises: open, saved, writable, visible, calculation mode, check compatibility
559
575
  # @param [String] file_or_workbook a file name or WIN32OLE workbook
560
- # @param [Hash] opts the options
561
- # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
562
- # @option opts [Boolean] :read_only true/false (default), open the workbook in read-only/read-write modus (save changes)
563
- # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved
576
+ # @param [Hash] opts the options
577
+ # @option opts [Boolean] :read_only true/false (default), force to open the workbook in read-only/read-write mode
578
+ # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved,
579
+ # and the workbook is being opened in read-only/read-write mode by default
580
+ # (when the workbook was not open before)
564
581
  # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening (default: false)
582
+ # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
565
583
  # @return [Workbook] a workbook
566
584
  def self.unobtrusively(file_or_workbook, opts = { }, &block)
567
585
  file = (file_or_workbook.is_a? WIN32OLE) ? file_or_workbook.Fullname.tr('\\','/') : file_or_workbook
@@ -577,36 +595,37 @@ module RobustExcelOle
577
595
 
578
596
  def self.unobtrusively_opening(file, opts, book_is_alive, &block)
579
597
  process_options(opts)
580
- opts = {:if_closed => :current, :keep_open => false}.merge(opts)
581
- 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]
582
600
  if book_is_alive.nil?
583
601
  prefer_writable = ((!(opts[:read_only]) || opts[:writable] == true) &&
584
602
  !(opts[:read_only].nil? && opts[:writable] == false))
585
- known_book = bookstore.fetch(file, :prefer_writable => prefer_writable)
603
+ known_book = bookstore.fetch(file, prefer_writable: prefer_writable)
586
604
  end
587
605
  excel_opts = if (book_is_alive==false || (book_is_alive.nil? && (known_book.nil? || !known_book.alive?)))
588
- {:force => {:excel => opts[:if_closed]}}
606
+ {force: {excel: opts[:if_closed]}}
589
607
  else
590
- {:force => {:excel => opts[:force][:excel]}, :default => {:excel => opts[:default][:excel]}}
608
+ {force: {excel: opts[:force][:excel]}, default: {excel: opts[:default][:excel]}}
591
609
  end
592
- open_opts = excel_opts.merge({:if_unsaved => :accept})
610
+ open_opts = excel_opts.merge({if_unsaved: :accept})
593
611
  begin
594
- open_opts[:was_open] = nil
612
+ open_opts[:was_open] = nil
595
613
  book = open(file, open_opts)
596
614
  was_visible = book.visible
597
615
  was_writable = book.writable
598
616
  was_saved = book.saved
599
617
  was_check_compatibility = book.check_compatibility
600
618
  was_calculation = book.excel.properties[:calculation]
619
+ opts[:read_only] = !opts[:writable] unless (!opts[:read_only].nil? || opts[:writable].nil? || open_opts[:was_open])
601
620
  book.send :apply_options, file, opts
602
621
  yield book
603
622
  ensure
604
623
  if book && book.alive?
605
624
  do_not_write = opts[:read_only] || opts[:writable]==false
606
625
  book.save unless book.saved || do_not_write || !book.writable
607
- if (opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable)
608
- book.send :apply_options, file, opts.merge({:read_only => !was_writable,
609
- :if_unsaved => (opts[:writable]==false ? :forget : :save)})
626
+ if ((opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable))
627
+ book.send :apply_options, file, opts.merge({read_only: !was_writable,
628
+ if_unsaved: (opts[:writable]==false ? :forget : :save)})
610
629
  end
611
630
  was_open = open_opts[:was_open]
612
631
  if was_open
@@ -626,20 +645,20 @@ module RobustExcelOle
626
645
  # @options options
627
646
  def reopen(options = { })
628
647
  book = self.class.open(@stored_filename, options)
629
- raise WorkbookREOError('cannot reopen book') unless book && book.alive?
648
+ raise WorkbookREOError("cannot reopen workbook\n#{$!.message}") unless book && book.alive?
630
649
  book
631
650
  end
632
651
 
633
652
  # simple save of a workbook.
634
653
  # @return [Boolean] true, if successfully saved, nil otherwise
635
654
  def save(opts = { }) # option opts is deprecated #
636
- raise ObjectNotAlive, 'workbook is not alive' unless alive?
637
- 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
638
657
  begin
639
658
  @ole_workbook.Save
640
659
  rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
641
660
  if msg.message =~ /SaveAs/ && msg.message =~ /Workbook/
642
- raise WorkbookNotSaved, 'workbook not saved'
661
+ raise WorkbookNotSaved, "workbook not saved"
643
662
  else
644
663
  raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
645
664
  end
@@ -665,64 +684,72 @@ module RobustExcelOle
665
684
  # otherwise raises an exception
666
685
  # @return [Workbook], the book itself, if successfully saved, raises an exception otherwise
667
686
  def save_as(file, options = { })
668
- raise FileNameNotGiven, 'filename is nil' if file.nil?
669
- raise ObjectNotAlive, 'workbook is not alive' unless alive?
670
- 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
671
690
  raise(FileNotFound, "file #{General.absolute_path(file).inspect} is a directory") if File.directory?(file)
672
- self.class.process_options(options)
673
- if File.exist?(file)
674
- case options[:if_exists]
675
- when :overwrite
676
- if file == self.filename
677
- save
678
- return self
679
- else
680
- begin
681
- File.delete(file)
682
- rescue Errno::EACCES
683
- raise WorkbookBeingUsed, 'workbook is open and being used in an Excel instance'
684
- end
685
- end
686
- when :alert, :excel
687
- @excel.with_displayalerts true do
688
- save_as_workbook(file, options)
689
- end
690
- return self
691
- when :raise
692
- raise FileAlreadyExists, "file already exists: #{File.basename(file).inspect}" +
693
- "\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
694
711
  else
695
- raise OptionInvalid, ":if_exists: invalid option: #{options[:if_exists].inspect}" +
696
- "\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
697
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"
698
727
  end
728
+ end
729
+
730
+ def saveas_manage_if_blocked(file, options)
699
731
  other_workbook = @excel.Workbooks.Item(File.basename(file)) rescue nil
700
- if other_workbook && self.filename != other_workbook.Fullname.tr('\\','/')
701
- case options[:if_obstructed]
702
- when :raise
703
- raise WorkbookBlocked, "blocked by another workbook: #{other_workbook.Fullname.tr('\\','/')}" +
704
- "\nHint: Use the option :if_blocked with values :forget or :save to
705
- close or save and close the blocking workbook"
706
- when :forget
707
- # nothing
708
- when :save
709
- other_workbook.Save
710
- when :close_if_saved
711
- unless other_workbook.Saved
712
- raise WorkbookBlocked, "blocking workbook is unsaved: #{File.basename(file).inspect}" +
713
- "\nHint: Use option :if_blocked => :save to save the blocking workbooks"
714
- end
715
- else
716
- raise OptionInvalid, ":if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
717
- "\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"
718
746
  end
719
- 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"
720
750
  end
721
- save_as_workbook(file, options)
722
- self
723
- end
724
-
725
- private
751
+ other_workbook.Close
752
+ end
726
753
 
727
754
  def save_as_workbook(file, options)
728
755
  dirname, basename = File.split(file)
@@ -743,6 +770,9 @@ module RobustExcelOle
743
770
  end
744
771
  end
745
772
 
773
+ class AlreadyManaged < Exception
774
+ end
775
+
746
776
  def store_myself
747
777
  bookstore.store(self)
748
778
  @stored_filename = filename
@@ -752,7 +782,7 @@ module RobustExcelOle
752
782
 
753
783
  # closes a given file if it is open
754
784
  # @options opts [Symbol] :if_unsaved
755
- def self.close(file, opts = {:if_unsaved => :raise})
785
+ def self.close(file, opts = {if_unsaved: :raise})
756
786
  book = begin
757
787
  bookstore.fetch(file)
758
788
  rescue
@@ -790,21 +820,20 @@ module RobustExcelOle
790
820
  @ole_workbook.Worksheets.Count
791
821
  end
792
822
 
823
+ # @return [Enumerator] traversing all worksheet objects
793
824
  def each
794
- @ole_workbook.Worksheets.each do |sheet|
795
- 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
796
831
  end
797
832
  end
798
833
 
799
- def worksheets
800
- result = []
801
- each { |worksheet| result << worksheet }
802
- result
803
- end
804
-
805
834
  def each_with_index(offset = 0)
806
835
  i = offset
807
- @ole_workbook.Worksheets.each do |sheet|
836
+ @ole_workbook.Worksheets.lazy.each do |sheet|
808
837
  yield worksheet_class.new(sheet), i
809
838
  i += 1
810
839
  end
@@ -845,7 +874,7 @@ module RobustExcelOle
845
874
  end
846
875
  end
847
876
  rescue # WIN32OLERuntimeError, NameNotFound, Java::OrgRacobCom::ComFailException
848
- raise WorksheetREOError, "could not add given worksheet #{sheet.inspect}"
877
+ raise WorksheetREOError, "could not add given worksheet #{sheet.inspect}\n#{$!.message}"
849
878
  end
850
879
  new_sheet = worksheet_class.new(ole_workbook.Activesheet)
851
880
  new_sheet.name = new_sheet_name if new_sheet_name
@@ -889,7 +918,7 @@ module RobustExcelOle
889
918
  def range(name_or_worksheet, name_or_address = :__not_provided, address2 = :__not_provided)
890
919
  if name_or_worksheet.respond_to?(:gsub)
891
920
  name = name_or_worksheet
892
- RobustExcelOle::Range.new(name_object(name).RefersToRange)
921
+ RobustExcelOle::Range.new(get_name_object(name).RefersToRange)
893
922
  else
894
923
  begin
895
924
  worksheet = name_or_worksheet.to_reo
@@ -904,21 +933,21 @@ module RobustExcelOle
904
933
  # @param [String] name the name of a range
905
934
  # @returns [Variant] the value of the range
906
935
  def [] name
907
- namevalue_glob(name)
936
+ namevalue_global(name)
908
937
  end
909
938
 
910
939
  # sets the value of a range
911
940
  # @param [String] name the name of the range
912
941
  # @param [Variant] value the contents of the range
913
942
  def []= (name, value)
914
- set_namevalue_glob(name, value)
943
+ set_namevalue_global(name, value)
915
944
  end
916
945
 
917
946
  # sets options
918
947
  # @param [Hash] opts
919
948
  def for_this_workbook(opts)
920
949
  return unless alive?
921
- self.class.process_options(opts, :use_defaults => false)
950
+ self.class.process_options(opts, use_defaults: false)
922
951
  self.send :apply_options, @stored_filename, opts
923
952
  end
924
953
 
@@ -1023,14 +1052,18 @@ module RobustExcelOle
1023
1052
 
1024
1053
  # @private
1025
1054
  def inspect
1026
- '#<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} >"
1027
1057
  end
1028
1058
 
1059
+ using ParentRefinement
1060
+ using StringRefinement
1061
+
1029
1062
  # @private
1030
1063
  def self.excel_class
1031
1064
  @excel_class ||= begin
1032
1065
  module_name = self.parent_name
1033
- "#{module_name}::Excel".constantize
1066
+ "#{module_name}::Excel".constantize
1034
1067
  rescue NameError => e
1035
1068
  # trace "excel_class: NameError: #{e}"
1036
1069
  Excel
@@ -1041,7 +1074,7 @@ module RobustExcelOle
1041
1074
  def self.worksheet_class
1042
1075
  @worksheet_class ||= begin
1043
1076
  module_name = self.parent_name
1044
- "#{module_name}::Worksheet".constantize
1077
+ "#{module_name}::Worksheet".constantize
1045
1078
  rescue NameError => e
1046
1079
  Worksheet
1047
1080
  end
@@ -1062,23 +1095,20 @@ module RobustExcelOle
1062
1095
  private
1063
1096
 
1064
1097
  def method_missing(name, *args)
1065
- if name.to_s[0,1] =~ /[A-Z]/
1066
- raise ObjectNotAlive, 'method missing: workbook not alive' unless alive?
1067
- if ::ERRORMESSAGE_JRUBY_BUG
1068
- begin
1069
- @ole_workbook.send(name, *args)
1070
- rescue Java::OrgRacobCom::ComFailException
1071
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
1072
- end
1073
- else
1074
- begin
1075
- @ole_workbook.send(name, *args)
1076
- rescue NoMethodError
1077
- raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
1078
- 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}"
1079
1105
  end
1080
1106
  else
1081
- super
1107
+ begin
1108
+ @ole_workbook.send(name, *args)
1109
+ rescue NoMethodError
1110
+ raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
1111
+ end
1082
1112
  end
1083
1113
  end
1084
1114
 
@@ -1094,6 +1124,10 @@ public
1094
1124
  class WorkbookNotSaved < WorkbookREOError
1095
1125
  end
1096
1126
 
1127
+ # @private
1128
+ class WorkbookLinked < WorkbookREOError
1129
+ end
1130
+
1097
1131
  # @private
1098
1132
  class WorkbookReadOnly < WorkbookREOError
1099
1133
  end