robust_excel_ole 1.13 → 1.14

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.
@@ -6,17 +6,17 @@
6
6
 
7
7
  Imagine, you have opened a workbook with
8
8
 
9
- book = Workbook.open('spec/data/workbook.xls', :visible => true)
9
+ workbook = Workbook.open('spec/data/workbook.xls', :visible => true)
10
10
 
11
11
  and have modified it.
12
12
 
13
13
  You can save the workbook by
14
14
 
15
- book.save
15
+ workbook.save
16
16
 
17
17
  If you want to save a workbook with a file name, then use
18
18
 
19
- book.save_as('new_workbook.xls')
19
+ workbook.save_as('new_workbook.xls')
20
20
 
21
21
  The options and respective valid values are the following:
22
22
 
@@ -33,19 +33,19 @@ If a workbook with the file name already exists, then
33
33
 
34
34
  For example, you want to save a workbook and overwrite the file if it exists before, then use
35
35
 
36
- book.save_as('another_workbook.xls', :if_exists => :overwrite)
36
+ workbook.save_as('another_workbook.xls', :if_exists => :overwrite)
37
37
 
38
38
  If a workbook blocks the workbook that should be saved, then the former one can be saved and closed before.
39
39
 
40
- book = Workbook.open('spec/data/workbook.xls')
41
- book2 = Workbook.open('spec/data/another_workbook.xls')
42
- book2.save_as('dir/workbook.xls', :if_exists => :overwrite, :if_obstructed => :save)
40
+ workbook = Workbook.open('spec/data/workbook.xls')
41
+ workbook2 = Workbook.open('spec/data/another_workbook.xls')
42
+ workbook2.save_as('dir/workbook.xls', :if_exists => :overwrite, :if_obstructed => :save)
43
43
 
44
44
  === Closing a workbook.
45
45
 
46
46
  You can close the workbook with the command
47
47
 
48
- book.close
48
+ workbook.close
49
49
 
50
50
  There is one option: +:if_unsaved+ . It can have one of the following values:
51
51
 
@@ -62,7 +62,7 @@ The option specifies: If the workbook is unsaved, then
62
62
 
63
63
  You can open a workbook with given file name.
64
64
 
65
- book = Workbook.open('spec/data/workbook.xls')
65
+ workbook = Workbook.open('spec/data/workbook.xls')
66
66
 
67
67
  You can save a workbook with given file name, if it is open.
68
68
 
@@ -6,30 +6,30 @@ Worksheets are represented by Worksheet objects.
6
6
 
7
7
  Assume you have opened a workbook
8
8
 
9
- book = Workbook.open('spec/data/workbook.xls', :visible => true)
9
+ workbook = Workbook.open('spec/data/workbook.xls', :visible => true)
10
10
 
11
11
  === Accessing a worksheet.
12
12
 
13
13
  You can access a worksheet by giving the number
14
14
 
15
- sheet = book.sheet(1)
15
+ worksheet = book.sheet(1)
16
16
 
17
17
  or its name
18
18
 
19
- sheet = book.sheet('Sheet1')
19
+ worksheet = book.sheet('Sheet1')
20
20
 
21
21
  You can get the first and last worksheet with
22
22
 
23
- sheet = book.first_sheet
23
+ worksheet = book.first_sheet
24
24
 
25
25
  and
26
26
 
27
- sheet = book.last_sheet
27
+ worksheet = book.last_sheet
28
28
 
29
- You can access all Sheet objects by using the methods Workbook#each.
29
+ You can access all Worksheet objects by using the methods Workbook#each.
30
30
 
31
- book.each do |sheet|
32
- # do something with sheet
31
+ workbook.each do |worksheet|
32
+ # do something with worksheet
33
33
  end
34
34
 
35
35
  Once you have got a Sheet object (in RobustExcelOle), you can apply all VBA methods that you would apply to a VBA Worksheet object
@@ -40,38 +40,38 @@ For some common and complex tasks you can apply methods of RobustExcelOle.
40
40
 
