robust_excel_ole 1.31 → 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.
- checksums.yaml +4 -4
- data/Changelog +20 -1
- data/README.rdoc +118 -18
- data/___dummy_workbook.xls +0 -0
- data/benchmarking/creek_example.rb +1 -1
- data/benchmarking/roo_example.rb +1 -1
- data/benchmarking/simple_xlsx_reader_example.rb +1 -1
- data/benchmarking/spreadsheet_example.rb +1 -1
- data/docs/README_excel.rdoc +16 -24
- data/docs/README_listobjects.rdoc +176 -0
- data/docs/README_open.rdoc +12 -12
- data/docs/README_ranges.rdoc +72 -55
- data/docs/README_save_close.rdoc +3 -3
- data/docs/README_sheet.rdoc +18 -13
- data/examples/example_ruby_library.rb +2 -2
- data/examples/introductory_examples/example_range.rb +2 -2
- data/examples/modifying_sheets/example_access_sheets_and_cells.rb +6 -6
- data/examples/modifying_sheets/example_add_names.rb +1 -1
- data/examples/modifying_sheets/example_concating.rb +1 -1
- data/examples/modifying_sheets/example_copying.rb +2 -2
- data/examples/modifying_sheets/example_listobjects.rb +86 -0
- data/examples/modifying_sheets/example_naming.rb +1 -1
- data/examples/modifying_sheets/example_ranges.rb +1 -1
- data/examples/open_save_close/example_control_to_excel.rb +1 -1
- data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
- data/examples/open_save_close/example_if_obstructed_save.rb +3 -3
- data/examples/open_save_close/example_if_unsaved_accept.rb +1 -1
- data/examples/open_save_close/example_if_unsaved_forget.rb +3 -3
- data/examples/open_save_close/example_if_unsaved_forget_more.rb +4 -4
- data/examples/open_save_close/example_read_only.rb +1 -1
- data/examples/open_save_close/example_simple.rb +1 -1
- data/examples/open_save_close/example_unobtrusively.rb +3 -3
- data/lib/robust_excel_ole/address_tool.rb +54 -44
- data/lib/robust_excel_ole/base.rb +4 -6
- data/lib/robust_excel_ole/bookstore.rb +2 -16
- data/lib/robust_excel_ole/cell.rb +16 -21
- data/lib/robust_excel_ole/excel.rb +131 -186
- data/lib/robust_excel_ole/general.rb +82 -55
- data/lib/robust_excel_ole/list_object.rb +182 -109
- data/lib/robust_excel_ole/list_row.rb +65 -38
- data/lib/robust_excel_ole/range.rb +125 -93
- data/lib/robust_excel_ole/range_owners.rb +52 -66
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +168 -176
- data/lib/robust_excel_ole/worksheet.rb +177 -141
- data/robust_excel_ole.gemspec +4 -3
- data/spec/bookstore_spec.rb +2 -3
- data/spec/cell_spec.rb +9 -9
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/excel_spec.rb +132 -85
- data/spec/general_spec.rb +47 -15
- data/spec/list_object_spec.rb +258 -145
- data/spec/list_row_spec.rb +218 -0
- data/spec/range_spec.rb +76 -29
- data/spec/spec_helper.rb +15 -1
- data/spec/workbook_spec.rb +75 -34
- data/spec/workbook_specs/workbook_all_spec.rb +2 -1
- data/spec/workbook_specs/workbook_misc_spec.rb +20 -13
- data/spec/workbook_specs/workbook_open_spec.rb +47 -45
- data/spec/workbook_specs/workbook_save_spec.rb +21 -22
- data/spec/workbook_specs/workbook_sheet_spec.rb +3 -3
- data/spec/workbook_specs/workbook_unobtr_spec.rb +303 -303
- data/spec/worksheet_spec.rb +522 -318
- metadata +37 -2
| @@ -16,71 +16,80 @@ module RobustExcelOle | |
| 16 16 | 
             
                #   integer_ranges-fromat: e.g. [3,1], [3,"A"], [3..5,1..2], [3..5, "A".."B"], 
         | 
| 17 17 | 
             
                #                               [3..4, nil], [nil, 2..4], [2,nil], [nil,4]
         | 
| 18 18 | 
             
                #   a1-format: e.g. "A3", "A3:B5", "A:B", "3:5", "A", "3"
         | 
| 19 | 
            -
             | 
| 20 19 | 
             
                def as_r1c1(address)
         | 
