robust_excel_ole 1.11 → 1.12

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog +11 -0
  3. data/README.rdoc +20 -8
  4. data/docs/README_open.rdoc +1 -0
  5. data/docs/README_ranges.rdoc +5 -11
  6. data/examples/{introducing_examples/example_introducing.rb → introductory_examples/example_introductory.rb} +10 -2
  7. data/examples/{introducing_examples → introductory_examples}/example_open.rb +18 -17
  8. data/examples/{introducing_examples → introductory_examples}/example_range.rb +29 -16
  9. data/examples/modifying_sheets/example_access_sheets_and_cells.rb +8 -7
  10. data/examples/modifying_sheets/example_add_names.rb +4 -8
  11. data/examples/modifying_sheets/example_adding_sheets.rb +7 -6
  12. data/examples/modifying_sheets/example_concating.rb +2 -2
  13. data/examples/modifying_sheets/example_copying.rb +3 -3
  14. data/examples/modifying_sheets/example_expanding.rb +2 -2
  15. data/examples/modifying_sheets/example_naming.rb +2 -2
  16. data/examples/modifying_sheets/example_ranges.rb +4 -4
  17. data/examples/modifying_sheets/example_saving.rb +3 -3
  18. data/examples/open_save_close/example_control_to_excel.rb +10 -11
  19. data/examples/open_save_close/example_default_excel.rb +13 -14
  20. data/examples/open_save_close/example_force_excel.rb +9 -10
  21. data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +7 -7
  22. data/examples/open_save_close/example_if_obstructed_forget.rb +5 -5
  23. data/examples/open_save_close/example_if_obstructed_save.rb +7 -7
  24. data/examples/open_save_close/example_if_unsaved_accept.rb +13 -13
  25. data/examples/open_save_close/example_if_unsaved_forget.rb +9 -10
  26. data/examples/open_save_close/example_if_unsaved_forget_more.rb +9 -10
  27. data/examples/open_save_close/example_read_only.rb +6 -6
  28. data/examples/open_save_close/example_rename_cells.rb +4 -5
  29. data/examples/open_save_close/example_reuse.rb +6 -6
  30. data/examples/open_save_close/example_simple.rb +5 -5
  31. data/examples/open_save_close/example_unobtrusively.rb +4 -4
  32. data/lib/robust_excel_ole/address.rb +0 -4
  33. data/lib/robust_excel_ole/bookstore.rb +4 -28
  34. data/lib/robust_excel_ole/excel.rb +17 -22
  35. data/lib/robust_excel_ole/general.rb +11 -18
  36. data/lib/robust_excel_ole/range_owners.rb +17 -27
  37. data/lib/robust_excel_ole/reo_common.rb +7 -3
  38. data/lib/robust_excel_ole/version.rb +1 -1
  39. data/lib/robust_excel_ole/workbook.rb +178 -180
  40. data/lib/robust_excel_ole/worksheet.rb +7 -4
  41. data/robust_excel_ole.gemspec +6 -4
  42. data/spec/bookstore_spec.rb +38 -34
  43. data/spec/data/more_data/workbook.xls +0 -0
  44. data/spec/excel_spec.rb +89 -44
  45. data/spec/general_spec.rb +1 -0
  46. data/spec/range_spec.rb +7 -4
  47. data/spec/workbook_specs/workbook_close_spec.rb +2 -1
  48. data/spec/workbook_specs/workbook_misc_spec.rb +34 -18
  49. data/spec/workbook_specs/workbook_open_spec.rb +112 -71
  50. data/spec/workbook_specs/workbook_save_spec.rb +173 -5
  51. data/spec/workbook_specs/workbook_sheet_spec.rb +6 -42
  52. data/spec/workbook_specs/workbook_unobtr_spec.rb +9 -246
  53. data/spec/worksheet_spec.rb +21 -5
  54. metadata +12 -11
@@ -75,13 +75,17 @@ module RobustExcelOle
75
75
  end
76
76
 
77
77
  # @private
78
- class WorkbookConnectingNotAliveError < WorkbookREOError
78
+ class WorkbookConnectingUnsavedError < WorkbookREOError
79
79
  end
80
80
 
81
81
  # @private
82
82
  class WorkbookConnectingBlockingError < WorkbookREOError
83
83
  end
84
84
 
85
+ # @private
86
+ class WorkbookConnectingUnknownError < WorkbookREOError
87
+ end
88
+
85
89
  # @private
86
90
  class FileNotFound < FileREOError
87
91
  end
@@ -169,8 +173,8 @@ module RobustExcelOle
169
173
  home = homes.find { |h| !ENV[h].nil? }
170
174
  reo_log_dir = ENV[home]
171
175
  else