41
41
  You can read and change the worksheet name.
42
42
 
43
- sheet1.name
43
+ worksheet1.name
44
44
  # => "Sheet1"
45
45
 
46
- sheet1.name = "new_sheet"
46
+ worksheet1.name = "new_sheet"
47
47
 
48
48
  === Adding and copying a worksheet.
49
49
 
50
50
  You can add (append) an empty worksheet using
51
51
 
52
- book.add_empty_sheet
52
+ workbook.add_empty_sheet
53
53
 
54
54
  Additionally you can name it.
55
55
 
56
- book.add_empty_sheet(:as => 'sheet_name')
56
+ workbook.add_empty_sheet(:as => 'sheet_name')
57
57
 
58
58
  You can specify the position of the added empty worksheet.
59
59
 
60
- book.add_empty_sheet(:as => 'new_name', :before => another_sheet)
60
+ workbook.add_empty_sheet(:as => 'new_name', :before => another_sheet)
61
61
 
62
62
  You can copy a worksheet and add it.
63
63
 
64
- book.copy_sheet sheet
64
+ workbook.copy_sheet sheet
65
65
 
66
66
  Additionally you can specify a name and a position.
67
67
 
68
- book.copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
68
+ workbook.copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
69
69
 
70
70
  If you want to copy a worksheet, if a worksheet +sheet+ is given, and add an empty worksheet, if no worksheet is given, then use
71
71
 
72
- book.add_or_copy_sheet
72
+ workbook.add_or_copy_sheet
73
73
 
74
- book.add_or_copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
74
+ workbook.add_or_copy_sheet(sheet, :as => 'new_name', :after => another_sheet)
75
75
 
76
76
  Note, that running in jruby, due to some restrictions of jruby, there is a workaround when adding or copy a worksheet at the end (appending): the last worksheet is being copied and deleted afterwards, in order to serve as a dummy worksheet. This may cause a different behaviour.
77
77
 
@@ -14,5 +14,7 @@ require File.join(File.dirname(__FILE__), 'robust_excel_ole/worksheet')
14
14
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/cell')
15
15
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/range')
16
16
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/cygwin') if RUBY_PLATFORM =~ /cygwin/
17
- #+#require "robust_excel_ole/version"
18
17
  require File.join(File.dirname(__FILE__), 'robust_excel_ole/version')
18
+
19
+ include RobustExcelOle
20
+ include General
@@ -76,12 +76,7 @@ module RobustExcelOle
76
76
  ole_xl = win32ole_excel unless win32ole_excel.nil?
77
77
  options = { :reuse => true }.merge(options)
78
78
  if options[:reuse] == true && ole_xl.nil?
79
- ole_xl = if JRUBY_BUG_CONNECTEXCEL
80
- excel_instance = known_excel_instance
81
- excel_instance.ole_excel unless excel_instance.nil?
82
- else
83
- current_excel
84
- end
79
+ ole_xl = current_ole_excel
85
80
  end
86
81
  connected = (not ole_xl.nil?) && win32ole_excel.nil?
87
82
  ole_xl ||= WIN32OLE.new('Excel.Application')
@@ -142,33 +137,6 @@ module RobustExcelOle
142
137
 
143
138
  private
144
139
 
145
- # returns a Win32OLE object that represents a Excel instance to which Excel connects
146
- # connects to the first opened Excel instance
147
- # if this Excel instance is being closed, then Excel creates a new Excel instance
148
- # @private
149
- def self.current_excel
150
- result = if result.nil?
151
- begin
152
- WIN32OLE.connect('Excel.Application')
153
- rescue
154
- nil
155
- end
156
- end
157
- if result
158
- begin
159
- result.Visible # send any method, just to see if it responds
160
- rescue
161
- trace 'dead excel ' + (begin
162
- "Window-handle = #{result.HWnd}"
163
- rescue
164
- 'without window handle'
165
- end)
166
- return nil
167
- end
168
- end
169
- result
170
- end
171
-
172
140
  # retain the saved status of all workbooks
