robust_excel_ole 1.2 → 1.3

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,7 +6,7 @@
6
6
 
7
7
  Imagine, you have opened a workbook with
8
8
 
9
- book = Workbook.open('workbook.xls', :visible => true)
9
+ book = Workbook.open('spec/data/workbook.xls', :visible => true)
10
10
 
11
11
  and have modified it.
12
12
 
@@ -37,8 +37,8 @@ For example, you want to save a workbook and overwrite the file if it exists bef
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('workbook.xls')
41
- book2 = Workbook.open('another_workbook.xls')
40
+ book = Workbook.open('spec/data/workbook.xls')
41
+ book2 = Workbook.open('spec/data/another_workbook.xls')
42
42
  book2.save_as('dir/workbook.xls', :if_exists => :overwrite, :if_obstructed => :save)
43
43
 
44
44
  === Closing a workbook.
@@ -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('workbook.xls')
65
+ book = 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
 
@@ -70,8 +70,8 @@ You can save a workbook with given file name, if it is open.
70
70
 
71
71
  The workbook can be saved under a new file name, if it is open.
72
72
 
73
- Workbook.save_as('workbook.xls', 'new_workbook.xls')
73
+ Workbook.save_as('spec/data/workbook.xls', 'new_workbook.xls')
74
74
 
75
75
  Finally the workbook can be closed with a given filename.
76
76
 
77
- Workbook.close('workbook.xls')
77
+ Workbook.close('spec/data/workbook.xls')
@@ -6,7 +6,7 @@ Worksheets are represented by Sheet objects.
6
6
 
7
7
  Assume you have opened a workbook
8
8
 
9
- book = Workbook.open('workbook.xls', :visible => true)
9
+ book = Workbook.open('spec/data/workbook.xls', :visible => true)
10
10
 
11
11
  === Accessing a worksheet.
12
12
 
@@ -32,6 +32,10 @@ You can access all Sheet objects by using the methods Workbook#each.
32
32
  # do something with sheet
33
33
  end