172
- reo_log_dir = REO_LOG_DIR
173
- #reo_log_dir = "C:/Users/User"
176
+ #reo_log_dir = REO_LOG_DIR
177
+ reo_log_dir = "C:/Users/User"
174
178
  end
175
179
  File.open(reo_log_dir + '/' + REO_LOG_FILE,'a') do |file|
176
180
  file.puts text
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "1.11"
2
+ VERSION = "1.12"
3
3
  end
@@ -14,7 +14,7 @@ module RobustExcelOle
14
14
  attr_accessor :excel
15
15
  attr_accessor :ole_workbook
16
16
  attr_accessor :stored_filename
17
- attr_accessor :modified_cells
17
+ attr_accessor :color_if_modified
18
18
  attr_reader :workbook
19
19
 
20
20
  alias ole_object ole_workbook
@@ -25,9 +25,14 @@ module RobustExcelOle
25
25
  :if_unsaved => :raise,
26
26
  :if_obstructed => :raise,
27
27
  :if_absent => :raise,
28
- :read_only => false,
28
+ #:read_only => false,
29
29
  :check_compatibility => false,
30
- :update_links => :never
30
+ :update_links => :never,
31
+ :if_exists => :raise
32
+ }.freeze
33
+
34
+ CORE_DEFAULT_OPTEN_OPTS = {
35
+ :default => {:excel => :current}, :force => {}, :update_links => :never
31
36
  }.freeze
32
37
 