173
141
  # @private
174
142
  def retain_saved_workbooks
@@ -424,6 +392,43 @@ module RobustExcelOle
424
392
  @@hwnd2excel.size
425
393
  end
426
394
 
395
+ #private
396
+
397
+ # returns a Win32OLE object that represents a Excel instance to which Excel connects
398
+ # connects to the first opened Excel instance
399
+ # if this Excel instance is being closed, then Excel creates a new Excel instance
400
+ # @private
401
+ def self.current_ole_excel
402
+ if JRUBY_BUG_CONNECT
403
+ result = known_excel_instance
404
+ if result.nil?
405
+ if excels_number > 0
406
+ dummy_ole_workbook = WIN32OLE.connect(General.absolute_path('___dummy_workbook.xls')) rescue nil
407
+ result = dummy_ole_workbook.Application
408
+ visible_status = result.Visible
409
+ dummy_ole_workbook.Close
410
+ dummy_ole_workbook = nil
411
+ result.Visible = visible_status
412
+ end
413
+ end
414
+ else
415
+ result = WIN32OLE.connect('Excel.Application') rescue nil
416
+ end
417
+ if result
418
+ begin
419
+ result.Visible # send any method, just to see if it responds
420
+ rescue
421
+ trace 'dead excel ' + (begin
422
+ "Window-handle = #{result.HWnd}"
423
+ rescue
424
+ 'without window handle'
425
+ end)
426
+ return nil
427
+ end
428
+ end
429
+ result
430
+ end
431
+
427
432
  # returns an Excel instance
428
433
  def self.known_excel_instance
429
434
  @@hwnd2excel.each do |hwnd, wr_excel|
@@ -173,8 +173,7 @@ module RobustExcelOle
173
173
  home = homes.find { |h| !ENV[h].nil? }
174
174
  reo_log_dir = ENV[home]
175
175
  else
176
- #reo_log_dir = REO_LOG_DIR
177
- reo_log_dir = "C:/Users/User"
176
+ reo_log_dir = REO_LOG_DIR
178
177
  end
179
178
  File.open(reo_log_dir + '/' + REO_LOG_FILE,'a') do |file|
180
179
  file.puts text
@@ -1,3 +1,3 @@
1
1
  module RobustExcelOle
2
- VERSION = "1.13"
2
+ VERSION = "1.14"
3
3
  end
@@ -11,10 +11,13 @@ module RobustExcelOle
11
11
 
12
12
  class Workbook < RangeOwners
13
13
 
14
+ #include General
15
+
14
16
  attr_accessor :excel
15
17
  attr_accessor :ole_workbook
16
18
  attr_accessor :stored_filename
17
19
  attr_accessor :color_if_modified
20
+ attr_accessor :was_open
18
21
  attr_reader :workbook
19
22
 
20
23
  alias ole_object ole_workbook
@@ -39,7 +42,7 @@ module RobustExcelOle
39
42
 
40
43
 
41
44
  # opens a workbook.
42
- # @param [String] file the file name
45
+ # @param [String] file_or_workbook a file name or WIN32OLE workbook
43
46
  # @param [Hash] opts the options
44
47
  # @option opts [Hash] :default or :d
45
48
  # @option opts [Hash] :force or :f
@@ -106,7 +109,8 @@ module RobustExcelOle
106
109
  rescue
107
110
  trace "#{$!.message}"
108
111
  end
109
- if book
112
+ if book
113
+ book.was_open = book.alive?
110
114
  # drop the fetched workbook if it shall be opened in another Excel instance
111
115
  # or the workbook is an unsaved workbook that should not be accepted
112
116
  if (options[:force][:excel].nil? || options[:force][:excel] == :current || forced_excel == book.excel) &&
@@ -226,51 +230,33 @@ module RobustExcelOle
226
230
  raise ExcelREOError, "excel is not alive" unless @excel && @excel.alive?
227
231
  end
228
232
 
