rubyexcel 0.0.5 → 0.0.6
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.
- data/lib/rubyexcel/address.rb +88 -5
- data/lib/rubyexcel/data.rb +209 -21
- data/lib/rubyexcel/element.rb +67 -6
- data/lib/rubyexcel/excel_tools.rb +62 -3
- data/lib/rubyexcel/rubyexcel_components.rb +8 -0
- data/lib/rubyexcel/section.rb +150 -14
- data/lib/rubyexcel.rb +448 -32
- metadata +2 -2
@@ -1,10 +1,21 @@
|
|
1
1
|
require 'win32ole' #Interface with Excel
|
2
2
|
require 'win32/registry' #Find Documents / My Documents for default directory
|
3
3
|
|
4
|
+
# Holder for WIN32OLE Excel Constants
|
4
5
|
module ExcelConstants; end
|
5
6
|
|
6
7
|
module RubyExcel
|
7
8
|
|
9
|
+
#
|
10
|
+
# Add borders to an Excel Range
|
11
|
+
#
|
12
|
+
# @param [WIN32OLE::Range] range the Excel Range to add borders to
|
13
|
+
# @param [Fixnum] weight the weight of the borders
|
14
|
+
# @param [Boolean] inner add inner borders
|
15
|
+
# @raise [ArgumentError] 'First Argument must be WIN32OLE Range'
|
16
|
+
# @return [WIN32OLE::Range] the range initially given
|
17
|
+
#
|
18
|
+
|
8
19
|
def self.borders( range, weight=1, inner=false )
|
9
20
|
range.ole_respond_to?( :borders ) or fail ArgumentError, 'First Argument must be WIN32OLE Range'
|
10
21
|
[0,1,2,3].include?( weight ) or fail ArgumentError, "Invalid line weight #{ weight }. Must be from 0 to 3"
|
@@ -18,12 +29,27 @@ module RubyExcel
|
|
18
29
|
|
19
30
|
class Workbook
|
20
31
|
|
32
|
+
#
|
33
|
+
# Drop a multidimensional Array into an Excel Sheet
|
34
|
+
#
|
35
|
+
# @param [Array<Array>] data the data to place in the Sheet
|
36
|
+
# @param [WIN32OLE::Worksheet, nil] sheet optional WIN32OLE Worksheet to use
|
37
|
+
# @return [WIN32OLE::Worksheet] the Worksheet containing the data
|
38
|
+
#
|
39
|
+
|
21
40
|
def dump_to_sheet( data, sheet=nil )
|
22
41
|
data.is_a?( Array ) or fail ArgumentError, "Invalid data type: #{ data.class }"
|
23
42
|
sheet ||= get_workbook.sheets(1)
|
24
43
|
sheet.range( sheet.cells( 1, 1 ), sheet.cells( data.length, data[0].length ) ).value = data
|
25
44
|
sheet
|
26
45
|
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Open or connect to an Excel instance
|
49
|
+
#
|
50
|
+
# @param [Boolean] invisible leave Excel invisible if creating a new instance
|
51
|
+
# @return [WIN32OLE::Excel] the first available Excel application
|
52
|
+
#
|
27
53
|
|
28
54
|
def get_excel( invisible = false )
|
29
55
|
excel = WIN32OLE::connect( 'excel.application' ) rescue WIN32OLE::new( 'excel.application' )
|
@@ -31,6 +57,14 @@ module RubyExcel
|
|
31
57
|
excel
|
32
58
|
end
|
33
59
|
|
60
|
+
#
|
61
|
+
# Create a new Excel Workbook
|
62
|
+
#
|
63
|
+
# @param [WIN32OLE::Excel, nil] excel an Excel object to use
|
64
|
+
# @param [Boolean] invisible leave Excel invisible if creating a new instance
|
65
|
+
# @return [WIN32OLE::Workbook] the new Excel Workbook
|
66
|
+
#
|
67
|
+
|
34
68
|
def get_workbook( excel=nil, invisible = false )
|
35
69
|
excel ||= get_excel( invisible )
|
36
70
|
wb = excel.workbooks.add
|
@@ -38,15 +72,32 @@ module RubyExcel
|
|
38
72
|
wb
|
39
73
|
end
|
40
74
|
|
75
|
+
#
|
76
|
+
# Take an Excel Sheet and standardise some of the formatting
|
77
|
+
#
|
78
|
+
# @param [WIN32OLE::Worksheet] sheet the Sheet to add formatting to
|
79
|
+
# @return [WIN32OLE::Worksheet] the sheet with formatting added
|
80
|
+
#
|
81
|
+
|
41
82
|
def make_sheet_pretty( sheet )
|
42
83
|
c = sheet.cells
|
43
84
|
c.rowheight = 15
|
44
85
|
c.entireColumn.autoFit
|
45
86
|
c.horizontalAlignment = -4108
|
46
87
|
c.verticalAlignment = -4108
|
88
|
+
sheet.UsedRange.Columns.each { |col| col.ColumnWidth = 30 if col.ColumnWidth > 50 }
|
89
|
+
RubyExcel.borders( sheet.usedrange, 1, true )
|
47
90
|
sheet
|
48
91
|
end
|
49
92
|
|
93
|
+
#
|
94
|
+
# Save the RubyExcel::Workbook as an Excel Workbook
|
95
|
+
#
|
96
|
+
# @param [String] filename the filename to save as
|
97
|
+
# @param [Boolean] invisible leave Excel invisible if creating a new instance
|
98
|
+
# @return [WIN32OLE::Workbook] the Workbook, saved as filename.
|
99
|
+
#
|
100
|
+
|
50
101
|
def save_excel( filename = 'Output', invisible = false )
|
51
102
|
filename = filename.gsub('/','\\')
|
52
103
|
unless filename.include?('\\')
|
@@ -59,9 +110,16 @@ module RubyExcel
|
|
59
110
|
wb
|
60
111
|
end
|
61
112
|
|
113
|
+
#
|
114
|
+
# Output the RubyExcel::Workbook to Excel
|
115
|
+
#
|
116
|
+
# @param [Boolean] invisible leave Excel invisible if creating a new instance
|
117
|
+
# @return [WIN32OLE::Workbook] the Workbook in Excel
|
118
|
+
#
|
119
|
+
|
62
120
|
def to_excel( invisible = false )
|
63
121
|
self.sheets.count == self.sheets.map(&:name).uniq.length or fail NoMethodError, 'Duplicate sheet name'
|
64
|
-
wb = get_workbook( nil,
|
122
|
+
wb = get_workbook( nil, true )
|
65
123
|
wb.parent.displayAlerts = false
|
66
124
|
first_time = true
|
67
125
|
self.each do |s|
|
@@ -70,9 +128,10 @@ module RubyExcel
|
|
70
128
|
make_sheet_pretty( dump_to_sheet( s.data.all, sht ) )
|
71
129
|
end
|
72
130
|
wb.sheets(1).select
|
131
|
+
wb.application.visible = true unless invisible
|
73
132
|
wb
|
74
133
|
end
|
75
134
|
|
76
|
-
end
|
135
|
+
end # Workbook
|
77
136
|
|
78
|
-
end
|
137
|
+
end # RubyExcel
|
@@ -5,6 +5,10 @@ require_relative 'section.rb'
|
|
5
5
|
|
6
6
|
module RubyExcel
|
7
7
|
|
8
|
+
#
|
9
|
+
# Example data to use in tests / demos
|
10
|
+
#
|
11
|
+
|
8
12
|
def self.sample_data
|
9
13
|
[
|
10
14
|
[ 'Part', 'Ref1', 'Ref2', 'Qty', 'Cost' ],
|
@@ -18,6 +22,10 @@ module RubyExcel
|
|
18
22
|
]
|
19
23
|
end
|
20
24
|
|
25
|
+
#
|
26
|
+
# Shortcut to create a Sheet with example data
|
27
|
+
#
|
28
|
+
|
21
29
|
def self.sample_sheet
|
22
30
|
Workbook.new.load RubyExcel.sample_data
|
23
31
|
end
|
data/lib/rubyexcel/section.rb
CHANGED
@@ -1,91 +1,177 @@
|
|
1
1
|
module RubyExcel
|
2
2
|
|
3
|
+
#
|
4
|
+
# Superclass for Row and Column
|
5
|
+
#
|
6
|
+
|
3
7
|
class Section
|
4
|
-
|
5
8
|
include Address
|
9
|
+
include Enumerable
|
6
10
|
|
7
|
-
|
11
|
+
# The Sheet parent of the Section
|
12
|
+
attr_reader :sheet
|
8
13
|
alias parent sheet
|
9
14
|
|
15
|
+
# The Data underlying the Sheet
|
16
|
+
attr_reader :data
|
17
|
+
|
18
|
+
#
|
19
|
+
# Creates a RubyExcel::Section instance
|
20
|
+
#
|
21
|
+
# @param [RubyExcel::Sheet] sheet the parent Sheet
|
22
|
+
#
|
23
|
+
|
10
24
|
def initialize( sheet )
|
11
25
|
@sheet = sheet
|
12
26
|
@data = sheet.data
|
13
27
|
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Append a value to the Section.
|
31
|
+
# This only adds an extra cell if it is the first Row / Column.
|
32
|
+
# This prevents a loop through Rows or Columns from extending diagonally away from the main data.
|
33
|
+
#
|
34
|
+
# @param [Object] value the object to append
|
35
|
+
#
|
14
36
|
|
15
37
|
def <<( value )
|
16
|
-
|
17
|
-
|
18
|
-
else
|
19
|
-
lastone = ( col_index( idx ) == 1 ? data.rows + 1 : data.rows )
|
38
|
+
case self
|
39
|
+
when Row ; lastone = ( col_index( idx ) == 1 ? data.cols + 1 : data.cols )
|
40
|
+
else ; lastone = ( col_index( idx ) == 1 ? data.rows + 1 : data.rows )
|
20
41
|
end
|
21
42
|
data[ translate_address( lastone ) ] = value
|
22
43
|
end
|
23
44
|
|
45
|
+
#
|
46
|
+
# Access a cell by its index within the Section
|
47
|
+
#
|
48
|
+
|
24
49
|
def cell( ref )
|
25
50
|
Element.new( sheet, translate_address( ref ) )
|
26
51
|
end
|
27
52
|
|
53
|
+
#
|
54
|
+
# Delete the data referenced by self
|
55
|
+
#
|
56
|
+
|
28
57
|
def delete
|
29
58
|
data.delete( self )
|
30
59
|
end
|
31
60
|
|
61
|
+
#
|
62
|
+
# Check whether the data in self is empty
|
63
|
+
#
|
64
|
+
|
32
65
|
def empty?
|
33
66
|
all? { |val| val.to_s.empty? }
|
34
67
|
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Return the address of a given value
|
71
|
+
#
|
72
|
+
# @yield [Object] yields each cell value to the block
|
73
|
+
# @return [String, nil] the address of the value or nil
|
74
|
+
#
|
35
75
|
|
36
76
|
def find
|
77
|
+
return to_enum( :find ) unless block_given?
|
37
78
|
each_cell { |ce| return ce.address if yield ce.value }; nil
|
38
79
|
end
|
39
80
|
|
81
|
+
#
|
82
|
+
# View the object for debugging
|
83
|
+
#
|
84
|
+
|
40
85
|
def inspect
|
41
86
|
"#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ idx }"
|
42
87
|
end
|
43
88
|
|
89
|
+
#
|
90
|
+
# Read a value by address
|
91
|
+
#
|
92
|
+
# @param [String, Fixnum] id the index or reference of the required value
|
93
|
+
#
|
94
|
+
|
44
95
|
def read( id )
|
45
96
|
data[ translate_address( id ) ]
|
46
97
|
end
|
47
98
|
alias [] read
|
48
99
|
|
100
|
+
#
|
101
|
+
# Summarise the values of a Section into a Hash
|
102
|
+
#
|
103
|
+
# @return [Hash]
|
104
|
+
#
|
105
|
+
|
49
106
|
def summarise
|
50
107
|
each_wh.inject( Hash.new(0) ) { |h, v| h[v]+=1; h }
|
51
108
|
end
|
52
109
|
alias summarize summarise
|
53
110
|
|
111
|
+
#
|
112
|
+
# The Section as a seperated value String
|
113
|
+
#
|
114
|
+
|
54
115
|
def to_s
|
55
116
|
to_a.join ( self.is_a?( Row ) ? "\t" : "\n" )
|
56
117
|
end
|
57
118
|
|
119
|
+
#
|
120
|
+
# Write a value by address
|
121
|
+
#
|
122
|
+
# @param [String, Fixnum] id the index or reference to write to
|
123
|
+
# @param [Object] val the object to place at the address
|
124
|
+
#
|
125
|
+
|
58
126
|
def write( id, val )
|
59
127
|
data[ translate_address( id ) ] = val
|
60
128
|
end
|
61
129
|
alias []= write
|
62
130
|
|
63
|
-
|
131
|
+
#
|
132
|
+
# Yields each value
|
133
|
+
#
|
64
134
|
|
65
135
|
def each
|
66
136
|
return to_enum(:each) unless block_given?
|
67
137
|
each_address { |addr| yield data[ addr ] }
|
68
138
|
end
|
69
139
|
|
140
|
+
#
|
141
|
+
# Yields each value, skipping headers
|
142
|
+
#
|
143
|
+
|
70
144
|
def each_without_headers
|
71
|
-
return to_enum(:each_without_headers) unless block_given?
|
145
|
+
return to_enum( :each_without_headers ) unless block_given?
|
72
146
|
each_address_without_headers { |addr| yield data[ addr ] }
|
73
147
|
end
|
74
148
|
alias each_wh each_without_headers
|
75
149
|
|
150
|
+
#
|
151
|
+
# Yields each cell
|
152
|
+
#
|
153
|
+
|
76
154
|
def each_cell
|
77
|
-
return to_enum(:each_cell) unless block_given?
|
155
|
+
return to_enum( :each_cell ) unless block_given?
|
78
156
|
each_address { |addr| yield Element.new( sheet, addr ) }
|
79
157
|
end
|
80
158
|
|
159
|
+
#
|
160
|
+
# Yields each cell, skipping headers
|
161
|
+
#
|
162
|
+
|
81
163
|
def each_cell_without_headers
|
82
|
-
return to_enum(:
|
164
|
+
return to_enum( :each_cell_without_headers ) unless block_given?
|
83
165
|
each_address { |addr| yield Element.new( sheet, addr ) }
|
84
166
|
end
|
85
167
|
alias each_cell_wh each_cell_without_headers
|
86
168
|
|
169
|
+
#
|
170
|
+
# Replaces each value with the result of the block
|
171
|
+
#
|
172
|
+
|
87
173
|
def map!
|
88
|
-
return to_enum(:map!) unless block_given?
|
174
|
+
return to_enum( :map! ) unless block_given?
|
89
175
|
each_address { |addr| data[addr] = ( yield data[addr] ) }
|
90
176
|
end
|
91
177
|
|
@@ -93,7 +179,8 @@ module RubyExcel
|
|
93
179
|
|
94
180
|
def translate_address( addr )
|
95
181
|
case self
|
96
|
-
when Row
|
182
|
+
when Row
|
183
|
+
col_letter( addr ) + idx.to_s
|
97
184
|
when Column
|
98
185
|
addr = addr.to_s unless addr.is_a?( String )
|
99
186
|
fail ArgumentError, "Invalid address : #{ addr }" if addr =~ /[^\d]/
|
@@ -103,24 +190,61 @@ module RubyExcel
|
|
103
190
|
|
104
191
|
end
|
105
192
|
|
193
|
+
#
|
194
|
+
# A Row in the Sheet
|
195
|
+
#
|
196
|
+
|
106
197
|
class Row < Section
|
107
198
|
|
199
|
+
# The Row index
|
108
200
|
attr_reader :idx
|
201
|
+
|
202
|
+
#
|
203
|
+
# Creates a RubyExcel::Row instance
|
204
|
+
#
|
205
|
+
# @param [RubyExcel::Sheet] sheet the Sheet which holds this Row
|
206
|
+
# @param [Fixnum] idx the index of this Row
|
207
|
+
#
|
109
208
|
|
110
209
|
def initialize( sheet, idx )
|
111
210
|
@idx = idx.to_i
|
112
211
|
super( sheet )
|
113
212
|
end
|
213
|
+
|
214
|
+
#
|
215
|
+
# Access a cell by its header
|
216
|
+
#
|
217
|
+
# @param [String] header the header to search for
|
218
|
+
# @return [RubyExcel::Element] the cell
|
219
|
+
#
|
114
220
|
|
115
221
|
def cell_by_header( header )
|
116
222
|
cell( getref( header ) )
|
117
223
|
end
|
118
224
|
alias cell_h cell_by_header
|
119
225
|
|
226
|
+
#
|
227
|
+
# Find the Address of a header
|
228
|
+
#
|
229
|
+
# @param [String] header the header to search for
|
230
|
+
# @return [String] the address of the header
|
231
|
+
#
|
232
|
+
|
120
233
|
def getref( header )
|
121
|
-
|
234
|
+
sheet.header_rows.times do |t|
|
235
|
+
res = sheet.row( t + 1 ).find &/^#{header}$/
|
236
|
+
return column_id( res ) if res
|
237
|
+
end
|
238
|
+
fail ArgumentError, 'Invalid header: ' + header.to_s
|
122
239
|
end
|
123
240
|
|
241
|
+
#
|
242
|
+
# Find a value in this Row by its header
|
243
|
+
#
|
244
|
+
# @param [String]header the header to search for
|
245
|
+
# @return [Object] the value at the address
|
246
|
+
#
|
247
|
+
|
124
248
|
def value_by_header( header )
|
125
249
|
self[ getref( header ) ]
|
126
250
|
end
|
@@ -133,15 +257,27 @@ module RubyExcel
|
|
133
257
|
end
|
134
258
|
|
135
259
|
def each_address_without_headers
|
136
|
-
(
|
260
|
+
( 'A'..col_letter( data.cols ) ).each { |col_id| yield "#{col_id}#{idx}" }
|
137
261
|
end
|
138
262
|
|
139
263
|
end
|
140
264
|
|
265
|
+
#
|
266
|
+
# A Column in the Sheet
|
267
|
+
#
|
268
|
+
|
141
269
|
class Column < Section
|
142
270
|
|
271
|
+
# The Row index
|
143
272
|
attr_reader :idx
|
144
273
|
|
274
|
+
#
|
275
|
+
# Creates a RubyExcel::Column instance
|
276
|
+
#
|
277
|
+
# @param [RubyExcel::Sheet] sheet the Sheet which holds this Column
|
278
|
+
# @param [String, Fixnum] idx the index of this Column
|
279
|
+
#
|
280
|
+
|
145
281
|
def initialize( sheet, idx )
|
146
282
|
@idx = idx
|
147
283
|
super( sheet )
|