33
38
  ABBREVIATIONS = [[:default,:d], [:force, :f], [:excel, :e], [:visible, :v],
@@ -39,8 +44,8 @@ module RobustExcelOle
39
44
  # @param [Hash] opts the options
40
45
  # @option opts [Hash] :default or :d
41
46
  # @option opts [Hash] :force or :f
42
- # @option opts [Symbol] :if_unsaved :raise (default), :forget, :accept, :alert, :excel, or :new_excel
43
- # @option opts [Symbol] :if_obstructed :raise (default), :forget, :save, :close_if_saved, or _new_excel
47
+ # @option opts [Symbol] :if_unsaved :raise (default), :forget, :save, :accept, :alert, :excel, or :new_excel
48
+ # @option opts [Symbol] :if_blocked :raise (default), :forget, :save, :close_if_saved, or _new_excel
44
49
  # @option opts [Symbol] :if_absent :raise (default) or :create
45
50
  # @option opts [Boolean] :read_only true (default) or false
46
51
  # @option opts [Boolean] :update_links :never (default), :always, :alert
@@ -49,7 +54,7 @@ module RobustExcelOle
49
54
  # :default : if the workbook was already open before, then use (unchange) its properties,
50
55
  # otherwise, i.e. if the workbook cannot be reopened, use the properties stated in :default
51
56
  # :force : no matter whether the workbook was already open before, use the properties stated in :force
52
- # :default and :force contain: :excel, :visible
57
+ # :default and :force contain: :excel
53
58
  # :excel :current (or :active or :reuse)
54
59
  # -> connects to a running (the first opened) Excel instance,
55
60
  # excluding the hidden Excel instance, if it exists,
@@ -81,14 +86,13 @@ module RobustExcelOle
81
86
  def self.open(file, opts = { }, &block)
82
87
  raise(FileNameNotGiven, 'filename is nil') if file.nil?
83
88
  raise(FileNotFound, "file #{General.absolute_path(file).inspect} is a directory") if File.directory?(file)
84
- options = process_options(opts)
89
+ options = process_options(opts, :use_defaults => false)
85
90
  book = nil
86
91
  if options[:force][:excel] != :new
87
- # if readonly is true, then prefer a book that is given in force_excel if this option is set
88
- forced_excel = if RUBY_PLATFORM !~ /java/
92
+ # if readonly is true, then prefer a book that is given in force_excel if this option is set
93
+ forced_excel =
89
94
  (options[:force][:excel].nil? || options[:force][:excel] == :current) ?
90
- excel_class.new(:reuse => true) : excel_of(options[:force][:excel])
91
- end
95
+ (excel_class.new(:reuse => true) if RUBY_PLATFORM !~ /java/) : excel_of(options[:force][:excel])
92
96
  begin
93
97
  book = if File.exists?(file)
94
98
  bookstore.fetch(file, :prefer_writable => !(options[:read_only]),
@@ -97,22 +101,13 @@ module RobustExcelOle
97
101
  rescue
98
102
  trace "#{$!.message}"
99
103
  end
100
- if book
101
- if (!(options[:force][:excel]) || (forced_excel == book.excel)) &&
102
- !(book.alive? && !book.saved && (options[:if_unsaved] != :accept))
103
- book.ensure_excel(options) # unless book.excel.alive?
104
- # if the ReadOnly status shall be changed, save, close and reopen it
105
- # removed the feature for the next time
106
- if book.alive? && ((!book.writable && !(options[:read_only])) ||
107
- (book.writable && options[:read_only]))
108
- book.save if book.writable && !book.saved
109
- book.close(:if_unsaved => :forget)
110
- end
111
- # reopens the book if it was closed
112
- book.ensure_workbook(file,options) unless book.alive?
113
- book.visible = options[:force][:visible] unless options[:force][:visible].nil?
114
- book.CheckCompatibility = options[:check_compatibility] unless options[:check_compatibility].nil?
115
- book.excel.calculation = options[:calculation] unless options[:calculation].nil?
104
+ if book
105
+ # drop the fetched workbook if it shall be opened in another Excel instance
106
+ # or the workbook is an unsaved workbook that should not be accepted
107
+ if (options[:force][:excel].nil? || options[:force][:excel] == :current || forced_excel == book.excel) &&
108
+ !(book.alive? && !book.saved && (options[:if_unsaved] != :accept))
109
+ options[:force][:excel] = book.excel if book.excel && book.excel.alive?
110
+ book.ensure_workbook(file,options)
116
111
  return book
117
112
  end
118
113
  end
@@ -134,11 +129,11 @@ module RobustExcelOle
134
129
  # use the Excel instance where the workbook is opened
135
130
  ole_excel = WIN32OLE.connect(workbook.Fullname).Application rescue nil
136
131
  @excel = excel_class.new(ole_excel)
137
- @excel.visible = options[:force][:visible] unless options[:force][:visible].nil?
138
- @excel.calculation = options[:calculation] unless options[:calculation].nil?
132
+ filename = @ole_workbook.Fullname.tr('\\','/')
133
+ set_options(filename, options)
139
134
  else
140
- filename = file_or_workbook
141
- ensure_excel(options)
135
+ filename = file_or_workbook
136
+ #ensure_excel(options) #unless RUBY_PLATFORM =~ /java/ && (options[:force][:excel].nil? || options[:force][:excel] == :current)
142
137
  ensure_workbook(filename, options)
143
138
  end
144
139
  bookstore.store(self)
@@ -175,8 +170,8 @@ module RobustExcelOle
175
170
  erg[new_key] = value
176
171
  end
177
172
  end
178
- erg[:default] = {} if erg[:default].nil?
179
- erg[:force] = {} if erg[:force].nil?
173
+ erg[:default] ||= {}
174
+ erg[:force] ||= {}
180
175
  force_list = [:visible, :excel]
181
176
  erg.each { |key,value| erg[:force][key] = value if force_list.include?(key) }
182
177
  erg[:default][:excel] = erg[:default_excel] unless erg[:default_excel].nil?
@@ -186,8 +181,7 @@ module RobustExcelOle
186
181
  erg
187
182
  end
188
183
  opts = translator.call(options)
189
- default_open_opts = proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS :
190
- {:default => {:excel => :current}, :force => {}, :update_links => :never }
184
+ default_open_opts = proc_opts[:use_defaults] ? DEFAULT_OPEN_OPTS : CORE_DEFAULT_OPTEN_OPTS
191
185
  default_opts = translator.call(default_open_opts)
192
186
  opts = default_opts.merge(opts)
193
187
  opts[:default] = default_opts[:default].merge(opts[:default]) unless opts[:default].nil?
@@ -198,21 +192,11 @@ module RobustExcelOle
198
192
  # returns an Excel object when given Excel, Workbook or Win32ole object representing a Workbook or an Excel
199
193
  # @private
200
194
  def self.excel_of(object)
201
- if object.is_a? WIN32OLE
202
- case object.ole_obj_help.name
203
- when /Workbook/i
204
- new(object).excel
205
- when /Application/i
206
- excel_class.new(object)
207
- else
208
- object.excel
209
- end
210
- else
211
- begin
212
- object.excel
213
- rescue
214
- raise TypeREOError, 'given object is neither an Excel, a Workbook, nor a Win32ole'
215
- end
195
+ begin
196
+ object = object.to_reo if object.is_a? WIN32OLE
197
+ object.excel
198
+ rescue
199
+ raise TypeREOError, 'given object is neither an Excel, a Workbook, nor a Win32ole'
216
200
  end
217
201
  end
218
202
 
@@ -221,50 +205,58 @@ module RobustExcelOle
221
205
  # @private
222
206
  # ensures an excel but not for jruby if current Excel shall be used
223
207
  def ensure_excel(options)
208
+ return if @excel && @excel.alive?
224
209
  excel_option = options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
225
210
  @excel = if excel_option == :new
226
211
  excel_class.new(:reuse => false)
227
212
  elsif excel_option.nil? || excel_option == :current
228
- excel_class.new(:reuse => true) unless RUBY_PLATFORM =~ /java/
213
+ excel_class.new(:reuse => true) # unless RUBY_PLATFORM =~ /java/
229
214
  else #elsif excel_options.is_a?(WIN32OLE) || excel_option.is_a?(Excel) #excel_option.respond_to?(:Hwnd)
230
215
  self.class.excel_of(excel_option)
231
216
  end
232
- if @excel && @excel.alive?
233
- @excel.visible = options[:force][:visible] unless options[:force][:visible].nil?
234
- @excel.calculation = options[:calculation] unless options[:calculation].nil?
235
- end
217
+ raise ExcelREOError, "excel is not alive" unless @excel && @excel.alive?
236
218
  end
237
219
 
238
-
239
220
  # @private
240
221
  # restriction for jruby: does not manage conflicts with blocking or unsaved workbooks
241
222
  def ensure_workbook(filename, options)
242
- return if @ole_workook && alive?
243
- filename = @stored_filename ? @stored_filename : filename
244
- manage_nonexisting_file(filename,options)
245
- if RUBY_PLATFORM =~ /java/ && (options[:force][:excel].nil? || options[:force][:excel] == :current)
246
- begin
247
- connect(filename,options)
248
- rescue WorkbookConnectingNotAliveError
249
- raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(filename).inspect}"
250
- rescue WorkbookConnectingBlockingError # can't find moniker
251
- raise WorkbookBlocked, "can't open workbook #{filename}"+
252
- "\nbecause it is being blocked by a workbook with the same name in a different path."
253
- end
254
- else
255
- workbooks = @excel.Workbooks
256
- @ole_workbook = workbooks.Item(File.basename(filename)) rescue nil if @ole_workbook.nil?
257
- if @ole_workbook
258
- manage_blocking_or_unsaved_workbook(filename,options)
259
- else
260
- if options[:force][:excel].nil? || options[:force][:excel] == :current
223
+ if options[:if_unsaved]==:accept && options[:read_only]==true
224
+ raise OptionInvalid, ":if_unsaved:accept and :read_only:true not possible"
225
+ end
226
+ unless @ole_workbook && alive?
227
+ filename = @stored_filename ? @stored_filename : filename
228
+ manage_nonexisting_file(filename,options)
229
+ excel_option = options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
230
+ if RUBY_PLATFORM =~ /java/ &&
231
+ (excel_option.nil? || excel_option == :current) && filename[0] != '/'
232
+ begin
261
233
  connect(filename,options)
262
- else
263
- open_or_create_workbook(filename,options)
234
+ rescue WorkbookConnectingUnsavedError
235
+ raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(filename).inspect}"
236
+ rescue WorkbookConnectingBlockingError
237
+ raise WorkbookBlocked, "can't open workbook #{filename}"+
238
+ "\nbecause it is being blocked by a workbook with the same name in a different path."
239
+ rescue WorkbookConnectingUnknownError
240
+ raise WorkbookREOError, "can't connect to workbook #{filename}"
264
241
  end