229
- # @private
230
- # restriction for jruby: does not manage conflicts with blocking or unsaved workbooks
233
+ # @private
231
234
  def ensure_workbook(filename, options)
232
- if options[:if_unsaved]==:accept &&
233
- ((options[:read_only]==true && self.ReadOnly==false) || (options[:read_only]==false && self.ReadOnly==true))
234
- raise OptionInvalid, ":if_unsaved:accept and change of read-only mode is not possible"
235
- end
236
235
  unless @ole_workbook && alive?
237
236
  filename = @stored_filename ? @stored_filename : filename
238
237
  manage_nonexisting_file(filename,options)
239
238
  excel_option = options[:force][:excel].nil? ? options[:default][:excel] : options[:force][:excel]
240
- if JRUBY_BUG_CONNECT
241
- (excel_option.nil? || excel_option == :current) && filename[0] != '/'
242
- begin
239
+ ensure_excel(options)
240
+ workbooks = @excel.Workbooks
241
+ @ole_workbook = workbooks.Item(File.basename(filename)) rescue nil if @ole_workbook.nil?
242
+ if @ole_workbook
243
+ @was_open = true
244
+ manage_blocking_or_unsaved_workbook(filename,options)
245
+ else
246
+ if excel_option.nil? || excel_option == :current &&
247
+ (!JRUBY_BUG_CONNECT || filename[0] != '/')
243
248
  connect(filename,options)
244
- rescue WorkbookConnectingUnsavedError
245
- raise WorkbookNotSaved, "workbook is already open but not saved: #{File.basename(filename).inspect}"
246
- rescue WorkbookConnectingBlockingError
247
- raise WorkbookBlocked, "can't open workbook #{filename}"+
248
- "\nbecause it is being blocked by a workbook with the same name in a different path."
249
- rescue WorkbookConnectingUnknownError
250
- raise WorkbookREOError, "can't connect to workbook #{filename}"
249
+ else
250
+ open_or_create_workbook(filename,options)
251
251
  end
252
- manage_unsaved_workbook(filename,options) unless @ole_workbook.Saved
253
- else
254
- ensure_excel(options)
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 excel_option.nil? || excel_option == :current &&
261
- (!JRUBY_BUG_CONNECT || filename[0] != '/')
262
- connect(filename,options)
263
- else
264
- open_or_create_workbook(filename,options)
265
- end
266
- end
267
- end
252
+ end
268
253
  end
269
254
  end
270
255
 
271
256
  # @private
272
257
  def set_options(filename, options)
273
258
  if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
259
+ #raise OptionInvalid, ":if_unsaved:accept and changing read-only mode is not possible" if options[:if_unsaved]==:accept
274
260
  @excel.with_displayalerts(false) { @ole_workbook.Close }
275
261
  @ole_workbook = nil
276
262
  open_or_create_workbook(filename, options)
@@ -282,11 +268,17 @@ module RobustExcelOle
282
268
  end
283
269
  end
284
270
 
271
+
285
272
  private
286
273
 
287
274
  # @private
288
- # connects to an unknown workbook and returns true, if it exists
275
+ # connects to an unknown workbook
289
276
  def connect(filename,options)
277
+ excels_number = excel_class.excels_number
278
+ workbooks_number = if excels_number>0
279
+ excel_class.current.Workbooks.Count
280
+ else 0
281
+ end
290
282
  abs_filename = General.absolute_path(filename)
291
283
  @ole_workbook = begin
292
284
  WIN32OLE.connect(abs_filename)
@@ -307,10 +299,13 @@ module RobustExcelOle
307
299
  end
308
300
  end
309
301
  @excel = excel_class.new(ole_excel)
302
+ excels_number_after = excel_class.excels_number
303
+ workbooks_number_after = ole_excel.Workbooks.Count
304
+ @was_open = (excels_number_after==excels_number) && (workbooks_number_after==workbooks_number)
310
305
  end
311
306
 
312
307
  # @private
313
- def manage_nonexisting_file(filename,options)
308
+ def manage_nonexisting_file(filename,options)
314
309
  return if File.exist?(filename)
