robust_excel_ole 1.30 → 1.31
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 +5 -0
- data/README.rdoc +4 -2
- data/benchmarking/reo_example.rb +1 -1
- data/benchmarking/reo_example1.rb +1 -1
- data/benchmarking/reo_example2.rb +1 -1
- data/docs/README_sheet.rdoc +1 -7
- data/examples/introductory_examples/example_open.rb +11 -0
- data/lib/robust_excel_ole.rb +18 -14
- data/lib/robust_excel_ole/cell.rb +1 -1
- data/lib/robust_excel_ole/cygwin.rb +2 -0
- data/lib/robust_excel_ole/general.rb +30 -80
- data/lib/robust_excel_ole/list_object.rb +17 -118
- data/lib/robust_excel_ole/list_row.rb +128 -0
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +47 -14
- data/lib/robust_excel_ole/worksheet.rb +35 -18
- data/lib/spec_helper.rb +1 -1
- data/spec/address_tool_spec.rb +2 -2
- data/spec/base_spec.rb +2 -2
- data/spec/bookstore_spec.rb +1 -1
- data/spec/cell_spec.rb +1 -1
- data/spec/cygwin_spec.rb +1 -1
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/excel_spec.rb +1 -1
- data/spec/general_spec.rb +34 -7
- data/spec/list_object_spec.rb +85 -20
- data/spec/range_spec.rb +1 -14
- data/spec/spec_helper.rb +1 -1
- data/spec/workbook_spec.rb +1 -1
- data/spec/workbook_specs/workbook_all_spec.rb +8 -28
- data/spec/workbook_specs/workbook_close_spec.rb +1 -1
- data/spec/workbook_specs/workbook_misc_spec.rb +3 -3
- data/spec/workbook_specs/workbook_open_spec.rb +45 -5
- data/spec/workbook_specs/workbook_save_spec.rb +1 -1
- data/spec/workbook_specs/workbook_sheet_spec.rb +1 -1
- data/spec/workbook_specs/workbook_subclass_spec.rb +1 -1
- data/spec/workbook_specs/workbook_unobtr_spec.rb +40 -62
- data/spec/worksheet_spec.rb +33 -1
- metadata +3 -2
@@ -0,0 +1,128 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module RobustExcelOle
|
4
|
+
|
5
|
+
using StringRefinement
|
6
|
+
|
7
|
+
class ListRow
|
8
|
+
|
9
|
+
def initialize(row_number)
|
10
|
+
@ole_listrow = ole_table.ListRows.Item(row_number)
|
11
|
+
end
|
12
|
+
|
13
|
+
# returns the value of the cell with given column name or number
|
14
|
+
# @param [Variant] column number or column name
|
15
|
+
# @return [Variant] value of the cell
|
16
|
+
def [] column_number_or_name
|
17
|
+
begin
|
18
|
+
ole_cell = ole_table.Application.Intersect(
|
19
|
+
@ole_listrow.Range, ole_table.ListColumns.Item(column_number_or_name).Range)
|
20
|
+
ole_cell.Value
|
21
|
+
rescue WIN32OLERuntimeError
|
22
|
+
raise TableRowError, "could not determine the value at column #{column_number_or_name}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# sets the value of the cell with given column name or number
|
27
|
+
# @param [Variant] column number or column name
|
28
|
+
# @param [Variant] value of the cell
|
29
|
+
def []=(column_number_or_name, value)
|
30
|
+
begin
|
31
|
+
ole_cell = ole_table.Application.Intersect(
|
32
|
+
@ole_listrow.Range, ole_table.ListColumns.Item(column_number_or_name).Range)
|
33
|
+
ole_cell.Value = value
|
34
|
+
rescue WIN32OLERuntimeError
|
35
|
+
raise TableRowError, "could not assign value #{value.inspect} to cell at column #{column_number_or_name}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# values of the row
|
40
|
+
# @return [Array] values of the row
|
41
|
+
def values
|
42
|
+
begin
|
43
|
+
@ole_listrow.Range.Value.first
|
44
|
+
rescue WIN32OLERuntimeError
|
45
|
+
raise TableError, "could not read values"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# sets the values of the row
|
50
|
+
# @param [Array] values of the row
|
51
|
+
def set_values values
|
52
|
+
begin
|
53
|
+
updated_values = self.values
|
54
|
+
updated_values[0,values.length] = values
|
55
|
+
@ole_listrow.Range.Value = [updated_values]
|
56
|
+
values
|
57
|
+
rescue WIN32OLERuntimeError
|
58
|
+
raise TableError, "could not set values #{values.inspect}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# deletes the values of the row
|
63
|
+
def delete_values
|
64
|
+
begin
|
65
|
+
@ole_listrow.Range.Value = [[].fill(nil,0..(ole_table.ListColumns.Count)-1)]
|
66
|
+
nil
|
67
|
+
rescue WIN32OLERuntimeError
|
68
|
+
raise TableError, "could not delete values"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def method_missing(name, *args)
|
73
|
+
name_str = name.to_s
|
74
|
+
core_name = name_str[-1]!='=' ? name_str : name_str[0..-2]
|
75
|
+
column_names = ole_table.HeaderRowRange.Value.first
|
76
|
+
column_name = column_names.find do |c|
|
77
|
+
c == core_name ||
|
78
|
+
c.gsub(/\W/,'_') == core_name ||
|
79
|
+
c.underscore == core_name ||
|
80
|
+
c.underscore.gsub(/\W/,'_') == core_name ||
|
81
|
+
c.replace_umlauts.gsub(/\W/,'_') == core_name ||
|
82
|
+
c.replace_umlauts.underscore.gsub(/\W/,'_') == core_name
|
83
|
+
end
|
84
|
+
if column_name
|
85
|
+
appended_eq = (name_str[-1]!='=' ? "" : "=")
|
86
|
+
method_name = core_name.replace_umlauts.underscore + appended_eq
|
87
|
+
define_and_call_method(column_name,method_name,*args)
|
88
|
+
else
|
89
|
+
super(name, *args)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# @private
|
94
|
+
def to_s
|
95
|
+
inspect
|
96
|
+
end
|
97
|
+
|
98
|
+
# @private
|
99
|
+
def inspect
|
100
|
+
"#<ListRow: " + "index:#{@ole_listrow.Index}" + " size:#{ole_table.ListColumns.Count}" + " #{ole_table.Name}" + ">"
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def define_and_call_method(column_name,method_name,*args)
|
106
|
+
ole_cell = ole_table.Application.Intersect(
|
107
|
+
@ole_listrow.Range, ole_table.ListColumns.Item(column_name).Range)
|
108
|
+
define_getting_setting_method(ole_cell,method_name)
|
109
|
+
self.send(method_name, *args)
|
110
|
+
end
|
111
|
+
|
112
|
+
def define_getting_setting_method(ole_cell,name)
|
113
|
+
if name[-1] != '='
|
114
|
+
self.class.define_method(name) do
|
115
|
+
ole_cell.Value
|
116
|
+
end
|
117
|
+
else
|
118
|
+
self.class.define_method(name) do |value|
|
119
|
+
ole_cell.Value = value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
TableRow = ListRow
|
127
|
+
|
128
|
+
end
|
@@ -158,7 +158,7 @@ module RobustExcelOle
|
|
158
158
|
@excel = excel_class.new(ole_excel)
|
159
159
|
filename = @ole_workbook.Fullname.tr('\\','/')
|
160
160
|
else
|
161
|
-
filename = file_or_workbook
|
161
|
+
filename = file_or_workbook
|
162
162
|
ensure_workbook(filename, opts)
|
163
163
|
end
|
164
164
|
apply_options(filename, opts)
|
@@ -236,7 +236,7 @@ module RobustExcelOle
|
|
236
236
|
end
|
237
237
|
|
238
238
|
# @private
|
239
|
-
def ensure_workbook(filename, options)
|
239
|
+
def ensure_workbook(filename, options)
|
240
240
|
set_was_open options, true
|
241
241
|
return if (@ole_workbook && alive? && (options[:read_only].nil? || @ole_workbook.ReadOnly == options[:read_only]))
|
242
242
|
set_was_open options, false
|
@@ -253,9 +253,7 @@ module RobustExcelOle
|
|
253
253
|
if @ole_workbook && alive?
|
254
254
|
set_was_open options, true
|
255
255
|
manage_blocking_or_unsaved_workbook(filename,options)
|
256
|
-
if
|
257
|
-
end
|
258
|
-
open_or_create_workbook(filename,options) if @ole_workbook.ReadOnly != options[:read_only]
|
256
|
+
open_or_create_workbook(filename,options) if (!options[:read_only].nil?) && options[:read_only] != @ole_workbook.ReadOnly
|
259
257
|
else
|
260
258
|
if (excel_option.nil? || excel_option == :current) &&
|
261
259
|
!(::CONNECT_JRUBY_BUG && filename[0] == '/')
|
@@ -341,11 +339,12 @@ module RobustExcelOle
|
|
341
339
|
end
|
342
340
|
end
|
343
341
|
|
344
|
-
def manage_blocking_workbook(filename, options)
|
342
|
+
def manage_blocking_workbook(filename, options)
|
343
|
+
blocked_filename = -> { General.canonize(@ole_workbook.Fullname.tr('\\','/')) }
|
345
344
|
case options[:if_obstructed]
|
346
345
|
when :raise
|
347
346
|
raise WorkbookBlocked, "can't open workbook #{filename},
|
348
|
-
because it is being blocked by #{
|
347
|
+
because it is being blocked by #{blocked_filename.call} with the same name in a different path." +
|
349
348
|
"\nHint: Use the option :if_blocked with values :forget or :save,
|
350
349
|
to allow automatic closing of the old workbook (without or with saving before, respectively),
|
351
350
|
before the new workbook is being opened."
|
@@ -355,7 +354,7 @@ module RobustExcelOle
|
|
355
354
|
manage_saving_workbook(filename, options)
|
356
355
|
when :close_if_saved
|
357
356
|
if !@ole_workbook.Saved
|
358
|
-
raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{
|
357
|
+
raise WorkbookBlocked, "workbook with the same name in a different path is unsaved: #{blocked_filename.call}" +
|
359
358
|
"\nHint: Use the option :if_blocked => :save to save the workbook"
|
360
359
|
else
|
361
360
|
manage_forgetting_workbook(filename, options)
|
@@ -371,9 +370,14 @@ module RobustExcelOle
|
|
371
370
|
def manage_unsaved_workbook(filename, options)
|
372
371
|
case options[:if_unsaved]
|
373
372
|
when :raise
|
374
|
-
|
375
|
-
|
376
|
-
|
373
|
+
msg = if !options[:read_only].nil? && @ole_workbook.ReadOnly != options[:read_only]
|
374
|
+
"cannot change read-only mode of the workbook #{File.basename(filename).inspect}, because it contains unsaved changes"
|
375
|
+
else
|
376
|
+
"workbook is already open but not saved: #{File.basename(filename).inspect}"
|
377
|
+
end
|
378
|
+
raise WorkbookNotSaved, msg +
|
379
|
+
"\nHint: Use the option :if_unsaved with values :forget to close the unsaved workbook,
|
380
|
+
:accept to let it open, or :save to save it, respectivly"
|
377
381
|
when :forget
|
378
382
|
manage_forgetting_workbook(filename, options)
|
379
383
|
when :accept
|
@@ -406,7 +410,24 @@ module RobustExcelOle
|
|
406
410
|
@ole_workbook = nil
|
407
411
|
open_or_create_workbook(filename, options)
|
408
412
|
end
|
409
|
-
|
413
|
+
|
414
|
+
def explore_workbook_error(msg, want_change_readonly = nil)
|
415
|
+
if msg.message =~ /800A03EC/ && msg.message =~ /0x80020009/
|
416
|
+
# error message:
|
417
|
+
# 'This workbook is currently referenced by another workbook and cannot be closed'
|
418
|
+
# 'Diese Arbeitsmappe wird momentan von einer anderen Arbeitsmappe verwendet und kann nicht geschlossen werden.'
|
419
|
+
if want_change_readonly==true
|
420
|
+
raise WorkbookLinked, "read-only mode of this workbook cannot be changed, because it is being used by another workbook"
|
421
|
+
elsif want_change_readonly.nil?
|
422
|
+
raise WorkbookLinked, "workbook is being used by another workbook"
|
423
|
+
else
|
424
|
+
raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
|
425
|
+
end
|
426
|
+
else
|
427
|
+
raise UnexpectedREOError, "unknown WIN32OLERuntimeError:\n#{msg.message}"
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
410
431
|
def open_or_create_workbook(filename, options)
|
411
432
|
return if @ole_workbook && options[:if_unsaved] != :alert && options[:if_unsaved] != :excel &&
|
412
433
|
(options[:read_only].nil? || options[:read_only]==@ole_workbook.ReadOnly )
|
@@ -427,7 +448,8 @@ module RobustExcelOle
|
|
427
448
|
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
428
449
|
# for Excel2007: for option :if_unsaved => :alert and user cancels: this error appears?
|
429
450
|
# if yes: distinguish these events
|
430
|
-
|
451
|
+
want_change_readonly = !options[:read_only].nil? && (options[:read_only] != @ole_workbook.ReadOnly)
|
452
|
+
explore_workbook_error(msg, want_change_readonly)
|
431
453
|
end
|
432
454
|
begin
|
433
455
|
# workaround for bug in Excel 2010: workbook.Open does not always return the workbook when given file name
|
@@ -522,7 +544,14 @@ module RobustExcelOle
|
|
522
544
|
private
|
523
545
|
|
524
546
|
def close_workbook
|
525
|
-
|
547
|
+
#@ole_workbook.Close if alive?
|
548
|
+
if alive?
|
549
|
+
begin
|
550
|
+
@ole_workbook.Close
|
551
|
+
rescue WIN32OLERuntimeError, Java::OrgRacobCom::ComFailException => msg
|
552
|
+
explore_workbook_error(msg)
|
553
|
+
end
|
554
|
+
end
|
526
555
|
@ole_workbook = nil unless alive?
|
527
556
|
end
|
528
557
|
|
@@ -1103,6 +1132,10 @@ public
|
|
1103
1132
|
class WorkbookNotSaved < WorkbookREOError
|
1104
1133
|
end
|
1105
1134
|
|
1135
|
+
# @private
|
1136
|
+
class WorkbookLinked < WorkbookREOError
|
1137
|
+
end
|
1138
|
+
|
1106
1139
|
# @private
|
1107
1140
|
class WorkbookReadOnly < WorkbookREOError
|
1108
1141
|
end
|
@@ -11,6 +11,8 @@ module RobustExcelOle
|
|
11
11
|
# worksheet: see https://github.com/Thomas008/robust_excel_ole/blob/master/lib/robust_excel_ole/worksheet.rb
|
12
12
|
class Worksheet < RangeOwners
|
13
13
|
|
14
|
+
using ToReoRefinement
|
15
|
+
|
14
16
|
attr_reader :ole_worksheet
|
15
17
|
attr_reader :workbook
|
16
18
|
|
@@ -194,10 +196,12 @@ module RobustExcelOle
|
|
194
196
|
raise RangeNotEvaluatable, "cannot assign value #{value.inspect} to cell (#{y.inspect},#{x.inspect})"
|
195
197
|
end
|
196
198
|
|
199
|
+
# provides a 2-dimensional array that contains the values in each row
|
197
200
|
def values
|
198
201
|
@ole_worksheet.UsedRange.Value
|
199
202
|
end
|
200
203
|
|
204
|
+
# enumerator for accessing cells
|
201
205
|
def each
|
202
206
|
each_row do |row_range|
|
203
207
|
row_range.each do |cell|
|
@@ -216,24 +220,7 @@ module RobustExcelOle
|
|
216
220
|
end
|
217
221
|
end
|
218
222
|
|
219
|
-
|
220
|
-
@ole_worksheet.UsedRange.Value.each do |row_values|
|
221
|
-
yield row_values
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def each_value # :deprecated: #
|
226
|
-
each_rowvalue
|
227
|
-
end
|
228
|
-
|
229
|
-
def each_rowvalue_with_index(offset = 0)
|
230
|
-
i = offset
|
231
|
-
@ole_worksheet.UsedRange.Value.each do |row_values|
|
232
|
-
yield row_values, i
|
233
|
-
i += 1
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
223
|
+
# enumerator for accessing rows
|
237
224
|
def each_row(offset = 0)
|
238
225
|
offset += 1
|
239
226
|
1.upto(@end_row) do |row|
|
@@ -248,6 +235,7 @@ module RobustExcelOle
|
|
248
235
|
end
|
249
236
|
end
|
250
237
|
|
238
|
+
# enumerator for accessing columns
|
251
239
|
def each_column(offset = 0)
|
252
240
|
offset += 1
|
253
241
|
1.upto(@end_column) do |column|
|
@@ -262,6 +250,24 @@ module RobustExcelOle
|
|
262
250
|
end
|
263
251
|
end
|
264
252
|
|
253
|
+
def each_rowvalue # :deprecated: #
|
254
|
+
values.each do |row_values|
|
255
|
+
yield row_values
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def each_value # :deprecated: #
|
260
|
+
each_rowvalue
|
261
|
+
end
|
262
|
+
|
263
|
+
def each_rowvalue_with_index(offset = 0) # :deprecated: #
|
264
|
+
i = offset
|
265
|
+
values.each do |row_values|
|
266
|
+
yield row_values, i
|
267
|
+
i += 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
265
271
|
def row_range(row, integer_range = nil)
|
266
272
|
integer_range ||= 1..@end_column
|
267
273
|
RobustExcelOle::Range.new(@ole_worksheet.Range(@ole_worksheet.Cells(row, integer_range.min), @ole_worksheet.Cells(row, integer_range.max)), self)
|
@@ -303,6 +309,17 @@ module RobustExcelOle
|
|
303
309
|
range
|
304
310
|
end
|
305
311
|
|
312
|
+
# @params [Variant] table (listobject) name or number
|
313
|
+
# @return [ListObject] a table (listobject)
|
314
|
+
def table(number_or_name)
|
315
|
+
begin
|
316
|
+
ole_listobject = @ole_worksheet.ListObjects.Item(number_or_name)
|
317
|
+
rescue
|
318
|
+
raise WorksheetREOError, "table #{number_or_name} not found"
|
319
|
+
end
|
320
|
+
ListObject.new(ole_listobject)
|
321
|
+
end
|
322
|
+
|
306
323
|
# @private
|
307
324
|
# returns true, if the worksheet object responds to VBA methods, false otherwise
|
308
325
|
def alive?
|
data/lib/spec_helper.rb
CHANGED
data/spec/address_tool_spec.rb
CHANGED
data/spec/base_spec.rb
CHANGED
data/spec/bookstore_spec.rb
CHANGED
data/spec/cell_spec.rb
CHANGED
data/spec/cygwin_spec.rb
CHANGED
Binary file
|
data/spec/excel_spec.rb
CHANGED
data/spec/general_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'spec_helper'
|
4
4
|
|
5
5
|
$VERBOSE = nil
|
6
6
|
|
@@ -34,6 +34,8 @@ module RobustExcelOle
|
|
34
34
|
@simple_file_xlsx = @dir + '/workbook.xlsx'
|
35
35
|
@network_path = "N:/data/workbook.xls"
|
36
36
|
@hostname_share_path = "//DESKTOP-A3C5CJ6/spec/data/workbook.xls"
|
37
|
+
@network_path_downcase = "n:/data/workbook.xls"
|
38
|
+
@hostname_share_path_downcase = "//desktop-a3c5cj6/spec/data/workbook.xls"
|
37
39
|
@simple_file_extern = "D:/data/workbook.xls"
|
38
40
|
@hostname_share_path = "//DESKTOP-A3C5CJ6/spec/data/workbook.xls"
|
39
41
|
end
|
@@ -43,6 +45,23 @@ module RobustExcelOle
|
|
43
45
|
rm_tmp(@dir)
|
44
46
|
end
|
45
47
|
|
48
|
+
describe "relace_umlauts, underscore" do
|
49
|
+
|
50
|
+
it "should" do
|
51
|
+
"ä".replace_umlauts.should == "ae"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should replace umlauts" do
|
55
|
+
new_word = "BeforeÄÖÜäöüßAfter".replace_umlauts
|
56
|
+
new_word.should == "BeforeAeOeUeaeoeuessAfter"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should underscore" do
|
60
|
+
"BeforeAfter".underscore.should == "before_after"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
46
65
|
describe "to_reo" do
|
47
66
|
|
48
67
|
before do
|
@@ -269,6 +288,8 @@ module RobustExcelOle
|
|
269
288
|
General.canonize(@network_path).should == @network_path
|
270
289
|
General.canonize(@simple_file).should == @simple_file
|
271
290
|
General.canonize(@simple_file_extern).should == @simple_file_extern
|
291
|
+
General.canonize(@hostname_share_path_downcase).should == @network_path
|
292
|
+
General.canonize(@network_path_downcase).should == @network_path_downcase
|
272
293
|
end
|
273
294
|
|
274
295
|
end
|
@@ -279,12 +300,18 @@ module RobustExcelOle
|
|
279
300
|
it "should create a path" do
|
280
301
|
path1 = "this" / "is" / "a" / "path"
|
281
302
|
path1.should == "this/is/a/path"
|
282
|
-
path2 = "this" /
|
283
|
-
|
284
|
-
path3 = "
|
285
|
-
|
286
|
-
path4 = "
|
287
|
-
path4.should == "
|
303
|
+
path2 = "this" / nil
|
304
|
+
path2.should == "this"
|
305
|
+
path3 = "N:/E2" / "C:/gim/E2/workbook.xls"
|
306
|
+
path3.should == "C:/gim/E2/workbook.xls"
|
307
|
+
path4 = "N:/E2/" / "/gim/E2/workbook.xls"
|
308
|
+
path4.should == "/gim/E2/workbook.xls"
|
309
|
+
path5 = "N:/E2/" / "gim/E2/workbook.xls"
|
310
|
+
path5.should == "N:/E2/gim/E2/workbook.xls"
|
311
|
+
path6 = "N:/E2" / "spec/data/workbook.xls"
|
312
|
+
path6.should == "N:/E2/spec/data/workbook.xls"
|
313
|
+
path7 = "N:/E2" / "c:/gim/E2/workbook.xls"
|
314
|
+
path7.should == "c:/gim/E2/workbook.xls"
|
288
315
|
end
|
289
316
|
end
|
290
317
|
|