265
- end
242
+ manage_unsaved_workbook(filename,options) unless @ole_workbook.Saved
243
+ else
244
+ ensure_excel(options)
245
+ workbooks = @excel.Workbooks
246
+ @ole_workbook = workbooks.Item(File.basename(filename)) rescue nil if @ole_workbook.nil?
247
+ if @ole_workbook
248
+ manage_blocking_or_unsaved_workbook(filename,options)
249
+ else
250
+ if excel_option.nil? || excel_option == :current &&
251
+ (RUBY_PLATFORM !~ /java/ || filename[0] != '/')
252
+ connect(filename,options)
253
+ else
254
+ open_or_create_workbook(filename,options)
255
+ end
256
+ end
257
+ end
266
258
  end
267
- set_options(options)
259
+ set_options(filename, options)
268
260
  end
269
261
 
270
262
  private
@@ -272,16 +264,24 @@ module RobustExcelOle
272
264
  # @private
273
265
  # connects to an unknown workbook and returns true, if it exists
274
266
  def connect(filename,options)
275
- abs_filename = General.absolute_path(filename).tr('/','\\')
267
+ abs_filename = General.absolute_path(filename)
276
268
  @ole_workbook = begin
277
269
  WIN32OLE.connect(abs_filename)
278
270
  rescue
279
- raise WorkbookConnectingBlockingError
271
+ if $!.message =~ /moniker/
272
+ raise WorkbookConnectingBlockingError
273
+ else
274
+ raise WorkbookConnectingUnknownError
275
+ end
280
276
  end
