robust_excel_ole 1.11 → 1.12

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