315
310
  abs_filename = General.absolute_path(filename)
316
311
  if options[:if_absent] == :create
@@ -566,75 +561,107 @@ module RobustExcelOle
566
561
  unobtrusively(*args, &block)
567
562
  end
568
563
 
569
-
570
564
  # allows to read or modify a workbook such that its state remains unchanged
571
565
  # state comprises: open, saved, writable, visible, calculation mode, check compatibility
572
- # @param [String] file the file name
566
+ # @param [String] file_or_workbook a file name or WIN32OLE workbook
573
567
  # @param [Hash] opts the options
574
568
  # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
575
- # @option opts [Boolean] :read_only true/false, open the workbook in read-only/read-write modus (save changes)
576
- # @option opts [Boolean] :writable true/false changes of the workbook shall be saved/not saved
577
- # @option opts [Boolean] :rw_change_excel Excel instance in which the workbook with the new
578
- # write permissions shall be opened :current (default), :new or an Excel instance
579
- # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening
569
+ # @option opts [Boolean] :read_only true/false (default), open the workbook in read-only/read-write modus (save changes)
570
+ # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved
571
+ # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening (default: false)
580
572
  # @return [Workbook] a workbook
581
- def self.unobtrusively(file, opts = { })
573
+ def self.unobtrusively(file_or_workbook, opts = { })
582
574
  opts = process_options(opts, :use_defaults => false)
583
- opts = {:if_closed => :current,
584
- :rw_change_excel => :current,
585
- :keep_open => false}.merge(opts)
586
- raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
587
- prefer_writable = ((!(opts[:read_only]) || opts[:writable] == true) &&
588
- !(opts[:read_only].nil? && opts[:writable] == false))
589
- do_not_write = (opts[:read_only] || (opts[:read_only].nil? && opts[:writable] == false))
590
- book = bookstore.fetch(file, :prefer_writable => prefer_writable)
591
- was_open = book && book.alive?
592
- if was_open
593
- was_saved = book.saved
594
- was_writable = book.writable
575
+ raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
576
+ opts = opts.merge({:force => {:excel => opts[:if_closed]}, :if_closed => :current,
577
+ :if_unsaved => :accept, :keep_open => false})
578
+ opts = opts.merge({:read_only => opts[:read_only]}) unless opts[:read_only].nil?
579
+ file = (file_or_workbook.is_a? WIN32OLE) ? file_or_workbook.Fullname.tr('\\','/') : file_or_workbook
580
+ begin
581
+ book = open(file)
595
582
  was_visible = book.visible
596
- was_calculation = book.calculation
583
+ was_writable = book.writable
584
+ was_saved = book.saved
597
585
  was_check_compatibility = book.check_compatibility
598
- if (opts[:writable] && !was_writable && !was_saved) ||
599
- (opts[:read_only] && was_writable && !was_saved)
586
+ was_calculation = book.excel.calculation
587
+ book.set_options(file,opts)
588
+ if !was_saved && ((opts[:writable] && !was_writable) || (opts[:read_only] && was_writable))
600
589
  raise NotImplementedREOError, 'unsaved read-only workbook shall be written'
601
590
  end
602
- opts[:rw_change_excel] = book.excel if opts[:rw_change_excel] == :current
591
+ yield book
592
+ ensure
593
+ if book && book.alive?
594
+ do_not_write = (opts[:read_only] || (opts[:read_only].nil? && opts[:writable] == false))
595
+ book.save unless was_saved || do_not_write || book.ReadOnly
596
+ # open and close if the read_only mode has changed
597
+ if (opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable)
598
+ book = open(file, :read_only => !was_writable, :if_unsaved => :forget)
599
+ end
600
+ if book.was_open
601
+ book.visible = was_visible
602
+ book.CheckCompatibility = was_check_compatibility
603
+ book.excel.calculation = was_calculation
604
+ end
605
+ book.Saved = (was_saved || !book.was_open)
606
+ book.close unless book.was_open || opts[:keep_open]
607
+ end
603
608
  end