281
277
  ole_excel = begin
282
- WIN32OLE.connect(@ole_workbook.Fullname).Application
278
+ @ole_workbook.Application
283
279
  rescue
284
- raise WorkbookConnectingNotAliveError
280
+ if $!.message =~ /dispid/
281
+ raise WorkbookConnectingUnsavedError
282
+ else
283
+ raise WorkbookConnectingUnknownError
284
+ end
285
285
  end
286
286
  @excel = excel_class.new(ole_excel)
287
287
  end
@@ -289,9 +289,9 @@ module RobustExcelOle
289
289
  # @private
290
290
  def manage_nonexisting_file(filename,options)
291
291
  return if File.exist?(filename)
292
- abs_filename = General.absolute_path(filename).tr('/','\\')
292
+ abs_filename = General.absolute_path(filename)
293
293
  if options[:if_absent] == :create
294
- ensure_excel({:force[:excel] => :new}.merge(options)) if RUBY_PLATFORM !~ /java/
294
+ ensure_excel(options) unless @excel && @excel.alive?
295
295
  @excel.Workbooks.Add
296
296
  empty_ole_workbook = excel.Workbooks.Item(excel.Workbooks.Count)
297
297
  begin
@@ -308,16 +308,7 @@ module RobustExcelOle
308
308
  # @private
309
309
  def manage_blocking_or_unsaved_workbook(filename,options)
310
310
  obstructed_by_other_book = if (File.basename(filename) == File.basename(@ole_workbook.Fullname))
311
- p1 = General.absolute_path(filename)
312
- p2 = @ole_workbook.Fullname
313
- is_same_path = if p1[1..1] == ":" and p2[0..1] == '\\\\' # normal path starting with the drive letter and a path in network notation (starting with 2 backslashes)
314
- # this is a Workaround for Excel2010 on WinXP: Network drives won't get translated to drive letters. So we'll do it manually:
315
- p1_pure_path = p1[2..-1]
316
- p2.end_with?(p1_pure_path) and p2[0, p2.rindex(p1_pure_path)] =~ /^\\\\[\w\d_]+(\\[\w\d_]+)*$/ # e.g. "\\\\server\\folder\\subfolder"
317
- else
318
- p1 == p2
319
- end
320
- not is_same_path
311
+ General.absolute_path(filename) != @ole_workbook.Fullname
321
312
  end
322
313
  if obstructed_by_other_book
323
314
  # workbook is being obstructed by a workbook with same name and different path
@@ -340,25 +331,17 @@ module RobustExcelOle
340
331
  to allow automatic closing of the old workbook (without or with saving before, respectively),
341
332
  before the new workbook is being opened."
342
333
  when :forget
343
- @excel.with_displayalerts(false) { @ole_workbook.Close }
344
- @ole_workbook = nil
345
- open_or_create_workbook(filename, options)
334
+ manage_forgetting_workbook(filename, options)
346
335
  when :save
347
- save unless @ole_workbook.Saved
348
- @ole_workbook.Close
349
- @ole_workbook = nil
350
- open_or_create_workbook(filename, options)
336
+ manage_saving_workbook(filename, options)
351
337
  when :close_if_saved
352
338
  if !@ole_workbook.Saved
353
339
  raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{@ole_workbook.Fullname.tr('\\','/')}"
354
340
  else
355
- @ole_workbook.Close
356
- @ole_workbook = nil
357
- open_or_create_workbook(filename, options)
341
+ manage_forgetting_workbook(filename, options)
358
342
  end
359
343
  when :new_excel
360
- @excel = excel_class.new(:reuse => false)
361
- open_or_create_workbook(filename, options)
344
+ manage_new_excel(filename, options)
362
345
  else
363
346
  raise OptionInvalid, ":if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
364
347
  "\nHint: Valid values are :raise, :forget, :save, :close_if_saved, :new_excel"
@@ -373,72 +356,88 @@ module RobustExcelOle
373
356
  "\nHint: Save the workbook or open the workbook using option :if_unsaved with values :forget and :accept to
374
357
  close the unsaved workbook and reopen it, or to let the unsaved workbook open, respectively"
375
358
  when :forget
376
- @excel.with_displayalerts(false) { @ole_workbook.Close }
377
- @ole_workbook = nil
378
- open_or_create_workbook(filename, options)
359
+ manage_forgetting_workbook(filename,options)
379
360
  when :accept
380
361
  # do nothing
362
+ when :save
363
+ manage_saving_workbook(filename, options)
381
364
  when :alert, :excel
382
365
  @excel.with_displayalerts(true) { open_or_create_workbook(filename,options) }
383
366
  when :new_excel
