rubyexcel 0.3.9 → 0.4.0
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/LICENSE.md +9 -9
- data/README.md +782 -779
- data/lib/rubyexcel.rb +264 -260
- data/lib/rubyexcel/address.rb +187 -187
- data/lib/rubyexcel/data.rb +450 -450
- data/lib/rubyexcel/element.rb +226 -226
- data/lib/rubyexcel/excel_tools.rb +287 -261
- data/lib/rubyexcel/rubyexcel_components.rb +70 -70
- data/lib/rubyexcel/section.rb +376 -376
- data/lib/rubyexcel/sheet.rb +720 -720
- metadata +6 -7
@@ -1,71 +1,71 @@
|
|
1
|
-
require_relative 'address.rb'
|
2
|
-
require_relative 'data.rb'
|
3
|
-
require_relative 'element.rb'
|
4
|
-
#require_relative 'excel_tools.rb'
|
5
|
-
require_relative 'section.rb'
|
6
|
-
require_relative 'sheet.rb'
|
7
|
-
|
8
|
-
module RubyExcel
|
9
|
-
|
10
|
-
#
|
11
|
-
# Example data to use in tests / demos
|
12
|
-
#
|
13
|
-
|
14
|
-
def self.sample_data
|
15
|
-
[
|
16
|
-
[ 'Part', 'Ref1', 'Ref2', 'Qty', 'Cost' ],
|
17
|
-
[ 'Type1', 'QT1', '231', 1, 35.15 ],
|
18
|
-
[ 'Type2', 'QT3', '123', 1, 40 ],
|
19
|
-
[ 'Type3', 'XT1', '321', 3, 0.1 ],
|
20
|
-
[ 'Type1', 'XY2', '132', 1, 30.00 ],
|
21
|
-
[ 'Type4', 'XT3', '312', 2, 3 ],
|
22
|
-
[ 'Type2', 'QY2', '213', 1, 99.99 ],
|
23
|
-
[ 'Type1', 'QT4', '123', 2, 104 ]
|
24
|
-
]
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
#
|
29
|
-
# Example hash to demonstrate imports
|
30
|
-
#
|
31
|
-
|
32
|
-
def self.sample_hash
|
33
|
-
|
34
|
-
{
|
35
|
-
Part1: {
|
36
|
-
Type1: {
|
37
|
-
SubType1: 1, SubType2: 2, SubType3: 3
|
38
|
-
},
|
39
|
-
Type2: {
|
40
|
-
SubType1: 4, SubType2: 5, SubType3: 6
|
41
|
-
}
|
42
|
-
},
|
43
|
-
Part2: {
|
44
|
-
Type1: {
|
45
|
-
SubType1: 1, SubType2: 2, SubType3: 3
|
46
|
-
},
|
47
|
-
Type2: {
|
48
|
-
SubType1: 4, SubType2: 5, SubType3: 6
|
49
|
-
}
|
50
|
-
}
|
51
|
-
}
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
#
|
56
|
-
# Shortcut to create a Sheet with example data
|
57
|
-
#
|
58
|
-
|
59
|
-
def self.sample_sheet
|
60
|
-
Workbook.new.load RubyExcel.sample_data
|
61
|
-
end
|
62
|
-
|
63
|
-
#
|
64
|
-
# Shortcut to import a WIN32OLE Workbook or Sheet
|
65
|
-
#
|
66
|
-
|
67
|
-
def self.import( *args )
|
68
|
-
Workbook.new.import( *args )
|
69
|
-
end
|
70
|
-
|
1
|
+
require_relative 'address.rb'
|
2
|
+
require_relative 'data.rb'
|
3
|
+
require_relative 'element.rb'
|
4
|
+
#require_relative 'excel_tools.rb'
|
5
|
+
require_relative 'section.rb'
|
6
|
+
require_relative 'sheet.rb'
|
7
|
+
|
8
|
+
module RubyExcel
|
9
|
+
|
10
|
+
#
|
11
|
+
# Example data to use in tests / demos
|
12
|
+
#
|
13
|
+
|
14
|
+
def self.sample_data
|
15
|
+
[
|
16
|
+
[ 'Part', 'Ref1', 'Ref2', 'Qty', 'Cost' ],
|
17
|
+
[ 'Type1', 'QT1', '231', 1, 35.15 ],
|
18
|
+
[ 'Type2', 'QT3', '123', 1, 40 ],
|
19
|
+
[ 'Type3', 'XT1', '321', 3, 0.1 ],
|
20
|
+
[ 'Type1', 'XY2', '132', 1, 30.00 ],
|
21
|
+
[ 'Type4', 'XT3', '312', 2, 3 ],
|
22
|
+
[ 'Type2', 'QY2', '213', 1, 99.99 ],
|
23
|
+
[ 'Type1', 'QT4', '123', 2, 104 ]
|
24
|
+
]
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Example hash to demonstrate imports
|
30
|
+
#
|
31
|
+
|
32
|
+
def self.sample_hash
|
33
|
+
|
34
|
+
{
|
35
|
+
Part1: {
|
36
|
+
Type1: {
|
37
|
+
SubType1: 1, SubType2: 2, SubType3: 3
|
38
|
+
},
|
39
|
+
Type2: {
|
40
|
+
SubType1: 4, SubType2: 5, SubType3: 6
|
41
|
+
}
|
42
|
+
},
|
43
|
+
Part2: {
|
44
|
+
Type1: {
|
45
|
+
SubType1: 1, SubType2: 2, SubType3: 3
|
46
|
+
},
|
47
|
+
Type2: {
|
48
|
+
SubType1: 4, SubType2: 5, SubType3: 6
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Shortcut to create a Sheet with example data
|
57
|
+
#
|
58
|
+
|
59
|
+
def self.sample_sheet
|
60
|
+
Workbook.new.load RubyExcel.sample_data
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Shortcut to import a WIN32OLE Workbook or Sheet
|
65
|
+
#
|
66
|
+
|
67
|
+
def self.import( *args )
|
68
|
+
Workbook.new.import( *args )
|
69
|
+
end
|
70
|
+
|
71
71
|
end
|
data/lib/rubyexcel/section.rb
CHANGED
@@ -1,377 +1,377 @@
|
|
1
|
-
module RubyExcel
|
2
|
-
|
3
|
-
#
|
4
|
-
# Superclass for Row and Column
|
5
|
-
#
|
6
|
-
|
7
|
-
class Section
|
8
|
-
include Address
|
9
|
-
include Enumerable
|
10
|
-
|
11
|
-
# The Sheet parent of the Section
|
12
|
-
attr_reader :sheet
|
13
|
-
alias parent sheet
|
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
|
-
|
24
|
-
def initialize( sheet )
|
25
|
-
@sheet = sheet
|
26
|
-
@data = sheet.data
|
27
|
-
end
|
28
|
-
|
29
|
-
#
|
30
|
-
# Access a cell by its index within the Section
|
31
|
-
#
|
32
|
-
|
33
|
-
def cell( ref )
|
34
|
-
Cell.new( sheet, translate_address( ref ) )
|
35
|
-
end
|
36
|
-
|
37
|
-
#
|
38
|
-
# Delete the data referenced by self
|
39
|
-
#
|
40
|
-
|
41
|
-
def delete
|
42
|
-
data.delete( self ); self
|
43
|
-
end
|
44
|
-
|
45
|
-
#
|
46
|
-
# Yields each value
|
47
|
-
#
|
48
|
-
|
49
|
-
def each
|
50
|
-
return to_enum(:each) unless block_given?
|
51
|
-
each_address { |addr| yield data[ addr ] }
|
52
|
-
end
|
53
|
-
|
54
|
-
#
|
55
|
-
# Yields each value, skipping headers
|
56
|
-
#
|
57
|
-
|
58
|
-
def each_without_headers
|
59
|
-
return to_enum( :each_without_headers ) unless block_given?
|
60
|
-
each_address( false ) { |addr| yield data[ addr ] }
|
61
|
-
end
|
62
|
-
alias each_wh each_without_headers
|
63
|
-
|
64
|
-
#
|
65
|
-
# Yields each cell
|
66
|
-
#
|
67
|
-
|
68
|
-
def each_cell
|
69
|
-
return to_enum( :each_cell ) unless block_given?
|
70
|
-
each_address { |addr| yield Cell.new( sheet, addr ) }
|
71
|
-
end
|
72
|
-
|
73
|
-
#
|
74
|
-
# Yields each cell, skipping headers
|
75
|
-
#
|
76
|
-
|
77
|
-
def each_cell_without_headers
|
78
|
-
return to_enum( :each_cell_without_headers ) unless block_given?
|
79
|
-
each_address( false ) { |addr| yield Cell.new( sheet, addr ) }
|
80
|
-
end
|
81
|
-
alias each_cell_wh each_cell_without_headers
|
82
|
-
|
83
|
-
#
|
84
|
-
# Check whether the data in self is empty
|
85
|
-
#
|
86
|
-
|
87
|
-
def empty?
|
88
|
-
each_wh.all? { |val| val.to_s.empty? }
|
89
|
-
end
|
90
|
-
|
91
|
-
#
|
92
|
-
# Return the address of a given value
|
93
|
-
#
|
94
|
-
# @yield [Object] yields each cell value to the block
|
95
|
-
# @return [String, nil] the address of the value or nil
|
96
|
-
#
|
97
|
-
|
98
|
-
def find
|
99
|
-
return to_enum( :find ) unless block_given?
|
100
|
-
each_cell { |ce| return ce.address if yield ce.value }; nil
|
101
|
-
end
|
102
|
-
|
103
|
-
#
|
104
|
-
# View the object for debugging
|
105
|
-
#
|
106
|
-
|
107
|
-
def inspect
|
108
|
-
"#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ idx }"
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
# Return the value of the last cell
|
113
|
-
#
|
114
|
-
|
115
|
-
def last
|
116
|
-
last_cell.value
|
117
|
-
end
|
118
|
-
|
119
|
-
#
|
120
|
-
# Return the last cell
|
121
|
-
#
|
122
|
-
# @return [RubyExcel::Cell]
|
123
|
-
#
|
124
|
-
|
125
|
-
def last_cell
|
126
|
-
Cell.new( sheet, each_address.to_a.last )
|
127
|
-
end
|
128
|
-
|
129
|
-
#
|
130
|
-
# Replaces each value with the result of the block
|
131
|
-
#
|
132
|
-
|
133
|
-
def map!
|
134
|
-
return to_enum( :map! ) unless block_given?
|
135
|
-
each_address { |addr| data[addr] = ( yield data[addr] ) }
|
136
|
-
end
|
137
|
-
|
138
|
-
#
|
139
|
-
# Replaces each value with the result of the block, skipping headers
|
140
|
-
#
|
141
|
-
|
142
|
-
def map_without_headers!
|
143
|
-
return to_enum( :map_without_headers! ) unless block_given?
|
144
|
-
each_address( false ) { |addr| data[addr] = ( yield data[addr] ) }
|
145
|
-
end
|
146
|
-
alias map_wh! map_without_headers!
|
147
|
-
|
148
|
-
#
|
149
|
-
# Read a value by address
|
150
|
-
#
|
151
|
-
# @param [String, Fixnum, ::Range] start an index or Range of indices.
|
152
|
-
# @param [Fixnum] slice if the first argument is an index, how many cells to read.
|
153
|
-
#
|
154
|
-
|
155
|
-
def read( start, slice=nil )
|
156
|
-
if slice
|
157
|
-
( start..( step_index( start, slice ) ) ).map { |n| data[ translate_address( n ) ] }
|
158
|
-
else
|
159
|
-
if start.is_a?( ::Range ) # Standard Ruby Range
|
160
|
-
start.map { |n| data[ translate_address( n ) ] }
|
161
|
-
else # Single value
|
162
|
-
data[ translate_address( start ) ]
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
alias [] read
|
167
|
-
|
168
|
-
#
|
169
|
-
# Summarise the values of a Section into a Hash
|
170
|
-
#
|
171
|
-
# @return [Hash]
|
172
|
-
#
|
173
|
-
|
174
|
-
def summarise
|
175
|
-
each_wh.inject( Hash.new(0) ) { |h, v| h[v]+=1; h }
|
176
|
-
end
|
177
|
-
alias summarize summarise
|
178
|
-
|
179
|
-
#
|
180
|
-
# The Section as a seperated value String
|
181
|
-
#
|
182
|
-
|
183
|
-
def to_s
|
184
|
-
to_a.map { |v| v.to_s.gsub(/\t|\n|\r/,' ') }.join ( self.is_a?( Row ) ? "\t" : "\n" )
|
185
|
-
end
|
186
|
-
|
187
|
-
#
|
188
|
-
# Write a value by address
|
189
|
-
#
|
190
|
-
# @param [Array<String, Fixnum, ::Range, Object>] args the address to write the data to, and the data to write.
|
191
|
-
#
|
192
|
-
|
193
|
-
def write( *args )
|
194
|
-
val = args.pop
|
195
|
-
if args.length == 1
|
196
|
-
if args[0].is_a?( ::Range ) # Standard Ruby Range
|
197
|
-
sheet.range( to_range_address( translate_address( args[0].first ), translate_address( args[0].last ) ) ).value = val
|
198
|
-
else # Single value
|
199
|
-
data[ translate_address( args[0] ) ] = val
|
200
|
-
end
|
201
|
-
else # Slice
|
202
|
-
sheet.range( to_range_address( translate_address( args[0] ), translate_address( step_index( args[0], args[1] ) ) ) ).value = val
|
203
|
-
end
|
204
|
-
end
|
205
|
-
alias []= write
|
206
|
-
|
207
|
-
end # Section
|
208
|
-
|
209
|
-
#
|
210
|
-
# A Row in the Sheet
|
211
|
-
# @attr_reader [Fixnum] idx the Row index
|
212
|
-
# @attr_reader [Fixnum] length the Row length
|
213
|
-
#
|
214
|
-
|
215
|
-
class Row < Section
|
216
|
-
|
217
|
-
# The Row number
|
218
|
-
attr_reader :idx
|
219
|
-
alias index idx
|
220
|
-
|
221
|
-
#
|
222
|
-
# Creates a RubyExcel::Row instance
|
223
|
-
#
|
224
|
-
# @param [RubyExcel::Sheet] sheet the Sheet which holds this Row
|
225
|
-
# @param [Fixnum] idx the index of this Row
|
226
|
-
#
|
227
|
-
|
228
|
-
def initialize( sheet, idx )
|
229
|
-
@idx = Integer( idx )
|
230
|
-
super( sheet )
|
231
|
-
end
|
232
|
-
|
233
|
-
#
|
234
|
-
# Append a value to the Row.
|
235
|
-
#
|
236
|
-
# @param [Object] value the object to append
|
237
|
-
# @note This only adds an extra cell if it is the first Row
|
238
|
-
# This prevents a loop through Rows from extending diagonally away from the main data.
|
239
|
-
#
|
240
|
-
|
241
|
-
def <<( value )
|
242
|
-
data[ translate_address( idx == 1 ? data.cols + 1 : data.cols ) ] = value
|
243
|
-
end
|
244
|
-
|
245
|
-
#
|
246
|
-
# Access a Cell by its header
|
247
|
-
#
|
248
|
-
# @param [String] header the header to search for
|
249
|
-
# @return [RubyExcel::Cell] the cell
|
250
|
-
#
|
251
|
-
|
252
|
-
def cell_by_header( header )
|
253
|
-
cell( getref( header ) )
|
254
|
-
end
|
255
|
-
alias cell_h cell_by_header
|
256
|
-
|
257
|
-
#
|
258
|
-
# Find the Address of a header
|
259
|
-
#
|
260
|
-
# @param [String] header the header to search for
|
261
|
-
# @return [String] the address of the header
|
262
|
-
#
|
263
|
-
|
264
|
-
def getref( header )
|
265
|
-
sheet.header_rows.times do |t|
|
266
|
-
res = sheet.row( t + 1 ).find { |v| v == header }
|
267
|
-
return column_id( res ) if res
|
268
|
-
end
|
269
|
-
fail ArgumentError, 'Invalid header: ' + header.to_s
|
270
|
-
end
|
271
|
-
|
272
|
-
#
|
273
|
-
# The number of Columns in the Row
|
274
|
-
#
|
275
|
-
|
276
|
-
def length
|
277
|
-
data.cols
|
278
|
-
end
|
279
|
-
|
280
|
-
#
|
281
|
-
# Find a value in this Row by its header
|
282
|
-
#
|
283
|
-
# @param [String] header the header to search for
|
284
|
-
# @return [Object] the value at the address
|
285
|
-
#
|
286
|
-
|
287
|
-
def value_by_header( header )
|
288
|
-
self[ getref( header ) ]
|
289
|
-
end
|
290
|
-
alias val value_by_header
|
291
|
-
|
292
|
-
#
|
293
|
-
# Set a value in this Row by its header
|
294
|
-
#
|
295
|
-
# @param [String] header the header to search for
|
296
|
-
# @param [Object] val the value to write
|
297
|
-
#
|
298
|
-
|
299
|
-
def set_value_by_header( header, val )
|
300
|
-
self[ getref( header ) ] = val
|
301
|
-
end
|
302
|
-
alias set_val set_value_by_header
|
303
|
-
|
304
|
-
private
|
305
|
-
|
306
|
-
def each_address( unused=nil )
|
307
|
-
return to_enum( :each_address ) unless block_given?
|
308
|
-
( 'A'..col_letter( data.cols ) ).each { |col_id| yield translate_address( col_id ) }
|
309
|
-
end
|
310
|
-
|
311
|
-
def translate_address( addr )
|
312
|
-
col_letter( addr ) + idx.to_s
|
313
|
-
end
|
314
|
-
|
315
|
-
end # Row
|
316
|
-
|
317
|
-
#
|
318
|
-
# A Column in the Sheet
|
319
|
-
#
|
320
|
-
# @attr_reader [String] idx the Column index
|
321
|
-
# @attr_reader [Fixnum] length the Column length
|
322
|
-
#
|
323
|
-
|
324
|
-
class Column < Section
|
325
|
-
|
326
|
-
# The Column letter
|
327
|
-
attr_reader :idx
|
328
|
-
alias index idx
|
329
|
-
|
330
|
-
#
|
331
|
-
# Creates a RubyExcel::Column instance
|
332
|
-
#
|
333
|
-
# @param [RubyExcel::Sheet] sheet the Sheet which holds this Column
|
334
|
-
# @param [String, Fixnum] idx the index of this Column
|
335
|
-
#
|
336
|
-
|
337
|
-
def initialize( sheet, idx )
|
338
|
-
@idx = idx
|
339
|
-
super( sheet )
|
340
|
-
end
|
341
|
-
|
342
|
-
#
|
343
|
-
# Append a value to the Column.
|
344
|
-
#
|
345
|
-
# @param [Object] value the object to append
|
346
|
-
# @note This only adds an extra cell if it is the first Column.
|
347
|
-
# This prevents a loop through Columns from extending diagonally away from the main data.
|
348
|
-
#
|
349
|
-
|
350
|
-
def <<( value )
|
351
|
-
data[ translate_address( idx == 'A' ? data.rows + 1 : data.rows ) ] = value
|
352
|
-
end
|
353
|
-
|
354
|
-
#
|
355
|
-
# The number of Rows in the Column
|
356
|
-
#
|
357
|
-
|
358
|
-
def length
|
359
|
-
data.rows
|
360
|
-
end
|
361
|
-
|
362
|
-
private
|
363
|
-
|
364
|
-
def each_address( headers=true )
|
365
|
-
return to_enum( :each_address ) unless block_given?
|
366
|
-
( headers ? 1 : sheet.header_rows + 1 ).upto( data.rows ) { |row_id| yield translate_address( row_id ) }
|
367
|
-
end
|
368
|
-
|
369
|
-
def translate_address( addr )
|
370
|
-
addr = addr.to_s unless addr.is_a?( String )
|
371
|
-
fail ArgumentError, "Invalid address : #{ addr }" if addr =~ /[^\d]/
|
372
|
-
idx + addr
|
373
|
-
end
|
374
|
-
|
375
|
-
end # Column
|
376
|
-
|
1
|
+
module RubyExcel
|
2
|
+
|
3
|
+
#
|
4
|
+
# Superclass for Row and Column
|
5
|
+
#
|
6
|
+
|
7
|
+
class Section
|
8
|
+
include Address
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# The Sheet parent of the Section
|
12
|
+
attr_reader :sheet
|
13
|
+
alias parent sheet
|
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
|
+
|
24
|
+
def initialize( sheet )
|
25
|
+
@sheet = sheet
|
26
|
+
@data = sheet.data
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Access a cell by its index within the Section
|
31
|
+
#
|
32
|
+
|
33
|
+
def cell( ref )
|
34
|
+
Cell.new( sheet, translate_address( ref ) )
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Delete the data referenced by self
|
39
|
+
#
|
40
|
+
|
41
|
+
def delete
|
42
|
+
data.delete( self ); self
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Yields each value
|
47
|
+
#
|
48
|
+
|
49
|
+
def each
|
50
|
+
return to_enum(:each) unless block_given?
|
51
|
+
each_address { |addr| yield data[ addr ] }
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Yields each value, skipping headers
|
56
|
+
#
|
57
|
+
|
58
|
+
def each_without_headers
|
59
|
+
return to_enum( :each_without_headers ) unless block_given?
|
60
|
+
each_address( false ) { |addr| yield data[ addr ] }
|
61
|
+
end
|
62
|
+
alias each_wh each_without_headers
|
63
|
+
|
64
|
+
#
|
65
|
+
# Yields each cell
|
66
|
+
#
|
67
|
+
|
68
|
+
def each_cell
|
69
|
+
return to_enum( :each_cell ) unless block_given?
|
70
|
+
each_address { |addr| yield Cell.new( sheet, addr ) }
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Yields each cell, skipping headers
|
75
|
+
#
|
76
|
+
|
77
|
+
def each_cell_without_headers
|
78
|
+
return to_enum( :each_cell_without_headers ) unless block_given?
|
79
|
+
each_address( false ) { |addr| yield Cell.new( sheet, addr ) }
|
80
|
+
end
|
81
|
+
alias each_cell_wh each_cell_without_headers
|
82
|
+
|
83
|
+
#
|
84
|
+
# Check whether the data in self is empty
|
85
|
+
#
|
86
|
+
|
87
|
+
def empty?
|
88
|
+
each_wh.all? { |val| val.to_s.empty? }
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Return the address of a given value
|
93
|
+
#
|
94
|
+
# @yield [Object] yields each cell value to the block
|
95
|
+
# @return [String, nil] the address of the value or nil
|
96
|
+
#
|
97
|
+
|
98
|
+
def find
|
99
|
+
return to_enum( :find ) unless block_given?
|
100
|
+
each_cell { |ce| return ce.address if yield ce.value }; nil
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# View the object for debugging
|
105
|
+
#
|
106
|
+
|
107
|
+
def inspect
|
108
|
+
"#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ idx }"
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Return the value of the last cell
|
113
|
+
#
|
114
|
+
|
115
|
+
def last
|
116
|
+
last_cell.value
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Return the last cell
|
121
|
+
#
|
122
|
+
# @return [RubyExcel::Cell]
|
123
|
+
#
|
124
|
+
|
125
|
+
def last_cell
|
126
|
+
Cell.new( sheet, each_address.to_a.last )
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Replaces each value with the result of the block
|
131
|
+
#
|
132
|
+
|
133
|
+
def map!
|
134
|
+
return to_enum( :map! ) unless block_given?
|
135
|
+
each_address { |addr| data[addr] = ( yield data[addr] ) }
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Replaces each value with the result of the block, skipping headers
|
140
|
+
#
|
141
|
+
|
142
|
+
def map_without_headers!
|
143
|
+
return to_enum( :map_without_headers! ) unless block_given?
|
144
|
+
each_address( false ) { |addr| data[addr] = ( yield data[addr] ) }
|
145
|
+
end
|
146
|
+
alias map_wh! map_without_headers!
|
147
|
+
|
148
|
+
#
|
149
|
+
# Read a value by address
|
150
|
+
#
|
151
|
+
# @param [String, Fixnum, ::Range] start an index or Range of indices.
|
152
|
+
# @param [Fixnum] slice if the first argument is an index, how many cells to read.
|
153
|
+
#
|
154
|
+
|
155
|
+
def read( start, slice=nil )
|
156
|
+
if slice
|
157
|
+
( start..( step_index( start, slice ) ) ).map { |n| data[ translate_address( n ) ] }
|
158
|
+
else
|
159
|
+
if start.is_a?( ::Range ) # Standard Ruby Range
|
160
|
+
start.map { |n| data[ translate_address( n ) ] }
|
161
|
+
else # Single value
|
162
|
+
data[ translate_address( start ) ]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
alias [] read
|
167
|
+
|
168
|
+
#
|
169
|
+
# Summarise the values of a Section into a Hash
|
170
|
+
#
|
171
|
+
# @return [Hash]
|
172
|
+
#
|
173
|
+
|
174
|
+
def summarise
|
175
|
+
each_wh.inject( Hash.new(0) ) { |h, v| h[v]+=1; h }
|
176
|
+
end
|
177
|
+
alias summarize summarise
|
178
|
+
|
179
|
+
#
|
180
|
+
# The Section as a seperated value String
|
181
|
+
#
|
182
|
+
|
183
|
+
def to_s
|
184
|
+
to_a.map { |v| v.to_s.gsub(/\t|\n|\r/,' ') }.join ( self.is_a?( Row ) ? "\t" : "\n" )
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
# Write a value by address
|
189
|
+
#
|
190
|
+
# @param [Array<String, Fixnum, ::Range, Object>] args the address to write the data to, and the data to write.
|
191
|
+
#
|
192
|
+
|
193
|
+
def write( *args )
|
194
|
+
val = args.pop
|
195
|
+
if args.length == 1
|
196
|
+
if args[0].is_a?( ::Range ) # Standard Ruby Range
|
197
|
+
sheet.range( to_range_address( translate_address( args[0].first ), translate_address( args[0].last ) ) ).value = val
|
198
|
+
else # Single value
|
199
|
+
data[ translate_address( args[0] ) ] = val
|
200
|
+
end
|
201
|
+
else # Slice
|
202
|
+
sheet.range( to_range_address( translate_address( args[0] ), translate_address( step_index( args[0], args[1] ) ) ) ).value = val
|
203
|
+
end
|
204
|
+
end
|
205
|
+
alias []= write
|
206
|
+
|
207
|
+
end # Section
|
208
|
+
|
209
|
+
#
|
210
|
+
# A Row in the Sheet
|
211
|
+
# @attr_reader [Fixnum] idx the Row index
|
212
|
+
# @attr_reader [Fixnum] length the Row length
|
213
|
+
#
|
214
|
+
|
215
|
+
class Row < Section
|
216
|
+
|
217
|
+
# The Row number
|
218
|
+
attr_reader :idx
|
219
|
+
alias index idx
|
220
|
+
|
221
|
+
#
|
222
|
+
# Creates a RubyExcel::Row instance
|
223
|
+
#
|
224
|
+
# @param [RubyExcel::Sheet] sheet the Sheet which holds this Row
|
225
|
+
# @param [Fixnum] idx the index of this Row
|
226
|
+
#
|
227
|
+
|
228
|
+
def initialize( sheet, idx )
|
229
|
+
@idx = Integer( idx )
|
230
|
+
super( sheet )
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# Append a value to the Row.
|
235
|
+
#
|
236
|
+
# @param [Object] value the object to append
|
237
|
+
# @note This only adds an extra cell if it is the first Row
|
238
|
+
# This prevents a loop through Rows from extending diagonally away from the main data.
|
239
|
+
#
|
240
|
+
|
241
|
+
def <<( value )
|
242
|
+
data[ translate_address( idx == 1 ? data.cols + 1 : data.cols ) ] = value
|
243
|
+
end
|
244
|
+
|
245
|
+
#
|
246
|
+
# Access a Cell by its header
|
247
|
+
#
|
248
|
+
# @param [String] header the header to search for
|
249
|
+
# @return [RubyExcel::Cell] the cell
|
250
|
+
#
|
251
|
+
|
252
|
+
def cell_by_header( header )
|
253
|
+
cell( getref( header ) )
|
254
|
+
end
|
255
|
+
alias cell_h cell_by_header
|
256
|
+
|
257
|
+
#
|
258
|
+
# Find the Address of a header
|
259
|
+
#
|
260
|
+
# @param [String] header the header to search for
|
261
|
+
# @return [String] the address of the header
|
262
|
+
#
|
263
|
+
|
264
|
+
def getref( header )
|
265
|
+
sheet.header_rows.times do |t|
|
266
|
+
res = sheet.row( t + 1 ).find { |v| v == header }
|
267
|
+
return column_id( res ) if res
|
268
|
+
end
|
269
|
+
fail ArgumentError, 'Invalid header: ' + header.to_s
|
270
|
+
end
|
271
|
+
|
272
|
+
#
|
273
|
+
# The number of Columns in the Row
|
274
|
+
#
|
275
|
+
|
276
|
+
def length
|
277
|
+
data.cols
|
278
|
+
end
|
279
|
+
|
280
|
+
#
|
281
|
+
# Find a value in this Row by its header
|
282
|
+
#
|
283
|
+
# @param [String] header the header to search for
|
284
|
+
# @return [Object] the value at the address
|
285
|
+
#
|
286
|
+
|
287
|
+
def value_by_header( header )
|
288
|
+
self[ getref( header ) ]
|
289
|
+
end
|
290
|
+
alias val value_by_header
|
291
|
+
|
292
|
+
#
|
293
|
+
# Set a value in this Row by its header
|
294
|
+
#
|
295
|
+
# @param [String] header the header to search for
|
296
|
+
# @param [Object] val the value to write
|
297
|
+
#
|
298
|
+
|
299
|
+
def set_value_by_header( header, val )
|
300
|
+
self[ getref( header ) ] = val
|
301
|
+
end
|
302
|
+
alias set_val set_value_by_header
|
303
|
+
|
304
|
+
private
|
305
|
+
|
306
|
+
def each_address( unused=nil )
|
307
|
+
return to_enum( :each_address ) unless block_given?
|
308
|
+
( 'A'..col_letter( data.cols ) ).each { |col_id| yield translate_address( col_id ) }
|
309
|
+
end
|
310
|
+
|
311
|
+
def translate_address( addr )
|
312
|
+
col_letter( addr ) + idx.to_s
|
313
|
+
end
|
314
|
+
|
315
|
+
end # Row
|
316
|
+
|
317
|
+
#
|
318
|
+
# A Column in the Sheet
|
319
|
+
#
|
320
|
+
# @attr_reader [String] idx the Column index
|
321
|
+
# @attr_reader [Fixnum] length the Column length
|
322
|
+
#
|
323
|
+
|
324
|
+
class Column < Section
|
325
|
+
|
326
|
+
# The Column letter
|
327
|
+
attr_reader :idx
|
328
|
+
alias index idx
|
329
|
+
|
330
|
+
#
|
331
|
+
# Creates a RubyExcel::Column instance
|
332
|
+
#
|
333
|
+
# @param [RubyExcel::Sheet] sheet the Sheet which holds this Column
|
334
|
+
# @param [String, Fixnum] idx the index of this Column
|
335
|
+
#
|
336
|
+
|
337
|
+
def initialize( sheet, idx )
|
338
|
+
@idx = idx
|
339
|
+
super( sheet )
|
340
|
+
end
|
341
|
+
|
342
|
+
#
|
343
|
+
# Append a value to the Column.
|
344
|
+
#
|
345
|
+
# @param [Object] value the object to append
|
346
|
+
# @note This only adds an extra cell if it is the first Column.
|
347
|
+
# This prevents a loop through Columns from extending diagonally away from the main data.
|
348
|
+
#
|
349
|
+
|
350
|
+
def <<( value )
|
351
|
+
data[ translate_address( idx == 'A' ? data.rows + 1 : data.rows ) ] = value
|
352
|
+
end
|
353
|
+
|
354
|
+
#
|
355
|
+
# The number of Rows in the Column
|
356
|
+
#
|
357
|
+
|
358
|
+
def length
|
359
|
+
data.rows
|
360
|
+
end
|
361
|
+
|
362
|
+
private
|
363
|
+
|
364
|
+
def each_address( headers=true )
|
365
|
+
return to_enum( :each_address ) unless block_given?
|
366
|
+
( headers ? 1 : sheet.header_rows + 1 ).upto( data.rows ) { |row_id| yield translate_address( row_id ) }
|
367
|
+
end
|
368
|
+
|
369
|
+
def translate_address( addr )
|
370
|
+
addr = addr.to_s unless addr.is_a?( String )
|
371
|
+
fail ArgumentError, "Invalid address : #{ addr }" if addr =~ /[^\d]/
|
372
|
+
idx + addr
|
373
|
+
end
|
374
|
+
|
375
|
+
end # Column
|
376
|
+
|
377
377
|
end # RubyExcel
|