604
- change_rw_mode = ((opts[:read_only] && was_writable) || (opts[:writable] && !was_writable))
609
+ end
610
+
611
+ # allows to read or modify a workbook such that its state remains unchanged
612
+ # state comprises: open, saved, writable, visible, calculation mode, check compatibility
613
+ # @param [String] file_or_workbook a file name or WIN32OLE workbook
614
+ # @param [Hash] opts the options
615
+ # @option opts [Variant] :if_closed :current (default), :new or an Excel instance
616
+ # @option opts [Boolean] :read_only true/false (default), open the workbook in read-only/read-write modus (save changes)
617
+ # @option opts [Boolean] :writable true (default)/false changes of the workbook shall be saved/not saved
618
+ # @option opts [Boolean] :keep_open whether the workbook shall be kept open after unobtrusively opening (default: false)
619
+ # @return [Workbook] a workbook
620
+ =begin
621
+ def self.unobtrusively(file_or_workbook, opts = { })
622
+ book = new(file_or_workbook)
623
+ book.unobtrusively(opts)
624
+ end
625
+ =end
626
+
627
+ def unobtrusively(opts = { })
628
+ opts = process_options(opts, :use_defaults => false)
629
+ raise OptionInvalid, 'contradicting options' if opts[:writable] && opts[:read_only]
630
+ opts = opts.merge({:force => {:excel => opts[:if_closed]}, :if_closed => :current,
631
+ :if_unsaved => :accept, :keep_open => false})
632
+ opts = opts.merge({:read_only => opts[:read_only]}) unless opts[:read_only].nil?
633
+ file = stored_filename
605
634
  begin
606
- book =
607
- if was_open
608
- if change_rw_mode
609
- opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => do_not_write})
610
- open(file, opts)
611
- else
612
- book
613
- end
614
- else
615
- opts = opts.merge({:force => {:excel => opts[:if_closed]}, :read_only => do_not_write})
616
- open(file, opts)
617
- end
635
+ book = open(file, opts)
636
+ was_visible = book.visible
637
+ was_saved = book.saved
638
+ was_writable = book.was_writable
639
+ was_check_compatibility = book.CheckCompatibility
640
+ was_calculation = book.excel.calculation
641
+ if !was_saved && ((opts[:writable] && !was_writable) || (opts[:read_only] && was_writable))
642
+ raise NotImplementedREOError, 'unsaved read-only workbook shall be written'
643
+ end
618
644
  yield book
619
645
  ensure
620
646
  if book && book.alive?
647
+ do_not_write = (opts[:read_only] || (opts[:read_only].nil? && opts[:writable] == false))
621
648
  book.save unless book.saved || do_not_write || book.ReadOnly
622
- if was_open
623
- if opts[:rw_change_excel] == book.excel && change_rw_mode
624
- book.close
625
- opts = opts.merge({:force => {:excel => opts[:rw_change_excel]}, :read_only => !was_writable})
626
- book = open(file, opts)
627
- end
649
+ # open and close if the read_only mode has changed
650
+ if (opts[:read_only] && was_writable) || (!opts[:read_only] && !was_writable)
651
+ book = open(file, :read_only => !was_writable, :if_unsaved => :forget)
652
+ end
653
+ if book.was_open
654
+ book.visible = was_visible
655
+ book.CheckCompatibility = was_check_compatibility
628
656
  book.excel.calculation = was_calculation
629
- book.CheckCompatibility = was_check_compatibility
630
- # book.visible = was_visible # not necessary
631
657
  end
632
- book.Saved = (was_saved || !was_open)
633
- book.close unless was_open || opts[:keep_open]
658
+ book.Saved = (was_saved || !book.was_open)
659
+ book.close unless book.was_open || opts[:keep_open]
634
660
  end
635
661
  end
636
662
  end
637
663
 
664
+
638
665
  # reopens a closed workbook
639
666
  # @options options
640
667
  def reopen(options = { })