384
- @excel = excel_class.new(:reuse => false)
385
- open_or_create_workbook(filename, options)
367
+ manage_new_excel(filename, options)
386
368
  else
387
369
  raise OptionInvalid, ":if_unsaved: invalid option: #{options[:if_unsaved].inspect}" +
388
- "\nHint: Valid values are :raise, :forget, :accept, :alert, :excel, :new_excel"
370
+ "\nHint: Valid values are :raise, :forget, :save, :accept, :alert, :excel, :new_excel"
389
371
  end
390
372
  end
391
373
 
392
374
  # @private
393
- def open_or_create_workbook(filename, options)
394
- if !@ole_workbook || (options[:if_unsaved] == :alert) || options[:if_obstructed]
375
+ def manage_forgetting_workbook(filename, options)
376
+ @excel.with_displayalerts(false) { @ole_workbook.Close }
377
+ @ole_workbook = nil
378
+ open_or_create_workbook(filename, options)
379
+ end
380
+
381
+ # @private
382
+ def manage_saving_workbook(filename, options)
383
+ save unless @ole_workbook.Saved
384
+ manage_forgetting_workbook(filename, options)
385
+ end
386
+
387
+ # @private
388
+ def manage_new_excel(filename, options)
389
+ @excel = excel_class.new(:reuse => false)
390
+ @ole_workbook = nil
391
+ open_or_create_workbook(filename, options)
392
+ end
393
+
394
+ # @private
395
+ def open_or_create_workbook(filename, options)
396
+ return if @ole_workbook && options[:if_unsaved] != :alert && options[:if_unsaved] != :excel
397
+ begin
398
+ abs_filename = General.absolute_path(filename)
395
399
  begin
396
- abs_filename = General.absolute_path(filename)
397
- begin
398
- workbooks = @excel.Workbooks
399
- rescue WIN32OLERuntimeError => msg
400
- raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
401
- end
402
- begin
403
- with_workaround_linked_workbooks_excel2007(options) do
404
- # temporary workaround until jruby-win32ole implements named parameters (Java::JavaLang::RuntimeException (createVariant() not implemented for class org.jruby.RubyHash)
405
- workbooks.Open(abs_filename,
406
- updatelinks_vba(options[:update_links]),
407
- options[:read_only] )
408
- end
409
- rescue WIN32OLERuntimeError => msg
410
- # for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
411
- # if yes: distinguish these events
412
- raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
400
+ workbooks = @excel.Workbooks
401
+ rescue WIN32OLERuntimeError => msg
402
+ raise UnexpectedREOError, "cannot access workbooks: #{msg.message} #{msg.backtrace}"
403
+ end
404
+ begin
405
+ with_workaround_linked_workbooks_excel2007(options) do
406
+ # temporary workaround until jruby-win32ole implements named parameters (Java::JavaLang::RuntimeException (createVariant() not implemented for class org.jruby.RubyHash)
407
+ workbooks.Open(abs_filename,
408
+ updatelinks_vba(options[:update_links]),
409
+ options[:read_only] )
413
410
  end
411
+ rescue WIN32OLERuntimeError => msg
412
+ # for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
413
+ # if yes: distinguish these events
414
+ raise UnexpectedREOError, "cannot open workbook: #{msg.message} #{msg.backtrace}"
415
+ end
416
+ begin
417
+ # workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
414
418
  begin
415
- # workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
416
- begin
417
- @ole_workbook = workbooks.Item(File.basename(filename))
418
- rescue WIN32OLERuntimeError => msg
419
- raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
420
- end
419
+ @ole_workbook = workbooks.Item(File.basename(filename))
421
420
  rescue WIN32OLERuntimeError => msg
422
- raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
421
+ raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message}"
423
422
  end
423
+ rescue WIN32OLERuntimeError => msg
424
+ raise UnexpectedREOError, "WIN32OLERuntimeError: #{msg.message} #{msg.backtrace}"
424
425
  end
425
426
  end
426
427
  end
427
428
 
428
429
  # @private
429
- def set_options(options)
430
- if options[:force][:visible].nil? && !options[:default][:visible].nil?
431
- if @excel.created
432
- self.visible = options[:default][:visible]
433
- else
434
- self.window_visible = options[:default][:visible]
435
- end
436
- else
437
- self.visible = options[:force][:visible] unless options[:force][:visible].nil?
430
+ def set_options(filename, options)
431
+ if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
432
+ @excel.with_displayalerts(false) { @ole_workbook.Close }
433
+ @ole_workbook = nil
434
+ open_or_create_workbook(filename, options)
435
+ end
436
+ retain_saved do
437
+ self.visible = options[:force][:visible].nil? ? @excel.Visible : options[:force][:visible]
438
+ @excel.calculation = options[:calculation] unless options[:calculation].nil?
439
+ @ole_workbook.CheckCompatibility = options[:check_compatibility] unless options[:check_compatibility].nil?
438
440
  end