| 21 | 
            -
                  transform_address(address | 
| 20 | 
            +
                  transform_address(address, :r1c1)
         | 
| 22 21 | 
             
                end
         | 
| 23 22 |  | 
| 24 23 | 
             
                def as_a1(address)
         | 
| 25 | 
            -
                  transform_address(address | 
| 24 | 
            +
                  transform_address(address, :a1)
         | 
| 26 25 | 
             
                end
         | 
| 27 26 |  | 
| 28 27 | 
             
                # valid address formats: e.g. [3,1], [3,"A"], [3..5,1..2], [3..5, "A".."B"], 
         | 
| 29 28 | 
             
                #                             [3..4, nil], [nil, 2..4], [2,nil], [nil,4]
         | 
| 30 29 | 
             
                def as_integer_ranges(address)
         | 
| 31 | 
            -
                  transform_address(address | 
| 30 | 
            +
                  transform_address(address, :int_range)
         | 
| 32 31 | 
             
                end
         | 
| 33 | 
            -
             | 
| 32 | 
            +
               
         | 
| 34 33 | 
             
              private
         | 
| 35 34 |  | 
| 36 35 | 
             
                def transform_address(address, format)
         | 
| 37 36 | 
             
                  address = address.is_a?(Array) ? address : [address]
         | 
| 38 37 | 
             
                  raise AddressInvalid, "address #{address.inspect} has more than two components" if address.size > 2
         | 
| 39 | 
            -
                  begin
         | 
| 40 | 
            -
                     | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
                      is_r1c1 = comp1 =~ r1c1_expr && (comp2.nil? || comp2 =~ r1c1_expr) && (not is_a1) 
         | 
| 46 | 
            -
                      raise AddressInvalid, "address #{address.inspect} not in A1- or r1c1-format" unless (is_a1 || is_r1c1)
         | 
| 47 | 
            -
                      return address[0].gsub('[','(').gsub(']',')') if (is_a1 && format==:a1) || (is_r1c1 && format==:r1c1)         
         | 
| 48 | 
            -
                      given_format = (is_a1) ? :a1 : :r1c1
         | 
| 49 | 
            -
                      row_comp1, col_comp1 = analyze(comp1,given_format)
         | 
| 50 | 
            -
                      row_comp2, col_comp2 = analyze(comp2,given_format) unless comp2.nil?
         | 
| 51 | 
            -
                      address_comp1 = comp2 && (not row_comp1.nil?) ? (row_comp1 .. row_comp2) : row_comp1
         | 
| 52 | 
            -
                      address_comp2 = comp2 && (not col_comp1.nil?) ? (col_comp1 .. col_comp2) : col_comp1          
         | 
| 53 | 
            -
                    else
         | 
| 54 | 
            -
                      address_comp1, address_comp2 = address      
         | 
| 55 | 
            -
                    end
         | 
| 56 | 
            -
                    address_comp1 = address_comp1..address_comp1 if (address_comp1.is_a?(Integer) || address_comp1.is_a?(String) || address_comp1.is_a?(Array))
         | 
| 57 | 
            -
                    address_comp2 = address_comp2..address_comp2 if (address_comp2.is_a?(Integer) || address_comp2.is_a?(String) || address_comp2.is_a?(Array)) 
         | 
| 58 | 
            -
                    #raise unless address_comp1.nil? || address_comp1.begin.to_i!=0 || address_comp1.begin.empty?
         | 
| 59 | 
            -
                    rows = unless address_comp1.nil? || address_comp1.begin == 0 # || address_comp1.begin.to_i==0          
         | 
| 60 | 
            -
                      address_comp1.begin..address_comp1.end
         | 
| 61 | 
            -
                    end
         | 
| 62 | 
            -
                    columns = unless address_comp2.nil?
         | 
| 63 | 
            -
                      if address_comp2.begin.is_a?(String) #address_comp2.begin.to_i == 0
         | 
| 64 | 
            -
                        col_range = str2num(address_comp2.begin)..str2num(address_comp2.end)
         | 
| 65 | 
            -
                        col_range==(0..0) ? nil : col_range
         | 
| 66 | 
            -
                      else
         | 
| 67 | 
            -
                        address_comp2.begin..address_comp2.end
         | 
| 68 | 
            -
                      end          
         | 
| 69 | 
            -
                    end
         | 
| 70 | 
            -
                  rescue
         | 
| 71 | 
            -
                    raise AddressInvalid, "address (#{address.inspect}) format not correct"
         | 
| 38 | 
            +
                  rows, columns = begin
         | 
| 39 | 
            +
                    rows_and_columns(address, format)
         | 
| 40 | 
            +
                  rescue AddressInvalid
         | 
| 41 | 
            +
                    raise
         | 
| 42 | 
            +
                  rescue AddressAlreadyInRightFormat
         | 
| 43 | 
            +
                    return address[0].gsub('[','(').gsub(']',')') 
         | 
| 72 44 | 
             
                  end
         | 
| 73 | 
            -
                  if format | 
| 45 | 
            +
                  if format == :int_range
         | 
| 46 | 
            +
                    [rows,columns]
         | 
| 47 | 
            +
                  elsif format == :r1c1
         | 
| 74 48 | 
             
                    r1c1_string(@row_letter,rows,:min) + r1c1_string(@col_letter,columns,:min) + ":" + 
         | 
| 75 49 | 
             
                    r1c1_string(@row_letter,rows,:max) + r1c1_string(@col_letter,columns,:max)
         | 
| 76 | 
            -
                  elsif format==:int_range
         | 
| 77 | 
            -
                    [rows,columns]
         | 
| 78 50 | 
             
                  else
         | 
| 79 51 | 
             
                    raise NotImplementedREOError, "not implemented"
         | 
| 80 52 | 
             
                  end
         | 
| 81 | 
            -
                end | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def rows_and_columns(address, format)
         | 
| 56 | 
            +
                  if address.size != 1
         | 
| 57 | 
            +
                    address_comp1, address_comp2 = address      
         | 
| 58 | 
            +
                  else
         | 
| 59 | 
            +
                    comp1, comp2 = address[0].split(':')
         | 
| 60 | 
            +
                    a1_expr = /^(([A-Z]+[0-9]+)|([A-Z]+$)|([0-9]+))$/
         | 
| 61 | 
            +
                    is_a1 = comp1 =~ a1_expr && (comp2.nil? || comp2 =~ a1_expr)
         | 
| 62 | 
            +
                    r1c1_expr = /^(([A-Z]\[?-?[0-9]+\]?[A-Z]\[?-?[0-9]+\]?)|([A-Z]\[?-?[0-9]+\]?)|([A-Z]\[?-?[0-9]+\]?))$/
         | 
| 63 | 
            +
                    is_r1c1 = comp1 =~ r1c1_expr && (comp2.nil? || comp2 =~ r1c1_expr) && (not is_a1) 
         | 
| 64 | 
            +
                    raise AddressInvalid, "address #{address.inspect} not in A1- or r1c1-format" unless (is_a1 || is_r1c1)
         | 
| 65 | 
            +
                    raise AddressAlreadyInRightFormat if (is_a1 && format==:a1) || (is_r1c1 && format==:r1c1)   
         | 
| 66 | 
            +
                    given_format = (is_a1) ? :a1 : :r1c1
         | 
| 67 | 
            +
                    row_comp1, col_comp1 = analyze(comp1,given_format)
         | 
| 68 | 
            +
                    row_comp2, col_comp2 = analyze(comp2,given_format) unless comp2.nil?
         | 
| 69 | 
            +
                    address_comp1 = comp2 && (not row_comp1.nil?) ? (row_comp1 .. row_comp2) : row_comp1
         | 
| 70 | 
            +
                    address_comp2 = comp2 && (not col_comp1.nil?) ? (col_comp1 .. col_comp2) : col_comp1          
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                  address_comp1 = address_comp1..address_comp1 if (address_comp1.is_a?(Integer) || address_comp1.is_a?(String) || address_comp1.is_a?(Array))
         | 
| 73 | 
            +
                  address_comp2 = address_comp2..address_comp2 if (address_comp2.is_a?(Integer) || address_comp2.is_a?(String) || address_comp2.is_a?(Array))
         | 
| 74 | 
            +
                  raise AddressInvalid, "address contains row 0" if !address_comp1.nil? && address_comp1.begin == 0
         | 
| 75 | 
            +
                  rows = address_comp1.begin..address_comp1.end unless address_comp1.nil? || address_comp1.begin == 0
         | 
| 76 | 
            +
                  columns = unless address_comp2.nil?
         | 
| 77 | 
            +
                    if address_comp2.begin.is_a?(String) #address_comp2.begin.to_i == 0
         | 
| 78 | 
            +
                      col_range = str2num(address_comp2.begin)..str2num(address_comp2.end)
         | 
| 79 | 
            +
                      col_range==(0..0) ? nil : col_range
         | 
| 80 | 
            +
                    else
         | 
| 81 | 
            +
                      address_comp2.begin..address_comp2.end
         | 
| 82 | 
            +
                    end          
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                  [rows, columns]
         | 
| 85 | 
            +
                rescue
         | 
| 86 | 
            +
                  raise AddressInvalid, "address (#{address.inspect}) format not correct"
         | 
| 87 | 
            +
                end
         | 
| 82 88 |  | 
| 83 | 
            -
                 | 
| 89 | 
            +
                class AddressAlreadyInRightFormat < Exception
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
                
         | 
| 92 | 
            +
                def r1c1_string(letter, int_range,type)
         | 
| 84 93 | 
             
                  return "" if int_range.nil? || int_range.begin.nil?
         | 
| 85 94 | 
             
                  parameter = type == :min ? int_range.begin : int_range.end
         | 
| 86 95 | 
             
                  is_relative = parameter.is_a?(Array)
         | 
| @@ -88,7 +97,7 @@ module RobustExcelOle | |
| 88 97 | 
             
                  letter + (is_relative ? "(" : "") + parameter.to_s + (is_relative ? ")" : "")       
         | 
| 89 98 | 
             
                end 
         | 
| 90 99 |  | 
| 91 | 
            -
                def analyze(comp,format)
         | 
| 100 | 
            +
                def analyze(comp, format)
         | 
| 92 101 | 
             
                  row_comp, col_comp = if format==:a1 
         | 
| 93 102 | 
             
                    [comp.gsub(/[A-Z]/,''), comp.gsub(/[0-9]/,'')]
         | 
| 94 103 | 
             
                  else
         | 
| @@ -108,6 +117,7 @@ module RobustExcelOle | |
| 108 117 |  | 
| 109 118 | 
             
              end
         | 
| 110 119 |  | 
| 120 | 
            +
             | 
| 111 121 | 
             
              # @private
         | 
| 112 122 | 
             
              class AddressInvalid < REOError                  
         | 
| 113 123 | 
             
              end
         | 
| @@ -69,7 +69,7 @@ module RobustExcelOle | |
| 69 69 |  | 
| 70 70 | 
             
                # @private
         | 
| 71 71 | 
             
                def own_methods
         | 
| 72 | 
            -
                  ( | 
| 72 | 
            +
                  (methods - Object.methods).sort
         | 
| 73 73 | 
             
                end
         | 
| 74 74 |  | 
| 75 75 | 
             
                # @private
         | 
| @@ -99,12 +99,10 @@ module RobustExcelOle | |
| 99 99 | 
             
                def self.puts_hash(hash)
         | 
| 100 100 | 
             
                  hash.each do |e|
         | 
| 101 101 | 
             
                    if e[1].is_a?(Hash)
         | 
| 102 | 
            -
                      puts "#{e[0]}  | 
| 103 | 
            -
                      e[1].each  | 
| 104 | 
            -
                        puts "  #{f[0]} => #{f[1]}"
         | 
| 105 | 
            -
                      end
         | 
| 102 | 
            +
                      puts "#{e[0]}: "
         | 
| 103 | 
            +
                      e[1].each{ |f| puts "  #{f[0]}: #{f[1]}" }
         | 
| 106 104 | 
             
                    else
         | 
| 107 | 
            -
                      puts "#{e[0]}  | 
| 105 | 
            +
                      puts "#{e[0]}: #{e[1]}"
         | 
| 108 106 | 
             
                    end
         | 
| 109 107 | 
             
                  end
         | 
| 110 108 | 
             
                end
         | 
| @@ -29,19 +29,17 @@ module RobustExcelOle | |
| 29 29 | 
             
                #                             otherwise returns the workbook according to the preference order mentioned above
         | 
| 30 30 | 
             
                #          :prefer_excel      returns the workbook in the given Excel instance, if it exists,
         | 
| 31 31 | 
             
                #                             otherwise proceeds according to prefer_writable
         | 
| 32 | 
            -
                def fetch(filename, options = { : | 
| 32 | 
            +
                def fetch(filename, options = { prefer_writable: true })
         | 
| 33 33 | 
             
                  return nil unless filename
         | 
| 34 34 | 
             
                  filename = General.absolute_path(filename)
         | 
| 35 35 | 
             
                  filename_key = General.canonize(filename).downcase
         | 
| 36 36 | 
             
                  weakref_books = @filename2books[filename_key]
         | 
| 37 37 | 
             
                  return nil if weakref_books.nil? || weakref_books.empty?
         | 
| 38 | 
            -
             | 
| 39 38 | 
             
                  result = open_book = closed_book = nil
         | 
| 40 39 | 
             
                  weakref_books = weakref_books.map { |wr_book| wr_book if wr_book.weakref_alive? }.compact
         | 
| 41 40 | 
             
                  @filename2books[filename_key] = weakref_books
         | 
| 42 41 | 
             
                  weakref_books.each do |wr_book|
         | 
| 43 42 | 
             
                    if !wr_book.weakref_alive?
         | 
| 44 | 
            -
                      # trace "warn: this should never happen"
         | 
| 45 43 | 
             
                      begin
         | 
| 46 44 | 
             
                        @filename2books[filename_key].delete(wr_book)
         | 
| 47 45 | 
             
                      rescue
         | 
| @@ -50,7 +48,6 @@ module RobustExcelOle | |
| 50 48 | 
             
                    else
         | 
| 51 49 | 
             
                      book = wr_book.__getobj__
         | 
| 52 50 | 
             
                      next if book.excel == try_hidden_excel
         | 
| 53 | 
            -
             | 
| 54 51 | 
             
                      if options[:prefer_excel] && book.excel == options[:prefer_excel]
         | 
| 55 52 | 
             
                        result = book
         | 
| 56 53 | 
             
                        break
         | 
| @@ -64,7 +61,6 @@ module RobustExcelOle | |
| 64 61 | 
             
                    end
         | 
| 65 62 | 
             
                  end
         | 
| 66 63 | 
             
                  result ||= (open_book || closed_book)
         | 
| 67 | 
            -
                  result
         | 
| 68 64 | 
             
                end
         | 
| 69 65 |  | 
| 70 66 | 
             
                # stores a workbook
         | 
| @@ -90,17 +86,7 @@ module RobustExcelOle | |
| 90 86 |  | 
| 91 87 | 
             
                # returns all stored books
         | 
| 92 88 | 
             
                def books
         | 
| 93 | 
            -
                   | 
| 94 | 
            -
                  if @filename2books
         | 
| 95 | 
            -
                    @filename2books.each do |_filename,books|
         | 
| 96 | 
            -
                      next if books.empty?
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                      books.each do |wr_book|
         | 
| 99 | 
            -
                        result << wr_book.__getobj__ if wr_book.weakref_alive?
         | 
| 100 | 
            -
                      end
         | 
| 101 | 
            -
                    end
         | 
| 102 | 
            -
                  end
         | 
| 103 | 
            -
                  result
         | 
| 89 | 
            +
                  @filename2books.map{ |_fn,books| books.map{ |wr_bk| wr_bk.__getobj__ if wr_bk.weakref_alive?}.compact}.flatten
         | 
| 104 90 | 
             
                end
         | 
| 105 91 |  | 
| 106 92 | 
             
              private
         | 
| @@ -7,7 +7,7 @@ module RobustExcelOle | |
| 7 7 | 
             
              class Cell < Range
         | 
| 8 8 | 
             
                #attr_reader :ole_cell    
         | 
| 9 9 |  | 
| 10 | 
            -
                def initialize(win32_cell, worksheet) | 
| 10 | 
            +
                def initialize(win32_cell, worksheet)
         | 
| 11 11 | 
             
                  super
         | 
| 12 12 | 
             
                  ole_cell
         | 
| 13 13 | 
             
                end
         | 
| @@ -20,8 +20,8 @@ module RobustExcelOle | |
| 20 20 | 
             
                  self.Value = value
         | 
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 | 
            -
                 | 
| 24 | 
            -
                 | 
| 23 | 
            +
                alias v value
         | 
| 24 | 
            +
                alias v= value=
         | 
| 25 25 |  | 
| 26 26 | 
             
                # @private
         | 
| 27 27 | 
             
                def ole_cell
         | 
| @@ -30,36 +30,31 @@ module RobustExcelOle | |
| 30 30 |  | 
| 31 31 | 
             
                # @private
         | 
| 32 32 | 
             
                def to_s    
         | 
| 33 | 
            -
                  "#<Cell: | 
| 33 | 
            +
                  "#<Cell: (#{@ole_range.Row},#{@ole_range.Column})>"
         | 
| 34 34 | 
             
                end
         | 
| 35 35 |  | 
| 36 36 | 
             
                # @private
         | 
| 37 37 | 
             
                def inspect 
         | 
| 38 | 
            -
                   | 
| 38 | 
            +
                  to_s[0..-2] + " #{@ole_range.Parent.Name}>" 
         | 
| 39 39 | 
             
                end
         | 
| 40 40 |  | 
| 41 41 | 
             
              private
         | 
| 42 42 |  | 
| 43 43 | 
             
                # @private
         | 
| 44 44 | 
             
                def method_missing(name, *args) 
         | 
| 45 | 
            -
                   | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
                       | 
| 51 | 
            -
                        raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
         | 
| 52 | 
            -
                      end
         | 
| 53 | 
            -
                    else
         | 
| 54 | 
            -
                      begin
         | 
| 55 | 
            -
                        #@ole_cell.send(name, *args)
         | 
| 56 | 
            -
                        @ole_range.send(name, *args)
         | 
| 57 | 
            -
                      rescue NoMethodError 
         | 
| 58 | 
            -
                        raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
         | 
| 59 | 
            -
                      end
         | 
| 45 | 
            +
                  super unless name.to_s[0,1] =~ /[A-Z]/
         | 
| 46 | 
            +
                  if ::ERRORMESSAGE_JRUBY_BUG
         | 
| 47 | 
            +
                    begin
         | 
| 48 | 
            +
                      @ole_range.send(name, *args)
         | 
| 49 | 
            +
                    rescue Java::OrgRacobCom::ComFailException 
         | 
| 50 | 
            +
                      raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
         | 
| 60 51 | 
             
                    end
         | 
| 61 52 | 
             
                  else
         | 
| 62 | 
            -
                     | 
| 53 | 
            +
                    begin
         | 
| 54 | 
            +
                      @ole_range.send(name, *args)
         | 
| 55 | 
            +
                    rescue NoMethodError 
         | 
| 56 | 
            +
                      raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
         | 
| 57 | 
            +
                    end
         | 
| 63 58 | 
             
                  end
         | 
| 64 59 | 
             
                end
         | 
| 65 60 | 
             
              end
         | 
| @@ -16,6 +16,8 @@ module RobustExcelOle | |
| 16 16 |  | 
| 17 17 | 
             
              class Excel < VbaObjects
         | 
| 18 18 |  | 
| 19 | 
            +
                include Enumerable
         | 
| 20 | 
            +
             | 
| 19 21 | 
             
                attr_reader :ole_excel
         | 
| 20 22 | 
             
                attr_reader :properties
         | 
| 21 23 | 
             
                attr_reader :address_tool
         | 
| @@ -34,7 +36,7 @@ module RobustExcelOle | |
| 34 36 | 
             
                # @option options [Boolean] :screenupdating
         | 
| 35 37 | 
             
                # @return [Excel] a new Excel instance
         | 
| 36 38 | 
             
                def self.create(options = {})
         | 
| 37 | 
            -
                  new(options.merge(: | 
| 39 | 
            +
                  new(options.merge(reuse: false))
         | 
| 38 40 | 
             
                end
         | 
| 39 41 |  | 
| 40 42 | 
             
                # connects to the current (first opened) Excel instance, if such a running Excel instance exists
         | 
| @@ -45,7 +47,7 @@ module RobustExcelOle | |
| 45 47 | 
             
                # @option options [Boolean] :screenupdating
         | 
| 46 48 | 
             
                # @return [Excel] an Excel instance
         | 
| 47 49 | 
             
                def self.current(options = {})
         | 
| 48 | 
            -
                  new(options.merge(: | 
| 50 | 
            +
                  new(options.merge(reuse: true))
         | 
| 49 51 | 
             
                end
         | 
| 50 52 |  | 
| 51 53 | 
             
                # returns an Excel instance
         | 
| @@ -70,10 +72,11 @@ module RobustExcelOle | |
| 70 72 | 
             
                    options = win32ole_excel
         | 
| 71 73 | 
             
                    win32ole_excel = nil
         | 
| 72 74 | 
             
                  end
         | 
| 73 | 
            -
                   | 
| 74 | 
            -
                   | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 75 | 
            +
                  options = { reuse: true }.merge(options)
         | 
| 76 | 
            +
                  ole_xl = if !win32ole_excel.nil? 
         | 
| 77 | 
            +
                    win32ole_excel
         | 
| 78 | 
            +
                  elsif options[:reuse] == true
         | 
| 79 | 
            +
                    current_ole_excel
         | 
| 77 80 | 
             
                  end
         | 
| 78 81 | 
             
                  connected = (not ole_xl.nil?) && win32ole_excel.nil?
         | 
| 79 82 | 
             
                  ole_xl ||= WIN32OLE.new('Excel.Application')
         | 
| @@ -87,14 +90,9 @@ module RobustExcelOle | |
| 87 90 | 
             
                    WIN32OLE.const_load(ole_xl, RobustExcelOle) unless RobustExcelOle.const_defined?(:CONSTANTS)
         | 
| 88 91 | 
             
                    @@hwnd2excel[hwnd] = WeakRef.new(result)
         | 
| 89 92 | 
             
                  end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                   | 
| 92 | 
            -
                     | 
| 93 | 
            -
                    unless reused || connected
         | 
| 94 | 
            -
                      options = { :displayalerts => :if_visible, :visible => false, :screenupdating => true }.merge(options)
         | 
| 95 | 
            -
                    end
         | 
| 96 | 
            -
                    result.set_options(options)        
         | 
| 97 | 
            -
                  end
         | 
| 93 | 
            +
                  reused = options[:reuse] && stored && stored.alive? 
         | 
| 94 | 
            +
                  options = { displayalerts: :if_visible, visible: false, screenupdating: true }.merge(options) unless reused || connected
         | 
| 95 | 
            +
                  result.set_options(options)        
         | 
| 98 96 | 
             
                  result
         | 
| 99 97 | 
             
                end    
         | 
| 100 98 |  | 
| @@ -111,15 +109,12 @@ module RobustExcelOle | |
| 111 109 | 
             
                # @return [Excel] an Excel instance
         | 
| 112 110 | 
             
                def recreate(opts = {})
         | 
| 113 111 | 
             
                  unless alive?
         | 
| 114 | 
            -
                    opts = {: | 
| 115 | 
            -
                           {: | 
| 112 | 
            +
                    opts = {visible: false, displayalerts: :if_visible}.merge(
         | 
| 113 | 
            +
                           {visible: @properties[:visible], displayalerts: @properties[:displayalerts]}).merge(opts)        
         | 
| 116 114 | 
             
                    @ole_excel = WIN32OLE.new('Excel.Application')
         | 
| 117 115 | 
             
                    set_options(opts)
         | 
| 118 116 | 
             
                    if opts[:reopen_workbooks]
         | 
| 119 | 
            -
                      books  | 
| 120 | 
            -
                      books.each do |book|
         | 
| 121 | 
            -
                        book.reopen if !book.alive? && book.excel.alive? && book.excel == self
         | 
| 122 | 
            -
                      end
         | 
| 117 | 
            +
                      workbook_class.books.each{ |book| book.reopen if !book.alive? && book.excel.alive? && book.excel == self }
         | 
| 123 118 | 
             
                    end
         | 
| 124 119 | 
             
                  end
         | 
| 125 120 | 
             
                  self
         | 
| @@ -147,34 +142,29 @@ module RobustExcelOle | |
| 147 142 | 
             
                end
         | 
| 148 143 |  | 
| 149 144 | 
             
                def ole_workbooks
         | 
| 150 | 
            -
                   | 
| 151 | 
            -
             | 
| 152 | 
            -
                   | 
| 153 | 
            -
                     | 
| 154 | 
            -
             | 
| 155 | 
            -
                     | 
| 156 | 
            -
                      raise ExcelREOError, 'workbooks could not be determined'
         | 
| 157 | 
            -
                    end
         | 
| 145 | 
            +
                  @ole_excel.Workbooks
         | 
| 146 | 
            +
                rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
         | 
| 147 | 
            +
                  if msg.message =~ /failed to get Dispatch Interface/
         | 
| 148 | 
            +
                    raise ExcelDamaged, "Excel instance not alive or damaged"
         | 
| 149 | 
            +
                  else
         | 
| 150 | 
            +
                    raise ExcelREOError, "workbooks could not be determined\n#{$!.message}"
         | 
| 158 151 | 
             
                  end
         | 
| 159 152 | 
             
                end
         | 
| 160 153 |  | 
| 161 154 | 
             
              public
         | 
| 162 155 |  | 
| 163 156 | 
             
                # @private
         | 
| 157 | 
            +
                # returns unsaved workbooks (win32ole objects)
         | 
| 164 158 | 
             
                def self.contains_unsaved_workbooks?
         | 
| 165 159 | 
             
                  !Excel.current.unsaved_workbooks.empty?
         | 
| 166 160 | 
             
                end
         | 
| 167 161 |  | 
| 168 | 
            -
                # returns unsaved workbooks (win32ole objects)
         | 
| 169 162 | 
             
                # @private
         | 
| 163 | 
            +
                # returns unsaved workbooks (win32ole objects)   
         | 
| 170 164 | 
             
                def unsaved_workbooks
         | 
| 171 | 
            -
                   | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
                  rescue RuntimeError => msg
         | 
| 175 | 
            -
                    raise ExcelDamaged, 'Excel instance not alive or damaged' if msg.message =~ /failed to get Dispatch Interface/
         | 
| 176 | 
            -
                  end
         | 
| 177 | 
            -
                  unsaved_workbooks
         | 
| 165 | 
            +
                  @ole_excel.Workbooks.reject { |w| w.Saved || w.ReadOnly }
         | 
| 166 | 
            +
                rescue RuntimeError => msg
         | 
| 167 | 
            +
                  raise ExcelDamaged, "Excel instance not alive or damaged" if msg.message =~ /failed to get Dispatch Interface/
         | 
| 178 168 | 
             
                end
         | 
| 179 169 |  | 
| 180 170 | 
             
                # closes workbooks
         | 
| @@ -185,7 +175,7 @@ module RobustExcelOle | |
| 185 175 | 
             
                #                      :save            -> saves the workbooks before closing
         | 
| 186 176 | 
             
                #                      :alert           -> let Excel do it
         | 
| 187 177 | 
             
                # @private
         | 
| 188 | 
            -
                def close_workbooks(options = { : | 
| 178 | 
            +
                def close_workbooks(options = { if_unsaved: :raise })
         | 
| 189 179 | 
             
                  return unless alive?
         | 
| 190 180 |  | 
| 191 181 | 
             
                  weak_wkbks = @ole_excel.Workbooks
         | 
| @@ -208,11 +198,12 @@ module RobustExcelOle | |
| 208 198 | 
             
                      "\nHint: Valid values are :raise, :forget, :save and :alert"
         | 
| 209 199 | 
             
                    end
         | 
| 210 200 | 
             
                  end
         | 
| 201 | 
            +
             | 
| 211 202 | 
             
                  begin
         | 
| 212 203 | 
             
                    @ole_excel.Workbooks.Close
         | 
| 213 204 | 
             
                  rescue
         | 
| 214 205 | 
             
                    if $!.message =~ /kann nicht zugeordnet werden/ or $!.message =~ /800A03EC/
         | 
| 215 | 
            -
                      raise ExcelREOError,  | 
| 206 | 
            +
                      raise ExcelREOError, "user canceled or runtime error"
         | 
| 216 207 | 
             
                    else
         | 
| 217 208 | 
             
                      raise UnexpectedREOError, "unknown WIN32OLERuntimeError: #{msg.message}"
         | 
| 218 209 | 
             
                    end
         | 
| @@ -235,7 +226,7 @@ module RobustExcelOle | |
| 235 226 | 
             
                #                      :forget          -> closes the excel instance without saving the workbooks
         | 
| 236 227 | 
             
                #                      :alert           -> give control to Excel
         | 
| 237 228 | 
             
                # @option options [Proc] block
         | 
| 238 | 
            -
                def self.close_all(options = { : | 
| 229 | 
            +
                def self.close_all(options = { if_unsaved: :raise }, &blk)
         | 
| 239 230 | 
             
                  options[:if_unsaved] = blk if blk
         | 
| 240 231 | 
             
                  finished_number = error_number = overall_number = 0
         | 
| 241 232 | 
             
                  first_error = nil
         | 
| @@ -243,10 +234,9 @@ module RobustExcelOle | |
| 243 234 | 
             
                    if excel
         | 
| 244 235 | 
             
                      begin
         | 
| 245 236 | 
             
                        overall_number += 1
         | 
| 246 | 
            -
                        finished_number += excel.close(: | 
| 237 | 
            +
                        finished_number += excel.close(if_unsaved: options[:if_unsaved])
         | 
| 247 238 | 
             
                      rescue
         | 
| 248 239 | 
             
                        first_error = $!
         | 
| 249 | 
            -
                        #trace "error when finishing #{$!}"
         | 
| 250 240 | 
             
                        error_number += 1
         | 
| 251 241 | 
             
                      end
         | 
| 252 242 | 
             
                    end
         | 
| @@ -269,19 +259,13 @@ module RobustExcelOle | |
| 269 259 | 
             
                  old_error_number = error_number
         | 
| 270 260 | 
             
                  9.times do |_index|
         | 
| 271 261 | 
             
                    sleep 0.1
         | 
| 272 | 
            -
                    excel =  | 
| 273 | 
            -
                              new(WIN32OLE.connect('Excel.Application'))
         | 
| 274 | 
            -
                            rescue
         | 
| 275 | 
            -
                              nil
         | 
| 276 | 
            -
                            end
         | 
| 262 | 
            +
                    excel = new(WIN32OLE.connect('Excel.Application')) rescue nil
         | 
| 277 263 | 
             
                    finishing_action.call(excel) if excel
         | 
| 278 264 | 
             
                    free_all_ole_objects unless (error_number > 0) && (options[:if_unsaved] == :raise)
         | 
| 279 265 | 
             
                    break unless excel
         | 
| 280 266 | 
             
                    break if error_number > old_error_number # + 3
         | 
| 281 267 | 
             
                  end
         | 
| 282 | 
            -
             | 
| 283 268 | 
             
                  raise first_error if ((options[:if_unsaved] == :raise) && first_error) || (first_error.class == OptionInvalid)
         | 
| 284 | 
            -
             | 
| 285 269 | 
             
                  [finished_number, error_number]
         | 
| 286 270 | 
             
                end
         | 
| 287 271 |  | 
| @@ -294,19 +278,13 @@ module RobustExcelOle | |
| 294 278 | 
             
                #                      :save            -> saves the workbooks before closing
         | 
| 295 279 | 
             
                #                      :forget          -> closes the Excel instance without saving the workbooks
         | 
| 296 280 | 
             
                #                      :alert           -> Excel takes over    
         | 
| 297 | 
            -
                def close(options = { : | 
| 281 | 
            +
                def close(options = { if_unsaved: :raise })
         | 
| 298 282 | 
             
                  finishing_living_excel = alive?
         | 
| 299 283 | 
             
                  if finishing_living_excel
         | 
| 300 | 
            -
                    hwnd =  | 
| 301 | 
            -
             | 
| 302 | 
            -
                            rescue
         | 
| 303 | 
            -
                              nil
         | 
| 304 | 
            -
                            end)
         | 
| 305 | 
            -
                    close_workbooks(:if_unsaved => options[:if_unsaved])
         | 
| 284 | 
            +
                    hwnd = @ole_excel.Hwnd rescue nil
         | 
| 285 | 
            +
                    close_workbooks(if_unsaved: options[:if_unsaved])
         | 
| 306 286 | 
             
                    @ole_excel.Quit
         | 
| 307 | 
            -
                    if false && defined?(weak_wkbks) && weak_wkbks.weakref_alive?
         | 
| 308 | 
            -
                      weak_wkbks.ole_free
         | 
| 309 | 
            -
                    end
         | 
| 287 | 
            +
                    weak_wkbks.ole_free if false && defined?(weak_wkbks) && weak_wkbks.weakref_alive?
         | 
| 310 288 | 
             
                    weak_xl = WeakRef.new(@ole_excel)
         | 
| 311 289 | 
             
                  else
         | 
| 312 290 | 
             
                    weak_xl = nil
         | 
| @@ -320,21 +298,10 @@ module RobustExcelOle | |
| 320 298 | 
             
                      pid_puffer = ' ' * 32
         | 
| 321 299 | 
             
                      process_id.call(hwnd, pid_puffer)
         | 
| 322 300 | 
             
                      pid = pid_puffer.unpack('L')[0]
         | 
| 323 | 
            -
                       | 
| 324 | 
            -
                        Process.kill('KILL', pid)
         | 
| 325 | 
            -
                      rescue
         | 
| 326 | 
            -
                        # trace "kill_error: #{$!}"
         | 
| 327 | 
            -
                      end
         | 
| 301 | 
            +
                      Process.kill('KILL', pid) rescue nil
         | 
| 328 302 | 
             
                    end
         | 
| 329 303 | 
             
                    @@hwnd2excel.delete(hwnd)
         | 
| 330 | 
            -
                    if weak_xl.weakref_alive?
         | 
| 331 | 
            -
                      # if WIN32OLE.ole_reference_count(weak_xlapp) > 0
         | 
| 332 | 
            -
                      begin
         | 
| 333 | 
            -
                        weak_xl.ole_free
         | 
| 334 | 
            -
                      rescue
         | 
| 335 | 
            -
                        # trace "weakref_probl_olefree"
         | 
| 336 | 
            -
                      end
         | 
| 337 | 
            -
                    end
         | 
| 304 | 
            +
                    weak_xl.ole_free if weak_xl.weakref_alive?
         | 
| 338 305 | 
             
                  end
         | 
| 339 306 | 
             
                  weak_xl ? 1 : 0
         | 
| 340 307 | 
             
                end
         | 
| @@ -383,15 +350,42 @@ module RobustExcelOle | |
| 383 350 | 
             
                  number
         | 
| 384 351 | 
             
                end
         | 
| 385 352 |  | 
| 386 | 
            -
                def self. | 
| 387 | 
            -
                   | 
| 388 | 
            -
                  processes.select { |p| p.Name == 'EXCEL.EXE' }.size
         | 
| 353 | 
            +
                def self.instance_count
         | 
| 354 | 
            +
                  WIN32OLE.connect('winmgmts:\\\\.').InstancesOf('win32_process').select { |p| p.Name == 'EXCEL.EXE' }.size
         | 
| 389 355 | 
             
                end
         | 
| 390 356 |  | 
| 391 | 
            -
                def self. | 
| 357 | 
            +
                def self.known_instance_count
         | 
| 392 358 | 
             
                  @@hwnd2excel.size
         | 
| 393 359 | 
             
                end
         | 
| 394 360 |  | 
| 361 | 
            +
                # returns a running Excel instance opened with RobustExcelOle
         | 
| 362 | 
            +
                def self.known_running_instance     
         | 
| 363 | 
            +
                  self.known_running_instances.first
         | 
| 364 | 
            +
                end
         | 
| 365 | 
            +
             | 
| 366 | 
            +
                # @return [Enumerator] known running Excel instances
         | 
| 367 | 
            +
                def self.known_running_instances
         | 
| 368 | 
            +
                  pid2excel = {}
         | 
| 369 | 
            +
                  @@hwnd2excel.each do |hwnd,wr_excel|
         | 
| 370 | 
            +
                    next unless wr_excel.weakref_alive?
         | 
| 371 | 
            +
                    excel = wr_excel.__getobj__
         | 
| 372 | 
            +
                    process_id = Win32API.new('user32', 'GetWindowThreadProcessId', %w[I P], 'I')
         | 
| 373 | 
            +
                    pid_puffer = ' ' * 32
         | 
| 374 | 
            +
                    process_id.call(hwnd, pid_puffer)
         | 
| 375 | 
            +
                    pid = pid_puffer.unpack('L')[0]
         | 
| 376 | 
            +
                    pid2excel[pid] = excel
         | 
| 377 | 
            +
                  end
         | 
| 378 | 
            +
                  processes = WIN32OLE.connect('winmgmts:\\\\.').InstancesOf('win32_process')     
         | 
| 379 | 
            +
                  processes.map{ |p| pid2excel[p.ProcessId] if p.Name == 'EXCEL.EXE'}.compact.lazy.each
         | 
| 380 | 
            +
                end
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                class << self
         | 
| 383 | 
            +
                  alias excels_number instance_count                  # :deprecated :#
         | 
| 384 | 
            +
                  alias known_excels_number known_instance_count      # :deprecated :#
         | 
| 385 | 
            +
                  alias known_excel_instance known_running_instance   # :deprecated :#
         | 
| 386 | 
            +
                  alias known_excel_instances known_running_instances # :deprecated :#
         | 
| 387 | 
            +
                end
         | 
| 388 | 
            +
             | 
| 395 389 | 
             
              private
         | 
| 396 390 |  | 
| 397 391 | 
             
                # returns a Win32OLE object that represents a Excel instance to which Excel connects
         | 
| @@ -399,7 +393,7 @@ module RobustExcelOle | |
| 399 393 | 
             
                # if this Excel instance is being closed, then Excel creates a new Excel instance
         | 
| 400 394 | 
             
                def self.current_ole_excel   
         | 
| 401 395 | 
             
                  if ::CONNECT_EXCEL_JRUBY_BUG
         | 
| 402 | 
            -
                    result =  | 
| 396 | 
            +
                    result = known_running_instance
         | 
| 403 397 | 
             
                    if result.nil?
         | 
| 404 398 | 
             
                      if excels_number > 0
         | 
| 405 399 | 
             
                        dummy_ole_workbook = WIN32OLE.connect(General.absolute_path('___dummy_workbook.xls')) rescue nil
         | 
| @@ -428,17 +422,6 @@ module RobustExcelOle | |
| 428 422 | 
             
                  result
         | 
| 429 423 | 
             
                end
         | 
| 430 424 |  | 
| 431 | 
            -
                # returns an Excel instance
         | 
| 432 | 
            -
                def self.known_excel_instance
         | 
| 433 | 
            -
                  @@hwnd2excel.each do |hwnd, wr_excel|
         | 
| 434 | 
            -
                    if wr_excel.weakref_alive?
         | 
| 435 | 
            -
                      excel = wr_excel.__getobj__
         | 
| 436 | 
            -
                      return excel if excel.alive?
         | 
| 437 | 
            -
                    end
         | 
| 438 | 
            -
                  end
         | 
| 439 | 
            -
                  nil
         | 
| 440 | 
            -
                end
         | 
| 441 | 
            -
             | 
| 442 425 | 
             
                def self.hwnd2excel(hwnd)
         | 
| 443 426 | 
             
                  excel_weakref = @@hwnd2excel[hwnd]
         | 
| 444 427 | 
             
                  if excel_weakref
         | 
| @@ -458,35 +441,6 @@ module RobustExcelOle | |
| 458 441 |  | 
| 459 442 | 
             
              public
         | 
| 460 443 |  | 
| 461 | 
            -
                # returns all Excel objects for all Excel instances opened with RobustExcelOle
         | 
| 462 | 
            -
                def self.known_excel_instances
         | 
| 463 | 
            -
                  pid2excel = {}
         | 
| 464 | 
            -
                  @@hwnd2excel.each do |hwnd,wr_excel|
         | 
| 465 | 
            -
                    next unless wr_excel.weakref_alive?
         | 
| 466 | 
            -
             | 
| 467 | 
            -
                    excel = wr_excel.__getobj__
         | 
| 468 | 
            -
                    process_id = Win32API.new('user32', 'GetWindowThreadProcessId', %w[I P], 'I')
         | 
| 469 | 
            -
                    pid_puffer = ' ' * 32
         | 
| 470 | 
            -
                    process_id.call(hwnd, pid_puffer)
         | 
| 471 | 
            -
                    pid = pid_puffer.unpack('L')[0]
         | 
| 472 | 
            -
                    pid2excel[pid] = excel
         | 
| 473 | 
            -
                  end
         | 
| 474 | 
            -
                  processes = WIN32OLE.connect('winmgmts:\\\\.').InstancesOf('win32_process')
         | 
| 475 | 
            -
                  processes.select { |p| Excel.new(pid2excel[p.ProcessId]) if p.Name == 'EXCEL.EXE' && pid2excel.include?(p.ProcessId) }
         | 
| 476 | 
            -
                  result = []
         | 
| 477 | 
            -
                  processes.each do |p|
         | 
| 478 | 
            -
                    next unless p.Name == 'EXCEL.EXE'
         | 
| 479 | 
            -
             | 
| 480 | 
            -
                    if pid2excel.include?(p.ProcessId)
         | 
| 481 | 
            -
                      excel = pid2excel[p.ProcessId]
         | 
| 482 | 
            -
                      result << excel
         | 
| 483 | 
            -
                    end
         | 
| 484 | 
            -
                    # how to connect to an (interactively opened) Excel instance and get a WIN32OLE object?
         | 
| 485 | 
            -
                    # after that, lift it to an Excel object
         | 
| 486 | 
            -
                  end
         | 
| 487 | 
            -
                  result
         | 
| 488 | 
            -
                end
         | 
| 489 | 
            -
             | 
| 490 444 | 
             
                # @private
         | 
| 491 445 | 
             
                def excel
         | 
| 492 446 | 
             
                  self
         | 
| @@ -524,15 +478,11 @@ module RobustExcelOle | |
| 524 478 |  | 
| 525 479 | 
             
                # returns unsaved workbooks in known (not opened by user) Excel instances
         | 
| 526 480 | 
             
                # @private
         | 
| 527 | 
            -
                def self.unsaved_known_workbooks | 
| 528 | 
            -
                   | 
| 529 | 
            -
                  @@hwnd2excel.each do |_hwnd,wr_excel|
         | 
| 530 | 
            -
                    excel = wr_excel.__getobj__ if wr_excel.weakref_alive?
         | 
| 531 | 
            -
                    result << excel.unsaved_workbooks
         | 
| 532 | 
            -
                  end
         | 
| 533 | 
            -
                  result
         | 
| 481 | 
            +
                def self.unsaved_known_workbooks       
         | 
| 482 | 
            +
                  @@hwnd2excel.values.map{ |wk_exl| wk_exl.__getobj__.unsaved_workbooks if wk_exl.weakref_alive? }.compact.flatten
         | 
| 534 483 | 
             
                end
         | 
| 535 484 |  | 
| 485 | 
            +
             | 
| 536 486 | 
             
                # @private
         | 
| 537 487 | 
             
                def print_workbooks
         | 
| 538 488 | 
             
                  self.Workbooks.each { |w| trace "#{w.Name} #{w}" }
         | 
| @@ -540,7 +490,7 @@ module RobustExcelOle | |
| 540 490 |  | 
| 541 491 | 
             
                # @private
         | 
| 542 492 | 
             
                def generate_workbook file_name  # :deprecated: #
         | 
| 543 | 
            -
                  workbook_class.open(file_name, : | 
| 493 | 
            +
                  workbook_class.open(file_name, if_absent: :create, force: {excel: self})
         | 
| 544 494 | 
             
                end
         | 
| 545 495 |  | 
| 546 496 | 
             
                # sets DisplayAlerts in a block
         | 
| @@ -580,37 +530,30 @@ module RobustExcelOle | |
| 580 530 | 
             
                  return if calculation_mode.nil?
         | 
| 581 531 | 
             
                  @properties[:calculation] = calculation_mode
         | 
| 582 532 | 
             
                  calc_mode_changable = @ole_excel.Workbooks.Count > 0 && @ole_excel.Calculation.is_a?(Integer)
         | 
| 583 | 
            -
                   | 
| 584 | 
            -
             | 
| 585 | 
            -
             | 
| 586 | 
            -
             | 
| 587 | 
            -
             | 
| 588 | 
            -
             | 
| 589 | 
            -
             | 
| 590 | 
            -
             | 
| 591 | 
            -
             | 
| 592 | 
            -
             | 
| 593 | 
            -
             | 
| 594 | 
            -
             | 
| 595 | 
            -
             | 
| 596 | 
            -
             | 
| 597 | 
            -
             | 
| 598 | 
            -
             | 
| 599 | 
            -
             | 
| 600 | 
            -
             | 
| 601 | 
            -
             | 
| 602 | 
            -
             | 
| 603 | 
            -
             | 
| 604 | 
            -
                      end
         | 
| 605 | 
            -
                      saved = []
         | 
| 606 | 
            -
                      @ole_excel.Workbooks.each { |w| saved << w.Saved }
         | 
| 607 | 
            -
                      @ole_excel.CalculateBeforeSave = false
         | 
| 608 | 
            -
                      @ole_excel.Calculation =
         | 
| 609 | 
            -
                        calculation_mode == :automatic ? XlCalculationAutomatic : XlCalculationManual
         | 
| 610 | 
            -
                      saved = []
         | 
| 611 | 
            -
                      @ole_excel.Workbooks.each { |w| saved << w.Saved }
         | 
| 533 | 
            +
                  return unless calc_mode_changable
         | 
| 534 | 
            +
                  retain_saved_workbooks do
         | 
| 535 | 
            +
                    begin
         | 
| 536 | 
            +
                      best_wb_to_make_visible = @ole_excel.Workbooks.sort_by {|wb|
         | 
| 537 | 
            +
                        score =
         | 
| 538 | 
            +
                          (wb.Saved    ? 0 : 40) +  # an unsaved workbooks is most likely the main workbook
         | 
| 539 | 
            +
                          (wb.ReadOnly ? 0 : 20) +  # the main wb is usually writable
         | 
| 540 | 
            +
                          case wb.Name.split(".").last.downcase
         | 
| 541 | 
            +
                            when "xlsm" then 10  # the main workbook is more likely to have macros
         | 
| 542 | 
            +
                            when "xls"  then  8
         | 
| 543 | 
            +
                            when "xlsx" then  4
         | 
| 544 | 
            +
                            when "xlam" then -2  # libraries are not normally the main workbook
         | 
| 545 | 
            +
                            else 0
         | 
| 546 | 
            +
                          end
         | 
| 547 | 
            +
                        score
         | 
| 548 | 
            +
                      }.last
         | 
| 549 | 
            +
                      best_wb_to_make_visible.Windows(1).Visible = true
         | 
| 550 | 
            +
                    rescue => e
         | 
| 551 | 
            +
                      trace "error setting calculation=#{calculation_mode} msg: " + e.message
         | 
| 552 | 
            +
                      trace e.backtrace
         | 
| 553 | 
            +
                      # continue on errors here, failing would usually disrupt too much
         | 
| 612 554 | 
             
                    end
         | 
| 613 | 
            -
                     | 
| 555 | 
            +
                    @ole_excel.CalculateBeforeSave = false
         | 
| 556 | 
            +
                    @ole_excel.Calculation = calculation_mode == :automatic ? XlCalculationAutomatic : XlCalculationManual
         | 
| 614 557 | 
             
                  end
         | 
| 615 558 | 
             
                end
         | 
| 616 559 |  | 
| @@ -628,7 +571,6 @@ module RobustExcelOle | |
| 628 571 | 
             
                # sets calculation mode in a block
         | 
| 629 572 | 
             
                def with_calculation(calculation_mode)
         | 
| 630 573 | 
             
                  return unless calculation_mode
         | 
| 631 | 
            -
             | 
| 632 574 | 
             
                  old_calculation_mode = @ole_excel.Calculation
         | 
| 633 575 | 
             
                  begin
         | 
| 634 576 | 
             
                    self.calculation = calculation_mode
         | 
| @@ -639,35 +581,39 @@ module RobustExcelOle | |
| 639 581 | 
             
                end
         | 
| 640 582 |  | 
| 641 583 | 
             
                # set options in this Excel instance
         | 
| 642 | 
            -
                def for_this_instance(options)
         | 
| 643 | 
            -
                  set_options(options)
         | 
| 644 | 
            -
                end
         | 
| 645 | 
            -
             | 
| 646 584 | 
             
                def set_options(options)      
         | 
| 647 585 | 
             
                  @properties ||= { }
         | 
| 648 586 | 
             
                  PROPERTIES.each do |property|
         | 
| 649 587 | 
             
                    method = (property.to_s + '=').to_sym
         | 
| 650 | 
            -
                     | 
| 588 | 
            +
                    send(method, options[property]) 
         | 
| 651 589 | 
             
                  end
         | 
| 652 590 | 
             
                end
         | 
| 653 | 
            -
             | 
| 654 | 
            -
                 | 
| 655 | 
            -
             | 
| 656 | 
            -
             | 
| 591 | 
            +
             | 
| 592 | 
            +
                alias for_this_instance set_options  # :deprecated: #    
         | 
| 593 | 
            +
             | 
| 594 | 
            +
                # @return [Enumerator] traversing all workbook objects
         | 
| 595 | 
            +
                def each
         | 
| 596 | 
            +
                  if block_given?
         | 
| 597 | 
            +
                    ole_workbooks.lazy.each do |ole_workbook|
         | 
| 598 | 
            +
                      yield workbook_class.new(ole_workbook)
         | 
| 599 | 
            +
                    end
         | 
| 600 | 
            +
                  else
         | 
| 601 | 
            +
                    to_enum(:each).lazy
         | 
| 602 | 
            +
                  end
         | 
| 657 603 | 
             
                end
         | 
| 658 604 |  | 
| 605 | 
            +
                # @return [Array] all workbook objects
         | 
| 659 606 | 
             
                def workbooks
         | 
| 660 | 
            -
                   | 
| 661 | 
            -
                  ole_workbooks.map {|ole_workbook| workbook_class.new(ole_workbook) }
         | 
| 607 | 
            +
                  to_a
         | 
| 662 608 | 
             
                end
         | 
| 663 609 |  | 
| 664 | 
            -
                # traverses  | 
| 610 | 
            +
                # traverses all workbooks and sets options if provided
         | 
| 665 611 | 
             
                def each_workbook(opts = { })
         | 
| 666 | 
            -
                  ole_workbooks.each do |ow|
         | 
| 612 | 
            +
                  ole_workbooks.lazy.each do |ow|
         | 
| 667 613 | 
             
                    wb = workbook_class.new(ow, opts)
         | 
| 668 614 | 
             
                    block_given? ? (yield wb) : wb
         | 
| 669 615 | 
             
                  end
         | 
| 670 | 
            -
                end
         | 
| 616 | 
            +
                end    
         | 
| 671 617 |  | 
| 672 618 | 
             
                def each_workbook_with_index(opts = { }, offset = 0)
         | 
| 673 619 | 
             
                  i = offset
         | 
| @@ -677,6 +623,8 @@ module RobustExcelOle | |
| 677 623 | 
             
                  end
         | 
| 678 624 | 
             
                end
         | 
| 679 625 |  | 
| 626 | 
            +
                alias for_all_workbooks each_workbook   # :deprecated: #
         | 
| 627 | 
            +
             | 
| 680 628 | 
             
                def focus
         | 
| 681 629 | 
             
                  self.visible = true
         | 
| 682 630 | 
             
                  # if not Windows10 then
         | 
| @@ -692,11 +640,11 @@ module RobustExcelOle | |
| 692 640 | 
             
                  @workbook ||= workbook_class.new(@ole_excel.ActiveWorkbook) if @ole_excel.Workbooks.Count > 0
         | 
| 693 641 | 
             
                end    
         | 
| 694 642 |  | 
| 695 | 
            -
                 | 
| 643 | 
            +
                alias active_workbook workbook
         | 
| 696 644 |  | 
| 697 645 | 
             
                # @private
         | 
| 698 646 | 
             
                def to_s            
         | 
| 699 | 
            -
                   | 
| 647 | 
            +
                  "#<Excel: #{hwnd}#{ ("not alive" unless alive?)}>"
         | 
| 700 648 | 
             
                end
         | 
| 701 649 |  | 
| 702 650 | 
             
                # @private
         | 
| @@ -743,23 +691,20 @@ module RobustExcelOle | |
| 743 691 | 
             
              private
         | 
| 744 692 |  | 
| 745 693 | 
             
                def method_missing(name, *args) 
         | 
| 746 | 
            -
                   | 
| 747 | 
            -
             | 
| 748 | 
            -
             | 
| 749 | 
            -
             | 
| 750 | 
            -
             | 
| 751 | 
            -
             | 
| 752 | 
            -
             | 
| 753 | 
            -
                      end
         | 
| 754 | 
            -
                    else
         | 
| 755 | 
            -
                      begin
         | 
| 756 | 
            -
                        @ole_excel.send(name, *args)
         | 
| 757 | 
            -
                      rescue NoMethodError
         | 
| 758 | 
            -
                        raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
         | 
| 759 | 
            -
                      end
         | 
| 694 | 
            +
                  super unless name.to_s[0,1] =~ /[A-Z]/
         | 
| 695 | 
            +
                  raise ObjectNotAlive, 'method missing: Excel not alive' unless alive?
         | 
| 696 | 
            +
                  if ::ERRORMESSAGE_JRUBY_BUG
         | 
| 697 | 
            +
                    begin
         | 
| 698 | 
            +
                      @ole_excel.send(name, *args)
         | 
| 699 | 
            +
                    rescue Java::OrgRacobCom::ComFailException
         | 
| 700 | 
            +
                      raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
         | 
| 760 701 | 
             
                    end
         | 
| 761 702 | 
             
                  else
         | 
| 762 | 
            -
                     | 
| 703 | 
            +
                    begin
         | 
| 704 | 
            +
                      @ole_excel.send(name, *args)
         | 
| 705 | 
            +
                    rescue NoMethodError
         | 
| 706 | 
            +
                      raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
         | 
| 707 | 
            +
                    end
         | 
| 763 708 | 
             
                  end
         | 
| 764 709 | 
             
                end
         | 
| 765 710 | 
             
              end
         |