34
34
 
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
36
+ (see https://docs.microsoft.com/en-us/office/vba/api/excel.worksheet#methods).
37
+ For some common and complex tasks you can apply methods of RobustExcelOle.
38
+
35
39
  === Reading and changing the worksheet name
36
40
 
37
41
  You can read and change the worksheet name.
@@ -24,7 +24,7 @@ begin
24
24
  name = cell.Name.Name rescue nil
25
25
  if name
26
26
  cell.Value = cell.Value.to_s + cell.Offset(0,1).Value.to_s
27
- sheet.set_name(name, cell.Row, cell.Column)
27
+ sheet.add_name(name, cell.Row, cell.Column)
28
28
  end
29
29
  end
30
30
  end
@@ -33,7 +33,7 @@ begin
33
33
  cell_name = short_name ? short_name : sheet_name
34
34
  contains_named_cells = true
35
35
  new_sheet[cell.Row, cell.Column].Value = cell.Value
36
- new_sheet.set_name(cell_name, cell.Row,cell.Column)
36
+ new_sheet.add_name(cell_name, cell.Row,cell.Column)
37
37
  end
38
38
  end
39
39
  new_sheet.Delete() unless contains_named_cells
@@ -36,7 +36,7 @@ begin
36
36
  rescue REOError => msg
37
37
  sheet_new.name = sheet_name + sheet.name if msg.message == "sheet name already exists"
38
38
  end
39
- sheet_new.set_name("name", 2, 2)
39
+ sheet_new.add_name("name",2,2)
40
40
  sheet_new["name"] = sheet_name
41
41
  end
42
42
 
@@ -29,7 +29,7 @@ begin
29
29
  book.each do |sheet|
30
30
  sheet.each do |cell_orig|
31
31
  contents = cell_orig.Value
32
- sheet.set_name(contents, cell_orig.Row, cell_orig.Column) if contents && contents.is_a?(String)
32
+ sheet.add_name(contents, cell_orig.Row, cell_orig.Column) if contents && contents.is_a?(String)
33
33
  end
34
34
  end
35
35
  end
@@ -17,7 +17,7 @@ begin
17
17
  workbook = book.ole_workbook
18
18
  fullname = workbook.Fullname
19
19
  puts "fullname: #{fullname}"
20
- sheet.set_name("a_name",1,1) # rename cell A1 to "a_name"
20
+ sheet.add_name("a_name",1,1) # rename cell A1 to "a_name"
21
21
  number = workbook.Names.Count
22
22
  puts "number of name objects :#{number}"
23
23
  name_object = workbook.Names("a_name")
@@ -77,7 +77,7 @@ module RobustExcelOle
77
77
  def open(file, opts={ }, &block)
78
78
  options = @options = process_options(opts)
79
79
  book = nil
80
- if (not (options[:force][:excel] == :new))
80
+ if (not (options[:force][:excel] == :new) and not (options[:force][:excel] == :reserved_new))
81
81
  # if readonly is true, then prefer a book that is given in force_excel if this option is set
82
82
  forced_excel = if options[:force][:excel]
83
83
  options[:force][:excel] == :current ? excel_class.new(:reuse => true) : excel_of(options[:force][:excel])
@@ -238,7 +238,8 @@ module RobustExcelOle
238
238
  return
239
239
  end
240
240
  excel_option = (options[:force].nil? or options[:force][:excel].nil?) ? options[:default][:excel] : options[:force][:excel]
241
- @excel = self.class.excel_of(excel_option) unless (excel_option == :current || excel_option == :new)
241
+ @excel = self.class.excel_of(excel_option) unless (excel_option == :current || excel_option == :new || excel_option == :reserved_new)
242
+ excel_class.new(:reuse => false) if excel_option == :reserved_new and Excel.known_excel_instances.empty?
242
243
  @excel = excel_class.new(:reuse => (excel_option == :current)) unless (@excel && @excel.alive?)
243
244
  @excel
244
245
  end
@@ -772,30 +773,14 @@ module RobustExcelOle
772
773
  # @param [String] name the name of a range
773
774
  # @returns [Variant] the value of the range
774
775
  def [] name
775
- nameval(name)
776
+ namevalue_glob(name)
776
777
  end
777
778
 
778
779
  # sets the value of a range
779
780
  # @param [String] name the name of the range
780
781
  # @param [Variant] value the contents of the range
781
782
  def []= (name, value)
782
- set_nameval(name,value, :color => 42) # 42 - aqua-marin, 4-green
783
- end
784
-
785
- # renames a range
786
- # @param [String] name the previous range name
787
- # @param [String] new_name the new range name
788
- def rename_range(name, new_name)
789
- begin
790
- item = self.Names.Item(name)
791
- rescue WIN32OLERuntimeError
792
- raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
793
- end
794
- begin
795
- item.Name = new_name
796
- rescue WIN32OLERuntimeError
797
- raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
798
- end
783
+ set_namevalue_glob(name,value, :color => 42) # 42 - aqua-marin, 4-green
799
784
  end
800
785
 
801
786
  # sets options
@@ -604,14 +604,14 @@ module RobustExcelOle
604
604
  # @param [String] name the name of a range
605
605
  # @returns [Variant] the value of the range
606
606
  def [] name
607
- nameval(name)
607
+ namevalue_glob(name)
608
608
  end
609
609
 
610
610
  # sets the value of a range
611
611
  # @param [String] name the name of the range
612
612
  # @param [Variant] value the contents of the range
613
613
  def []= (name, value)
614
- set_nameval(name,value, :color => 42) # 42 - aqua-marin, 7-green
614
+ set_namevalue_glob(name,value, :color => 42) # 42 - aqua-marin, 7-green
615
615
  end
616
616
 
617
617
  def to_s # :nodoc: #
@@ -657,8 +657,13 @@ module RobustExcelOle
657
657
  end
658
658
 
659
659
  end
660
+
661
+ public
662
+
663
+ Application = Excel
664
+
660
665
  end
661
666
 
662
667
  class WIN32OLE
663
668
  include Enumerable
664
- end
669
+ end
@@ -1,22 +1,27 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module RobustExcelOle
3
+
3
4
  class Range < REOCommon
4
5
  include Enumerable
6
+ attr_reader :ole_range
7
+ attr_reader :worksheet
5
8
 
6
9
  def initialize(win32_range)
7
- @range = win32_range
10
+ @ole_range = win32_range
11
+ @worksheet = sheet_class.new(self.Parent)
8
12
  end
9
13
 
10
14
  def each
11
- @range.each do |row_or_column|
15
+ @ole_range.each do |row_or_column|
12
16
  yield RobustExcelOle::Cell.new(row_or_column)
13
17
  end
14
18
  end
15
19
 
20
+ # returns flat array of the values of a given range
21
+ # @params [Range] a range
22
+ # @returns [Array] the values
16
23
  def values(range = nil)
17
- #+# result = self.map(&:value).flatten
18
24
  result = self.map{|x| x.value}.flatten
19
- #+# range ? result.each_with_index.select{ |row_or_column, i| range.include?(i) }.map{ |i| i[0] } : result
20
25
  if range
21
26
  relevant_result = []
22
27
  result.each_with_index{ |row_or_column, i| relevant_result << row_or_column if range.include?(i) }
@@ -28,11 +33,48 @@ module RobustExcelOle
28
33
 
29
34
  def [] index
30
35
  @cells = []
31
- @cells[index + 1] = RobustExcelOle::Cell.new(@range.Cells.Item(index + 1))
36
+ @cells[index + 1] = RobustExcelOle::Cell.new(@ole_range.Cells.Item(index + 1))
32
37
  end
33
38
 
39
+ # copies a range
40
+ # @params [Fixnum,Range] row or range of the rows
41
+ # @params [Fixnum,Range] column or range of columns
42
+ # @options [Sheet] the worksheet in which to copy
43
+ def copy(int_range1, int_range2, sheet = :__not_provided)
44
+ int_range1 = int_range1 .. int_range1 if int_range1.is_a?(Fixnum)
45
+ int_range2 = int_range2 .. int_range2 if int_range2.is_a?(Fixnum)
46
+ sheet = @worksheet if sheet == :__not_provided
47
+ if sheet.workbook.excel == @worksheet.workbook.excel
48
+ begin
49
+ self.Copy(:destination => sheet.range(int_range1.min..int_range1.max,
50
+ int_range2.min..int_range2.max).ole_range)
51
+ rescue WIN32OLERuntimeError
52
+ raise RangeNotCopied, "cannot copy range"
53
+ end
54
+ else
55
+ self.Select
56
+ self.Copy
57
+ sheet.Paste(sheet.range(int_range1.min..int_range1.max,int_range2.min..int_range2.max).ole_range)
58
+ end
59
+ end
60
+
61
+ def self.sheet_class # :nodoc: #
62
+ @sheet_class ||= begin
63
+ module_name = self.parent_name
64
+ "#{module_name}::Sheet".constantize
65
+ rescue NameError => e
66
+ Sheet
67
+ end
68
+ end
69
+
70
+ def sheet_class # :nodoc: #
71
+ self.class.sheet_class
72
+ end
73
+
74
+ private
75
+
34
76
  def method_missing(id, *args) # :nodoc: #
35
- @range.send(id, *args)
77
+ @ole_range.send(id, *args)
36
78
  end
37
79
  end
38
80
  end
@@ -65,6 +65,12 @@ module RobustExcelOle
65
65
  class RangeNotEvaluatable < MiscREOError # :nodoc: #
66
66
  end
67
67
 
68
+ class RangeNotCreated < MiscREOError # :nodoc: #
69
+ end
70
+
71
+ class RangeNotCopied < MiscREOError # :nodoc: #
72
+ end
73
+
68
74
  class OptionInvalid < MiscREOError # :nodoc: #
69
75
  end
70
76
 
@@ -77,6 +83,9 @@ module RobustExcelOle
77
83
  class TimeOut < REOError # :nodoc: #
78
84
  end
79
85
 
86
+ class AddressInvalid < REOError # :nodoc: #
87
+ end
88
+
80
89
  class UnexpectedREOError < REOError # :nodoc: #
81
90
  end
82
91
 
@@ -129,6 +138,42 @@ module RobustExcelOle
129
138
 
130
139
  end
131
140
 
141
+ class Address < REOCommon
142
+
143
+ attr_reader :rows
144
+ attr_reader :columns
145
+ attr_reader :a1_format
146
+
147
+ def initialize(address_comp1, address_comp2 = __not_provided)
148
+ begin
149
+ if address_comp2 == __not_provided
150
+ row_comp = address_comp1.gsub(/[0-9]/,'')
151
+ column_comp = address_comp1.gsub(/[A-Z]/,'')
152
+ if address_comp1 != column_comp+row_comp
153
+ raise AddressInvalid, "address #{address_comp1.inspect} not in A1-format"
154
+ end
155
+ address_comp1, address_comp2 = column_comp, row_comp
156
+ end
157
+ address_comp1 = address_comp1 .. address_comp1 unless address_comp1.is_a?(Range)
158
+ address_comp2 = address_comp2 .. address_comp2 unless address_comp2.is_a?(Range)
159
+ if address_comp1.min.is_a?(String)
160
+ raise AddressInvalid, "address (#{address_comp1.inspect}, #{address_comp2.inspect}) not in A1-format" if address_comp2.min.is_a?(String)
161
+ @columns = address_comp1
162
+ @rows = address_comp2
163
+ @a1_format = true
164
+ else
165
+ @columns = address_comp2
166
+ @rows = address_comp1
167
+ @a1_format = false
168
+ end
169
+ rescue
170
+ address_string = (address_comp2 == __not_provided) ?
171
+ "#{address_comp1.inspect}" : "#{address_comp1.inspect}, #{address_comp2.inspect}"
172
+ raise AddressInvalid, "address (#{address_string}) not in A1- or R1C1-format"
173
+ end
174
+ end
175
+ end
176
+
132
177
  class RangeOwners < REOCommon
133
178
 
134
179
  # returns the contents of a range with given name
@@ -141,7 +186,7 @@ module RobustExcelOle
141
186
  # @param [Hash] opts the options
142
187
  # @option opts [Symbol] :default the default value that is provided if no contents could be returned
143
188
  # @return [Variant] the contents of a range with given name
144
- def nameval(name, opts = {:default => :__not_provided})
189
+ def namevalue_glob(name, opts = {:default => :__not_provided})
145
190
  name_obj = begin
146
191
  name_object(name)
147
192
  rescue NameNotFound => msg
@@ -175,7 +220,7 @@ module RobustExcelOle
175
220
  # @param [Variant] value the contents of the range
176
221
  # @param [FixNum] color the color when setting a value
177
222
  # @param [Hash] opts :color [FixNum] the color when setting the contents
178
- def set_nameval(name, value, opts = {:color => 0})
223
+ def set_namevalue_glob(name, value, opts = {:color => 0})
179
224
  begin
180
225
  cell = name_object(name).RefersToRange
181
226
  cell.Interior.ColorIndex = opts[:color]
@@ -194,8 +239,8 @@ module RobustExcelOle
194
239
  # @param [Hash] opts the options
195
240
  # @option opts [Symbol] :default the default value that is provided if no contents could be returned
196
241
  # @return [Variant] the contents of a range with given name
197
- def rangeval(name, opts = {:default => :__not_provided})
198
- return nameval(name, opts) if self.is_a?(Book)
242
+ def namevalue(name, opts = {:default => :__not_provided})
243
+ return namevalue_glob(name, opts) if self.is_a?(Book)
199
244
  begin
200
245
  range = self.Range(name)
201
246
  rescue WIN32OLERuntimeError
@@ -214,17 +259,15 @@ module RobustExcelOle
214
259
  end
215
260
  return opts[:default] unless opts[:default] == :__not_provided or value.nil?
216
261
  value
217
- #return opts[:default] unless opts[:default] == :__not_provided
218
- #raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect}" if value == -2146828288 + RobustExcelOle::XlErrName
219
262
  end
220
263
 
221
264
  # assigns a value to a range given a locally defined name
222
265
  # @param [String] name the name of a range
223
266
  # @param [Variant] value the assigned value
224
267
  # @param [Hash] opts :color [FixNum] the color when setting the contents
225
- def set_rangeval(name,value, opts = {:color => 0})
268
+ def set_namevalue(name, value, opts = {:color => 0})
226
269
  begin
227
- return set_nameval(name, value, opts) if self.is_a?(Book)
270
+ return set_namevalue_glob(name, value, opts) if self.is_a?(Book)
228
271
  range = self.Range(name)
229
272
  rescue WIN32OLERuntimeError
230
273
  raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
@@ -238,14 +281,85 @@ module RobustExcelOle
238
281
  end
239
282
  end
240
283
 
284
+ def nameval(name, opts = {:default => :__not_provided}) # :deprecated: #
285
+ namevalue_glob(name, opts)
286
+ end
287
+
288
+ def set_nameval(name, value, opts = {:color => 0}) # :deprecated: #
289
+ set_namevalue_glob(name, value, opts)
290
+ end
291
+
292
+ def rangeval(name, opts = {:default => :__not_provided}) # :deprecated: #
293
+ namevalue(name, opts)
294
+ end
295
+
296
+ def set_rangeval(name, value, opts = {:color => 0}) # :deprecated: #
297
+ set_namevalue(name, value, opts)
298
+ end
299
+
300
+ # adds a name referring to a range given by the row and column
301
+ # @param [String] name the range name
302
+ # @params [Fixnum,Range] row or range of the rows
303
+ # @params [Fixnum,Range] column or range of columns
304
+ def add_name(name, int_range1, int_range2)
305
+ int_range1 = int_range1 .. int_range1 if int_range1.is_a?(Fixnum)
306
+ int_range2 = int_range2 .. int_range2 if int_range2.is_a?(Fixnum)
307
+ address = "Z" + int_range1.min.to_s + "S" + int_range2.min.to_s +
308
+ ":Z" + int_range1.max.to_s + "S" + int_range2.max.to_s
309
+ begin
310
+ self.Names.Add("Name" => name, "RefersToR1C1" => "=" + address)
311
+ rescue WIN32OLERuntimeError => msg
312
+ #trace "WIN32OLERuntimeError: #{msg.message}"
313
+ raise RangeNotEvaluatable, "cannot add name #{name.inspect} to cell with row #{row.inspect} and column #{column.inspect}"
314
+ end
315
+ name
316
+ end
317
+
318
+ def set_name(name,row,column) # :deprecated :#
319
+ add_name(name,row,column)
320
+ end
321
+
322
+ # renames a range
323
+ # @param [String] name the previous range name
324
+ # @param [String] new_name the new range name
325
+ def rename_range(name, new_name)
326
+ begin
327
+ item = self.Names.Item(name)
328
+ rescue WIN32OLERuntimeError
329
+ raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
330
+ end
331
+ begin
332
+ item.Name = new_name
333
+ rescue WIN32OLERuntimeError
334
+ raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
335
+ end
336
+ end
337
+
338
+ # deletes a name of a range
339
+ # @param [String] name the previous range name
340
+ # @param [String] new_name the new range name
341
+ def delete_name(name)
342
+ begin
343
+ item = self.Names.Item(name)
344
+ rescue WIN32OLERuntimeError
345
+ raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
346
+ end
347
+ begin
348
+ item.Delete
349
+ rescue WIN32OLERuntimeError
350
+ raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
351
+ end
352
+ end
353
+
354
+
241
355
  private
242
356
 
243
357
  def name_object(name)
244
358
  begin
245
- self.Parent.Names.Item(name)
359
+ self.Names.Item(name)
246
360
  rescue WIN32OLERuntimeError
247
361
  begin
248
- self.Names.Item(name)
362
+ self.Parent.Names.Item(name)
249
363
  rescue WIN32OLERuntimeError
250
364
  raise RobustExcelOle::NameNotFound, "name #{name.inspect} not in #{self.inspect}"
251
365
  end