439
- @excel.calculation = options[:calculation] unless options[:calculation].nil?
440
- @ole_workbook.CheckCompatibility = options[:check_compatibility]
441
- @ole_workbook.Saved = true # unless self.Saved # ToDo: this is too hard
442
441
  end
443
442
 
444
443
  # @private
@@ -571,6 +570,7 @@ module RobustExcelOle
571
570
  # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening
572
571
  # @return [Workbook] a workbook
573
572
  def self.unobtrusively(file, opts = { })
573
+ opts = process_options(opts, :use_defaults => false)
574
574
  opts = {:if_closed => :current,
575
575
  :rw_change_excel => :current,
576
576
  :keep_open => false}.merge(opts)
@@ -597,12 +597,16 @@ module RobustExcelOle
597
597
  book =
598
598
  if was_open
599
599
  if change_rw_mode
600
- open(file, :force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write)
600
+ opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write})
601
+ open(file, opts)
602
+ #open(file, :force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write)
601
603
  else
602
604
  book
603
605
  end
604
606
  else
605
- open(file, :force => {:excel => opts[:if_closed]}, :read_only => do_not_write)
607
+ opts = opts.merge({:force => {:excel => opts[:if_closed]}, :read_only => do_not_write})
608
+ open(file, opts)
609
+ #open(file, :force => {:excel => opts[:if_closed]}, :read_only => do_not_write)
606
610
  end
607
611
  yield book
608
612
  ensure
@@ -611,7 +615,8 @@ module RobustExcelOle
611
615
  if was_open
612
616
  if opts[:rw_change_excel] == book.excel && change_rw_mode
613
617
  book.close
614
- book = open(file, :force => {:excel => opts[:rw_change_excel]}, :read_only => !was_writable)
618
+ opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => !was_writable})
619
+ book = open(file, opts)
615
620
  end
616
621
  book.excel.calculation = was_calculation
617
622
  book.CheckCompatibility = was_check_compatibility
@@ -632,9 +637,8 @@ module RobustExcelOle
632
637
  end
633
638
 
634
639
  # simple save of a workbook.
635
- # @option opts [Boolean] :discoloring states, whether colored ranges shall be discolored
636
640
  # @return [Boolean] true, if successfully saved, nil otherwise
637
- def save(opts = {:discoloring => false})
641
+ def save(opts = { }) # option opts is deprecated #
638
642
  raise ObjectNotAlive, 'workbook is not alive' unless alive?
639
643
  raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
640
644
  # if you have open the workbook with :read_only => true,
@@ -642,7 +646,6 @@ module RobustExcelOle
642
646
  # otherwise the workbook may already be open writable in an another Excel instance
643
647
  # then you could use this workbook or close the workbook there
644
648
  begin
645
- discoloring if opts[:discoloring]
646
649
  @modified_cells = []
647
650
  @ole_workbook.Save
648
651
  rescue WIN32OLERuntimeError => msg
@@ -666,26 +669,23 @@ module RobustExcelOle
666
669
  # :overwrite -> writes the file, delete the old file
667
670
  # :alert or :excel -> gives control to Excel
668
671
  # :if_obstructed if a workbook with the same name and different path is already open and blocks the saving, then
669
- # :raise -> raises an exception
670
- # :forget -> closes the blocking workbook
672
+ # or :raise -> raises an exception
673
+ # :if_blocked :forget -> closes the blocking workbook
671
674
  # :save -> saves the blocking workbook and closes it
672
675
  # :close_if_saved -> closes the blocking workbook, if it is saved,
673
- # otherwise raises an exception
674
- # :discoloring states, whether colored ranges shall be discolored
676
+ # otherwise raises an exception
675
677
  # @return [Workbook], the book itself, if successfully saved, raises an exception otherwise
676
678
  def save_as(file, opts = { })
677
679
  raise FileNameNotGiven, 'filename is nil' if file.nil?
678
680
  raise ObjectNotAlive, 'workbook is not alive' unless alive?
679
681
  raise WorkbookReadOnly, 'Not opened for writing (opened with :read_only option)' if @ole_workbook.ReadOnly
680
- options = {
681
- :if_exists => :raise,
682
- :if_obstructed => :raise
683
- }.merge(opts)
682
+ raise(FileNotFound, "file #{General.absolute_path(file).inspect} is a directory") if File.directory?(file)
683
+ options = self.class.process_options(opts)
684
684
  if File.exist?(file)
685
685
  case options[:if_exists]
686
686
  when :overwrite
687
687
  if file == self.filename
