robust_excel_ole 1.16 → 1.18.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.
- checksums.yaml +4 -4
- data/Changelog +23 -0
- data/README.rdoc +17 -0
- data/benchmarking/Gemfile +7 -0
- data/benchmarking/README.md +131 -0
- data/benchmarking/creek_example.rb +33 -0
- data/benchmarking/generating_excel_files.rb +28 -0
- data/benchmarking/reo_example.rb +26 -0
- data/benchmarking/reo_example1.rb +31 -0
- data/benchmarking/reo_example2.rb +26 -0
- data/benchmarking/roo_example.rb +33 -0
- data/benchmarking/ruby_xl_example.rb +36 -0
- data/benchmarking/sample_excel_files/xlsx_500_rows.xlsx +0 -0
- data/benchmarking/simple_xlsx_reader_example.rb +33 -0
- data/benchmarking/spreadsheet_example.rb +36 -0
- data/bin/jreo.bat +3 -0
- data/bin/reo.bat +3 -0
- data/docs/README_excel.rdoc +6 -0
- data/docs/README_open.rdoc +4 -0
- data/docs/README_ranges.rdoc +15 -0
- data/examples/example_ruby_library.rb +27 -0
- data/lib/robust_excel_ole.rb +4 -3
- data/lib/robust_excel_ole/{address.rb → address_tool.rb} +23 -22
- data/lib/robust_excel_ole/{reo_common.rb → base.rb} +2 -90
- data/lib/robust_excel_ole/bookstore.rb +2 -2
- data/lib/robust_excel_ole/cell.rb +30 -18
- data/lib/robust_excel_ole/excel.rb +65 -30
- data/lib/robust_excel_ole/general.rb +7 -5
- data/lib/robust_excel_ole/range.rb +38 -15
- data/lib/robust_excel_ole/range_owners.rb +26 -11
- data/lib/robust_excel_ole/vba_objects.rb +30 -0
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +136 -147
- data/lib/robust_excel_ole/worksheet.rb +47 -15
- data/lib/rubygems_plugin.rb +3 -0
- data/robust_excel_ole.gemspec +1 -1
- data/spec/address_tool_spec.rb +175 -0
- data/spec/{reo_common_spec.rb → base_spec.rb} +10 -29
- data/spec/cell_spec.rb +67 -25
- data/spec/data/more_data/workbook.xls +0 -0
- data/spec/excel_spec.rb +41 -273
- data/spec/general_spec.rb +15 -19
- data/spec/range_spec.rb +57 -3
- data/spec/workbook_spec.rb +7 -75
- data/spec/workbook_specs/workbook_misc_spec.rb +10 -10
- data/spec/workbook_specs/workbook_open_spec.rb +228 -14
- data/spec/workbook_specs/workbook_unobtr_spec.rb +31 -31
- data/spec/worksheet_spec.rb +42 -0
- metadata +27 -8
- data/spec/address_spec.rb +0 -174
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
require 'spreadsheet'
|
3
|
+
|
4
|
+
start_time = Time.now
|
5
|
+
|
6
|
+
# ============================================
|
7
|
+
# =========== Read Example ===============
|
8
|
+
# ============================================
|
9
|
+
|
10
|
+
# Note: spreadsheet only supports .xls files (not .xlsx)
|
11
|
+
workbook = Spreadsheet.open './sample_excel_files/xls_500_rows.xls'
|
12
|
+
|
13
|
+
worksheets = workbook.worksheets
|
14
|
+
puts "Found #{worksheets.count} worksheets"
|
15
|
+
|
16
|
+
|
17
|
+
worksheets.each do |worksheet|
|
18
|
+
puts "Reading: #{worksheet.name}"
|
19
|
+
num_rows = 0
|
20
|
+
|
21
|
+
worksheet.rows.each do |row|
|
22
|
+
row_cells = row.to_a.map{ |v| v.methods.include?(:value) ? v.value : v }
|
23
|
+
num_rows += 1
|
24
|
+
|
25
|
+
# uncomment to print out row values
|
26
|
+
# puts row_cells.join " "
|
27
|
+
end
|
28
|
+
puts "Read #{num_rows} rows"
|
29
|
+
end
|
30
|
+
|
31
|
+
end_time = Time.now
|
32
|
+
running_time = end_time - start_time
|
33
|
+
puts "time: #{running_time} sec."
|
34
|
+
|
35
|
+
|
36
|
+
puts 'Done'
|
data/bin/jreo.bat
ADDED
data/bin/reo.bat
ADDED
data/docs/README_excel.rdoc
CHANGED
@@ -124,6 +124,12 @@ or, with a block,
|
|
124
124
|
|
125
125
|
excel.each_workbook(:visible => true) {|w| puts w}
|
126
126
|
|
127
|
+
=== Accessing the active workbook
|
128
|
+
|
129
|
+
You can operate on the active workbook with help of the method Workbook#active_workbook, e.g.
|
130
|
+
|
131
|
+
workbook = excel.active_workbook
|
132
|
+
|
127
133
|
=== Bringing an Excel instance to the foreground
|
128
134
|
|
129
135
|
excel1.focus
|
data/docs/README_open.rdoc
CHANGED
@@ -153,6 +153,7 @@ The method +General.to_reo+ enables type-lifting WIN32OLE objects to RobustExcel
|
|
153
153
|
This object can be type-lifted to a RobustExcelOle workbook.
|
154
154
|
|
155
155
|
workbook = win32ole_workbook.to_reo
|
156
|
+
|
156
157
|
workbook.to_class
|
157
158
|
=> RobustExcelOle::Workbook
|
158
159
|
|
@@ -166,6 +167,9 @@ You can supply options, e.g. +:visible+.
|
|
166
167
|
|
167
168
|
workbook = Workbook.new(win32ole_workbook, :visible => true)
|
168
169
|
|
170
|
+
You can also supply a workbook and options, e.g.
|
171
|
+
|
172
|
+
new_workbook = Workbook.new(workbook, :visible => true)
|
169
173
|
|
170
174
|
=== Identity transperence ===
|
171
175
|
|
data/docs/README_ranges.rdoc
CHANGED
@@ -394,11 +394,26 @@ The methods Worksheet#each, Worksheet#each_row and Worksheet#each_column enable
|
|
394
394
|
# do something with column
|
395
395
|
end
|
396
396
|
|
397
|
+
The method Worksheet#values yields all cell values of the used range of the worksheet into a 2-dimensional array. For example:
|
398
|
+
|
399
|
+
worksheet.values
|
400
|
+
=> [["foo", "workbook", "sheet1"], ["foo", nil, "foobaaa"], ["matz", "is", "nice"]]
|
401
|
+
|
402
|
+
The method Worksheet#each_rowvalue provides enable to access the values of each row.
|
403
|
+
|
404
|
+
worksheet.each_rowvalue do |row_values|
|
405
|
+
# do something with the row_values
|
406
|
+
end
|
407
|
+
|
397
408
|
You access a range of a row by giving the number of the row, and optionally, the range of the cell numbers.
|
398
409
|
|
399
410
|
worksheet.row_range(1) # => first row
|
400
411
|
worksheet.row_range(1, 1..3 ) # => first three cells of the first row
|
401
412
|
|
413
|
+
Reading the values is enabled with help of #values:
|
414
|
+
|
415
|
+
worksheet.row_range(1).values
|
416
|
+
|
402
417
|
Simarly you can access a range of a column.
|
403
418
|
|
404
419
|
worksheet.col_range(3) # => third column
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#require 'robust_excel_ole'
|
2
|
+
|
3
|
+
#workbook = Workbook.open './sample_excel_files/xlsx_500_rows.xlsx'
|
4
|
+
|
5
|
+
require_relative '../lib/robust_excel_ole'
|
6
|
+
|
7
|
+
include RobustExcelOle
|
8
|
+
|
9
|
+
workbook = Workbook.open './../spec/data/workbook.xls'
|
10
|
+
|
11
|
+
puts "Found #{workbook.worksheets_count} worksheets"
|
12
|
+
|
13
|
+
workbook.each do |worksheet|
|
14
|
+
puts "Reading: #{worksheet.name}"
|
15
|
+
num_rows = 0
|
16
|
+
|
17
|
+
worksheet.each do |row|
|
18
|
+
row_cells = row.map{ |cell| cell.value }
|
19
|
+
num_rows += 1
|
20
|
+
|
21
|
+
# uncomment to print out row values
|
22
|
+
# puts row_cells.join " "
|
23
|
+
end
|
24
|
+
puts "Read #{num_rows} rows"
|
25
|
+
end
|
26
|
+
|
27
|
+
puts 'Done'
|
data/lib/robust_excel_ole.rb
CHANGED
@@ -3,10 +3,11 @@ if RUBY_PLATFORM =~ /java/
|
|
3
3
|
else
|
4
4
|
require 'win32ole'
|
5
5
|
end
|
6
|
-
require File.join(File.dirname(__FILE__), 'robust_excel_ole/
|
7
|
-
require File.join(File.dirname(__FILE__), 'robust_excel_ole/range_owners')
|
8
|
-
require File.join(File.dirname(__FILE__), 'robust_excel_ole/address')
|
6
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/base')
|
9
7
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/general')
|
8
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/vba_objects')
|
9
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/range_owners')
|
10
|
+
require File.join(File.dirname(__FILE__), 'robust_excel_ole/address_tool')
|
10
11
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/excel')
|
11
12
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/bookstore')
|
12
13
|
require File.join(File.dirname(__FILE__), 'robust_excel_ole/workbook')
|
@@ -2,37 +2,38 @@
|
|
2
2
|
|
3
3
|
module RobustExcelOle
|
4
4
|
|
5
|
-
class
|
5
|
+
class AddressTool < Base
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
7
|
+
def initialize(address_string)
|
8
|
+
r1c1_letters = address_string.gsub(/[0-9]/,'')
|
9
|
+
@row_letter = r1c1_letters[0..0]
|
10
|
+
@col_letter = r1c1_letters[1..1]
|
10
11
|
end
|
11
12
|
|
12
13
|
# address formats that are valid:
|
13
14
|
# r1c1-format: e.g. "Z3S1", "Z3S1:Z5S2", "Z[3]S1", "Z3S[-1]:Z[5]S1", "Z[3]", "S[-2]"
|
14
15
|
# infinite ranges are not possible, e.g. "Z3:Z5", "S2:S5", "Z2", "S3", "Z[2]"
|
15
|
-
#
|
16
|
-
#
|
16
|
+
# integer_ranges-fromat: e.g. [3,1], [3,"A"], [3..5,1..2], [3..5, "A".."B"],
|
17
|
+
# [3..4, nil], [nil, 2..4], [2,nil], [nil,4]
|
17
18
|
# a1-format: e.g. "A3", "A3:B5", "A:B", "3:5", "A", "3"
|
18
19
|
|
19
|
-
def
|
20
|
+
def as_r1c1(address)
|
20
21
|
transform_address(address,:r1c1)
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
+
def as_a1(address)
|
24
25
|
transform_address(address,:a1)
|
25
26
|
end
|
26
27
|
|
27
28
|
# valid address formats: e.g. [3,1], [3,"A"], [3..5,1..2], [3..5, "A".."B"],
|
28
29
|
# [3..4, nil], [nil, 2..4], [2,nil], [nil,4]
|
29
|
-
def
|
30
|
+
def as_integer_ranges(address)
|
30
31
|
transform_address(address,:int_range)
|
31
32
|
end
|
32
33
|
|
33
34
|
private
|
34
35
|
|
35
|
-
def
|
36
|
+
def transform_address(address, format)
|
36
37
|
address = address.is_a?(Array) ? address : [address]
|
37
38
|
raise AddressInvalid, "address #{address.inspect} has more than two components" if address.size > 2
|
38
39
|
begin
|
@@ -70,8 +71,8 @@ module RobustExcelOle
|
|
70
71
|
raise AddressInvalid, "address (#{address.inspect}) format not correct"
|
71
72
|
end
|
72
73
|
if format==:r1c1
|
73
|
-
r1c1_string(
|
74
|
-
r1c1_string(
|
74
|
+
r1c1_string(@row_letter,rows,:min) + r1c1_string(@col_letter,columns,:min) + ":" +
|
75
|
+
r1c1_string(@row_letter,rows,:max) + r1c1_string(@col_letter,columns,:max)
|
75
76
|
elsif format==:int_range
|
76
77
|
[rows,columns]
|
77
78
|
else
|
@@ -79,8 +80,7 @@ module RobustExcelOle
|
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
82
|
-
|
83
|
-
def self.r1c1_string(letter,int_range,type)
|
83
|
+
def r1c1_string(letter,int_range,type)
|
84
84
|
return "" if int_range.nil? || int_range.begin.nil?
|
85
85
|
parameter = type == :min ? int_range.begin : int_range.end
|
86
86
|
is_relative = parameter.is_a?(Array)
|
@@ -88,27 +88,28 @@ module RobustExcelOle
|
|
88
88
|
letter + (is_relative ? "(" : "") + parameter.to_s + (is_relative ? ")" : "")
|
89
89
|
end
|
90
90
|
|
91
|
-
|
92
|
-
def self.analyze(comp,format)
|
91
|
+
def analyze(comp,format)
|
93
92
|
row_comp, col_comp = if format==:a1
|
94
93
|
[comp.gsub(/[A-Z]/,''), comp.gsub(/[0-9]/,'')]
|
95
94
|
else
|
96
|
-
a,b = comp.split(
|
97
|
-
c,d = b.split(
|
95
|
+
a,b = comp.split(@row_letter)
|
96
|
+
c,d = b.split(@col_letter)
|
98
97
|
b.nil? ? ["",b] : (d.nil? ? [c,""] : [c,d])
|
99
98
|
end
|
100
|
-
def
|
99
|
+
def s2n(s)
|
101
100
|
s!="" ? (s[0] == "[" ? [s.gsub(/\[|\]/,'').to_i] : (s.to_i!=0 ? s.to_i : s)) : nil
|
102
101
|
end
|
103
102
|
[s2n(row_comp), s2n(col_comp)]
|
104
103
|
end
|
105
104
|
|
106
|
-
|
107
|
-
# @private
|
108
|
-
def self.str2num(str)
|
105
|
+
def str2num(str)
|
109
106
|
str.tr("A-Z","0-9A-P").to_i(26) + (26**str.size-1)/25
|
110
107
|
end
|
111
108
|
|
112
109
|
end
|
113
110
|
|
111
|
+
# @private
|
112
|
+
class AddressInvalid < REOError
|
113
|
+
end
|
114
|
+
|
114
115
|
end
|
@@ -50,94 +50,10 @@ module RobustExcelOle
|
|
50
50
|
class MiscREOError < REOError
|
51
51
|
end
|
52
52
|
|
53
|
-
# @private
|
54
|
-
class ExcelDamaged < ExcelREOError
|
55
|
-
end
|
56
|
-
|
57
|
-
# @private
|
58
|
-
class UnsavedWorkbooks < ExcelREOError
|
59
|
-
end
|
60
|
-
|
61
|
-
# @private
|
62
|
-
class WorkbookBlocked < WorkbookREOError
|
63
|
-
end
|
64
|
-
|
65
|
-
# @private
|
66
|
-
class WorkbookNotSaved < WorkbookREOError
|
67
|
-
end
|
68
|
-
|
69
|
-
# @private
|
70
|
-
class WorkbookReadOnly < WorkbookREOError
|
71
|
-
end
|
72
|
-
|
73
|
-
# @private
|
74
|
-
class WorkbookBeingUsed < WorkbookREOError
|
75
|
-
end
|
76
|
-
|
77
|
-
# @private
|
78
|
-
class WorkbookConnectingUnsavedError < WorkbookREOError
|
79
|
-
end
|
80
|
-
|
81
|
-
# @private
|
82
|
-
class WorkbookConnectingBlockingError < WorkbookREOError
|
83
|
-
end
|
84
|
-
|
85
|
-
# @private
|
86
|
-
class WorkbookConnectingUnknownError < WorkbookREOError
|
87
|
-
end
|
88
|
-
|
89
|
-
# @private
|
90
|
-
class FileNotFound < FileREOError
|
91
|
-
end
|
92
|
-
|
93
|
-
# @private
|
94
|
-
class FileNameNotGiven < FileREOError
|
95
|
-
end
|
96
|
-
|
97
|
-
# @private
|
98
|
-
class FileAlreadyExists < FileREOError
|
99
|
-
end
|
100
|
-
|
101
|
-
# @private
|
102
|
-
class NameNotFound < NamesREOError
|
103
|
-
end
|
104
|
-
|
105
|
-
# @private
|
106
|
-
class NameAlreadyExists < NamesREOError
|
107
|
-
end
|
108
|
-
|
109
|
-
# @private
|
110
|
-
class RangeNotEvaluatable < MiscREOError
|
111
|
-
end
|
112
|
-
|
113
|
-
# @private
|
114
|
-
class RangeNotCreated < MiscREOError
|
115
|
-
end
|
116
|
-
|
117
|
-
# @private
|
118
|
-
class RangeNotCopied < MiscREOError
|
119
|
-
end
|
120
|
-
|
121
|
-
# @private
|
122
|
-
class OptionInvalid < MiscREOError
|
123
|
-
end
|
124
|
-
|
125
|
-
# @private
|
126
|
-
class ObjectNotAlive < MiscREOError
|
127
|
-
end
|
128
|
-
|
129
53
|
# @private
|
130
54
|
class TypeREOError < REOError
|
131
55
|
end
|
132
56
|
|
133
|
-
# @private
|
134
|
-
class TimeOut < REOError
|
135
|
-
end
|
136
|
-
|
137
|
-
# @private
|
138
|
-
class AddressInvalid < REOError
|
139
|
-
end
|
140
|
-
|
141
57
|
# @private
|
142
58
|
class UnexpectedREOError < REOError
|
143
59
|
end
|
@@ -145,13 +61,9 @@ module RobustExcelOle
|
|
145
61
|
# @private
|
146
62
|
class NotImplementedREOError < REOError
|
147
63
|
end
|
64
|
+
|
148
65
|
|
149
|
-
class
|
150
|
-
|
151
|
-
# @private
|
152
|
-
def excel
|
153
|
-
raise TypeREOError, 'receiver instance is neither an Excel nor a Workbook'
|
154
|
-
end
|
66
|
+
class Base
|
155
67
|
|
156
68
|
# @private
|
157
69
|
def own_methods
|
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module RobustExcelOle
|
4
4
|
|
5
|
-
class Bookstore <
|
5
|
+
class Bookstore < Base
|
6
|
+
|
6
7
|
def initialize
|
7
8
|
@filename2books ||= Hash.new { |hash, key| hash[key] = [] }
|
8
9
|
@hidden_excel_instance = nil
|
@@ -103,7 +104,6 @@ module RobustExcelOle
|
|
103
104
|
|
104
105
|
private
|
105
106
|
|
106
|
-
# @private
|
107
107
|
def try_hidden_excel
|
108
108
|
@hidden_excel_instance.__getobj__ if @hidden_excel_instance && @hidden_excel_instance.weakref_alive? && @hidden_excel_instance.__getobj__.alive?
|
109
109
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
+
require File.join(File.dirname(__FILE__), './range')
|
4
|
+
|
3
5
|
module RobustExcelOle
|
4
|
-
class Cell < REOCommon
|
5
|
-
attr_reader :cell
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
class Cell < Range
|
8
|
+
#attr_reader :ole_cell
|
9
|
+
|
10
|
+
def initialize(win32_cell, worksheet)
|
11
|
+
super
|
12
|
+
ole_cell
|
9
13
|
end
|
10
14
|
|
11
15
|
def v
|
@@ -16,25 +20,33 @@ module RobustExcelOle
|
|
16
20
|
self.Value = value
|
17
21
|
end
|
18
22
|
|
23
|
+
def ole_cell
|
24
|
+
@ole_range = @ole_range.MergeArea.Item(1,1) if @ole_range.MergeCells
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
19
29
|
# @private
|
20
30
|
def method_missing(name, *args)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
if name.to_s[0,1] =~ /[A-Z]/
|
32
|
+
if ::ERRORMESSAGE_JRUBY_BUG
|
33
|
+
begin
|
34
|
+
#@ole_cell.send(name, *args)
|
35
|
+
@ole_range.send(name, *args)
|
36
|
+
rescue Java::OrgRacobCom::ComFailException
|
37
|
+
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
38
|
+
end
|
39
|
+
else
|
40
|
+
begin
|
41
|
+
#@ole_cell.send(name, *args)
|
42
|
+
@ole_range.send(name, *args)
|
43
|
+
rescue NoMethodError
|
44
|
+
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
45
|
+
end
|
27
46
|
end
|
28
47
|
else
|
29
|
-
|
30
|
-
@cell.send(name, *args)
|
31
|
-
rescue NoMethodError
|
32
|
-
raise VBAMethodMissingError, "unknown VBA property or method #{name.inspect}"
|
33
|
-
end
|
48
|
+
super
|
34
49
|
end
|
35
|
-
# else
|
36
|
-
# super
|
37
|
-
# end
|
38
50
|
end
|
39
51
|
end
|
40
52
|
end
|