688
- save({:discoloring => opts[:discoloring]})
688
+ save
689
689
  return self
690
690
  else
691
691
  begin
@@ -712,7 +712,7 @@ module RobustExcelOle
712
712
  case options[:if_obstructed]
713
713
  when :raise
714
714
  raise WorkbookBlocked, "blocked by another workbook: #{other_workbook.Fullname.tr('\\','/')}" +
715
- "\nHint: Use the option :if_obstructed with values :forget or :save to
715
+ "\nHint: Use the option :if_blocked with values :forget or :save to
716
716
  close or save and close the blocking workbook"
717
717
  when :forget
718
718
  # nothing
@@ -721,25 +721,20 @@ module RobustExcelOle
721
721
  when :close_if_saved
722
722
  unless other_workbook.Saved
723
723
  raise WorkbookBlocked, "blocking workbook is unsaved: #{File.basename(file).inspect}" +
724
- "\nHint: Use option :if_obstructed => :save to save the blocking workbooks"
724
+ "\nHint: Use option :if_blocked => :save to save the blocking workbooks"
725
725
  end
726
726
  else
727
- raise OptionInvalid, ":if_obstructed: invalid option: #{options[:if_obstructed].inspect}" +
727
+ raise OptionInvalid, ":if_blocked: invalid option: #{options[:if_obstructed].inspect}" +
728
728
  "\nHint: Valid values are :raise, :forget, :save, :close_if_saved"
729
729
  end
730
730
  other_workbook.Close
731
731
  end
732
732
  save_as_workbook(file, options)
733
733
  self
734
- end
734
+ end
735
735
 
736
736
  private
737
737
 
738
- # @private
739
- def discoloring
740
- @modified_cells.each { |cell| cell.Interior.ColorIndex = XlNone }
741
- end
742
-
743
738
  # @private
744
739
  def save_as_workbook(file, options)
745
740
  dirname, basename = File.split(file)
@@ -749,7 +744,6 @@ module RobustExcelOle
749
744
  when '.xlsx' then RobustExcelOle::XlOpenXMLWorkbook
750
745
  when '.xlsm' then RobustExcelOle::XlOpenXMLWorkbookMacroEnabled
751
746
  end
752
- discoloring if options[:discoloring]
753
747
  @modified_cells = []
754
748
  @ole_workbook.SaveAs(General.absolute_path(file), file_format)
755
749
  bookstore.store(self)
@@ -832,7 +826,7 @@ module RobustExcelOle
832
826
  after_or_before, base_sheet = opts.to_a.first || [:after, last_sheet_local]
833
827
  begin
834
828
  if RUBY_PLATFORM !~ /java/
835
- if sheet
829
+ if sheet
836
830
  sheet.Copy({ after_or_before.to_s => base_sheet.ole_worksheet })
837
831
  else
838
832
  @ole_workbook.Worksheets.Add({ after_or_before.to_s => base_sheet.ole_worksheet })
@@ -862,11 +856,12 @@ module RobustExcelOle
862
856
  ole_workbook.Worksheets.Add(base_sheet.ole_worksheet)
863
857
  end
864
858
  base_sheet.Move(ole_workbook.Worksheets.Item(ole_workbook.Worksheets.Count-1))
859
+ ole_workbook.Worksheets.Item(ole_workbook.Worksheets.Count).Activate
865
860
  end
866
861
  end
867
862
  end
868
863
  rescue #WIN32OLERuntimeError
869
- trace "#{$!.message}"
864
+ #trace "#{$!.message}"
870
865
  raise WorksheetREOError, "could not add given worksheet #{sheet.inspect}"
871
866
  end
872
867
  #ole_sheet = @excel.Activesheet
@@ -906,7 +901,10 @@ module RobustExcelOle
906
901
  # @param [String] name the name of the range
907
902
  # @param [Variant] value the contents of the range
908
903
  def []= (name, value)
909
- set_namevalue_glob(name,value, :color => 42) # 42 - aqua-marin, 4-green
904
+ old_color_if_modified = @color_if_modified
905
+ workbook.color_if_modified = 42 # 42 - aqua-marin, 4-green
906
+ set_namevalue_glob(name,value)
907
+ workbook.color_if_modified = old_color_if_modified
910
908
  end
911
909
 
912
910
  # sets options
@@ -981,7 +979,7 @@ module RobustExcelOle
981
979
  def visible= visible_value
982
980
  return if visible_value.nil?
983
981
  @excel.visible = true if visible_value
984
- self.window_visible = visible_value
982
+ self.window_visible = @excel.Visible ? visible_value : true
985
983
  end
986
984
 
987
985
  # returns true, if the window of the workbook is set to visible, false otherwise