rubyexcel 0.0.1
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/excel_tools.rb +54 -0
- data/lib/rubyexcel/rubyexcel_advanced.rb +164 -0
- data/lib/rubyexcel/rubyexcel_components.rb +348 -0
- data/lib/rubyexcel.rb +230 -0
- metadata +49 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
module Excel_Tools
|
3
|
+
|
4
|
+
require 'win32ole'
|
5
|
+
|
6
|
+
def get_excel
|
7
|
+
excel = WIN32OLE::connect( 'excel.application' ) rescue WIN32OLE::new( 'excel.application' )
|
8
|
+
excel.visible = true
|
9
|
+
excel
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_workbook( excel=nil )
|
13
|
+
excel ||= get_excel
|
14
|
+
wb = excel.workbooks.add
|
15
|
+
( ( wb.sheets.count.to_i ) - 1 ).times { |time| wb.sheets(2).delete }
|
16
|
+
wb
|
17
|
+
end
|
18
|
+
|
19
|
+
def dump_to_sheet( data, sheet=nil )
|
20
|
+
fail ArgumentError, "Invalid data type: #{ data.class }" unless data.is_a?( Array ) || data.is_a?( RubyExcel )
|
21
|
+
data = data.to_a if data.is_a? RubyExcel
|
22
|
+
sheet ||= get_workbook.sheets(1)
|
23
|
+
sheet.range( sheet.cells( 1, 1 ), sheet.cells( data.length, data[0].length ) ).value = data
|
24
|
+
sheet
|
25
|
+
end
|
26
|
+
|
27
|
+
def make_sheet_pretty( sheet )
|
28
|
+
c = sheet.cells
|
29
|
+
c.EntireColumn.AutoFit
|
30
|
+
c.HorizontalAlignment = -4108
|
31
|
+
c.VerticalAlignment = -4108
|
32
|
+
sheet
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_excel
|
36
|
+
fail NoMethodError, 'Duplicate sheet name' if self.sheets.count != self.sheets.map(&:name).uniq.length
|
37
|
+
wb = get_workbook
|
38
|
+
wb.parent.DisplayAlerts = false
|
39
|
+
first_time = true
|
40
|
+
self.each do |s|
|
41
|
+
sht = ( first_time ? wb.sheets(1) : wb.sheets.add( { 'after' => wb.sheets( wb.sheets.count ) } ) ); first_time = false
|
42
|
+
sht.name = s.name
|
43
|
+
make_sheet_pretty( dump_to_sheet( s.data.all, sht ) )
|
44
|
+
end
|
45
|
+
wb.sheets(1).select
|
46
|
+
wb
|
47
|
+
end
|
48
|
+
|
49
|
+
def save_excel( filename = 'Output.xlsx' )
|
50
|
+
filename = Dir.pwd.gsub('/','\\') + '\\' + filename unless filename.include?('\\')
|
51
|
+
to_excel.saveas filename
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module RubyExcel
|
2
|
+
|
3
|
+
def self.sample_data
|
4
|
+
a=[];8.times{|t|b=[];c='A';5.times{b<<"#{c}#{t+1}";c.next!};a<<b};a
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.sample_sheet
|
8
|
+
Workbook.new.load RubyExcel.sample_data
|
9
|
+
end
|
10
|
+
|
11
|
+
class Sheet
|
12
|
+
|
13
|
+
def +( other )
|
14
|
+
dup << other
|
15
|
+
end
|
16
|
+
|
17
|
+
def -( other )
|
18
|
+
case other
|
19
|
+
when Array
|
20
|
+
Workbook.new.load( data.all - other )
|
21
|
+
when Sheet
|
22
|
+
Workbook.new.load( data.all - other.data.no_headers )
|
23
|
+
else
|
24
|
+
fail ArgumentError, "Unsupported class: #{ other.class }"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def <<( other )
|
29
|
+
case other
|
30
|
+
when Array
|
31
|
+
load( data.all + other, header_rows, header_cols )
|
32
|
+
when Sheet
|
33
|
+
load( data.all + other.data.no_headers, header_rows, header_cols )
|
34
|
+
else
|
35
|
+
fail ArgumentError, "Unsupported class: #{ other.class }"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def compact!
|
40
|
+
data.compact!
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def empty?
|
45
|
+
data.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def column_by_header( header )
|
49
|
+
Column.new( self, data.colref_by_header( header ) )
|
50
|
+
end
|
51
|
+
alias ch column_by_header
|
52
|
+
|
53
|
+
def filter!( ref, &block )
|
54
|
+
data.filter!( ref, &block )
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def sumif( find_header, sum_header )
|
59
|
+
col1 = column_by_header( find_header )
|
60
|
+
col2 = column_by_header( sum_header )
|
61
|
+
total = 0
|
62
|
+
col1.each.with_index do |val,idx|
|
63
|
+
total += ( ( n = col2[ idx + 1 ] ).is_a?( String ) ? n.to_i : n ) if yield( val ) && idx >= header_rows
|
64
|
+
end
|
65
|
+
total
|
66
|
+
end
|
67
|
+
|
68
|
+
def uniq!( header )
|
69
|
+
data.uniq!( header )
|
70
|
+
self
|
71
|
+
end
|
72
|
+
alias unique! uniq!
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
class Data
|
77
|
+
|
78
|
+
def delete( object )
|
79
|
+
case object
|
80
|
+
when Row
|
81
|
+
@data.slice!( object.idx - 1 )
|
82
|
+
when Column
|
83
|
+
idx = col_index( object.idx ) - 1
|
84
|
+
@data.each { |r| r.slice! idx }
|
85
|
+
when Element
|
86
|
+
addresses = expand( object.address )
|
87
|
+
indices = [ address_to_indices( addresses.first.first ), address_to_indices( addresses.last.last ) ].flatten.map { |n| n-1 }
|
88
|
+
@data[ indices[0]..indices[2] ].each { |r| r.slice!( indices[1], indices[3] - indices[1] + 1 ) }
|
89
|
+
@data.delete_if.with_index { |r,i| r.empty? && i.between?( indices[0], indices[2] ) }
|
90
|
+
else
|
91
|
+
fail NoMethodError, "#{ object.class } is not supported"
|
92
|
+
end
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def delete_column( ref )
|
97
|
+
delete( Column.new( sheet, ref ) )
|
98
|
+
end
|
99
|
+
|
100
|
+
def delete_row( ref )
|
101
|
+
delete( Row.new( sheet, ref ) )
|
102
|
+
end
|
103
|
+
|
104
|
+
def delete_range( ref )
|
105
|
+
delete( Element.new( sheet, ref ) )
|
106
|
+
end
|
107
|
+
|
108
|
+
def filter!( header )
|
109
|
+
hrows = sheet.header_rows
|
110
|
+
idx = col_index( hrows > 0 ? colref_by_header( header ) : header )
|
111
|
+
@data = @data.select.with_index { |row, i| hrows > i || yield( row[ idx -1 ] ) }
|
112
|
+
calc_dimensions
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
def get_columns!( *headers )
|
117
|
+
hrow = sheet.header_rows - 1
|
118
|
+
ensure_shape
|
119
|
+
@data = @data.transpose.select{ |col| headers.include?( col[hrow] ) }
|
120
|
+
ensure_shape
|
121
|
+
@data = @data.sort_by{ |col| headers.index( col[hrow] ) || col[hrow] }.transpose
|
122
|
+
calc_dimensions
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def uniq!( header )
|
127
|
+
column = col_index( colref_by_header( header ) )
|
128
|
+
@data = @data.uniq { |row| row[ column - 1 ] }
|
129
|
+
self
|
130
|
+
end
|
131
|
+
alias unique! uniq!
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
class Section
|
136
|
+
|
137
|
+
def <<( value )
|
138
|
+
if self.is_a? Row
|
139
|
+
lastone = ( col_index( idx ) == 1 ? data.cols + 1 : data.cols )
|
140
|
+
else
|
141
|
+
lastone = ( col_index( idx ) == 1 ? data.rows + 1 : data.rows )
|
142
|
+
end
|
143
|
+
data[ translate_address( lastone ) ] = value
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
class Row < Section
|
149
|
+
|
150
|
+
def getref( header )
|
151
|
+
column_id( sheet.row(1).find &/#{header}/ )
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
class Column < Section
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
class Element
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
@@ -0,0 +1,348 @@
|
|
1
|
+
module RubyExcel
|
2
|
+
|
3
|
+
module Address
|
4
|
+
|
5
|
+
def address_to_col_index( address )
|
6
|
+
col_index( column_id( address ) )
|
7
|
+
end
|
8
|
+
|
9
|
+
def address_to_indices( address )
|
10
|
+
[ row_id( address ), address_to_col_index( address ) ]
|
11
|
+
end
|
12
|
+
|
13
|
+
def col_index( letter )
|
14
|
+
return letter if letter.is_a? Fixnum
|
15
|
+
letter !~ /[^A-Z]/ && [1,2,3].include?( letter.length ) or fail ArgumentError, "Invalid column reference: #{ letter }"
|
16
|
+
idx, a = 1, 'A'
|
17
|
+
loop { return idx if a == letter; idx+=1; a.next! }
|
18
|
+
end
|
19
|
+
|
20
|
+
def col_letter( index )
|
21
|
+
return index if index.is_a? String
|
22
|
+
index > 0 or fail ArgumentError, 'Indexing is 1-based'
|
23
|
+
a = 'A' ; return a if index == 1
|
24
|
+
(index - 1).times{ a.next! }; a
|
25
|
+
end
|
26
|
+
|
27
|
+
def column_id( address )
|
28
|
+
address[/[A-Z]+/] or fail ArgumentError, "Invalid address: #{ address }"
|
29
|
+
end
|
30
|
+
|
31
|
+
def expand( address )
|
32
|
+
return [[address]] unless address.include? ':'
|
33
|
+
start_col, end_col, start_row, end_row = [ address[/^[A-Z]+/], address[/(?<=:)[A-Z]+/] ].sort + [ address.match(/(\d+):/).captures.first, address[/\d+$/] ].sort
|
34
|
+
(start_row..end_row).map { |r| (start_col..end_col).map { |c| "#{ c }#{ r }" } }
|
35
|
+
end
|
36
|
+
|
37
|
+
def indices_to_address( row_idx, column_idx )
|
38
|
+
[ row_idx, column_idx ].all? { |a| a.is_a?( Fixnum ) } or fail ArgumentError, 'Input must be Fixnum'
|
39
|
+
col_letter( column_idx ) + row_idx.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def multi_array?( obj )
|
43
|
+
obj.all? { |el| el.is_a?( Array ) } && obj.is_a?( Array ) rescue false
|
44
|
+
end
|
45
|
+
|
46
|
+
def offset(address, row, col)
|
47
|
+
( col_letter( address_to_col_index( address ) + col ) ) + ( row_id( address ) + row ).to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_range_address( obj1, obj2 )
|
51
|
+
if obj2
|
52
|
+
obj2.is_a?( String ) ? ( obj1 + ':' + obj2 ) : "#{ obj1.address }:#{ obj2.address }"
|
53
|
+
else
|
54
|
+
obj1.is_a?( String ) ? obj1 : obj1.address
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def row_id( address )
|
59
|
+
return nil unless address
|
60
|
+
address[/\d+/].to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class Data
|
66
|
+
attr_reader :rows, :cols
|
67
|
+
attr_accessor :sheet
|
68
|
+
alias parent sheet
|
69
|
+
|
70
|
+
include Address
|
71
|
+
|
72
|
+
def initialize( sheet, input_data )
|
73
|
+
@sheet = sheet
|
74
|
+
( input_data.kind_of?( Array ) && input_data.all? { |el| el.kind_of?( Array ) } ) or fail ArgumentError, 'Input must be Array of Arrays'
|
75
|
+
@data = input_data.dup
|
76
|
+
calc_dimensions
|
77
|
+
end
|
78
|
+
|
79
|
+
def all
|
80
|
+
@data.dup
|
81
|
+
end
|
82
|
+
|
83
|
+
def append( multi_array )
|
84
|
+
@data
|
85
|
+
@data += multi_array
|
86
|
+
calc_dimensions
|
87
|
+
end
|
88
|
+
|
89
|
+
def colref_by_header( header )
|
90
|
+
sheet.header_rows > 0 or fail NoMethodError, 'No header rows present'
|
91
|
+
@data[ 0..sheet.header_rows-1 ].each do |r|
|
92
|
+
if ( idx = r.index( header ) )
|
93
|
+
return col_letter( idx+1 )
|
94
|
+
end
|
95
|
+
end
|
96
|
+
fail IndexError, "#{ header } is not a valid header"
|
97
|
+
end
|
98
|
+
|
99
|
+
def compact!
|
100
|
+
compact_columns!
|
101
|
+
compact_rows!
|
102
|
+
end
|
103
|
+
|
104
|
+
def compact_columns!
|
105
|
+
ensure_shape
|
106
|
+
@data = @data.transpose.delete_if { |ar| ar.all? { |el| el.to_s.empty? } || ar.empty? }.transpose
|
107
|
+
calc_dimensions
|
108
|
+
@data
|
109
|
+
end
|
110
|
+
|
111
|
+
def compact_rows!
|
112
|
+
@data.delete_if { |ar| ar.all? { |el| el.to_s.empty? } || ar.empty? }
|
113
|
+
calc_dimensions
|
114
|
+
@data
|
115
|
+
end
|
116
|
+
|
117
|
+
def dup
|
118
|
+
Data.new( sheet, @data.dup )
|
119
|
+
end
|
120
|
+
|
121
|
+
def empty?
|
122
|
+
no_headers.empty?
|
123
|
+
end
|
124
|
+
|
125
|
+
def insert_columns( before, number=1 )
|
126
|
+
a = Array.new( number, nil )
|
127
|
+
before = col_index( before ) - 1
|
128
|
+
@data.map! { |row| row.insert( before, *a ) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def insert_rows( before, number=1 )
|
132
|
+
@data = @data.insert( ( col_index( before ) - 1 ), *Array.new( number, [nil] ) )
|
133
|
+
end
|
134
|
+
|
135
|
+
def no_headers
|
136
|
+
if sheet.header_cols.zero?
|
137
|
+
@data[ sheet.header_rows..-1 ].dup
|
138
|
+
else
|
139
|
+
@data[ sheet.header_rows..-1 ].map { |row| row[ sheet.header_cols..-1 ] }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def read( addr )
|
144
|
+
row_idx, col_idx = address_to_indices( addr )
|
145
|
+
@data[ row_idx-1 ][ col_idx-1 ]
|
146
|
+
end
|
147
|
+
alias [] read
|
148
|
+
|
149
|
+
def write( addr, val )
|
150
|
+
row_idx, col_idx = address_to_indices( addr )
|
151
|
+
( row_idx - rows ).times { @data << [] }
|
152
|
+
@data[ row_idx-1 ][ col_idx-1 ] = val
|
153
|
+
calc_dimensions
|
154
|
+
end
|
155
|
+
alias []= write
|
156
|
+
|
157
|
+
include Enumerable
|
158
|
+
|
159
|
+
def each
|
160
|
+
@data.each { |ar| yield ar }
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def calc_dimensions
|
166
|
+
@rows, @cols = @data.length, @data.max_by(&:length).length
|
167
|
+
end
|
168
|
+
|
169
|
+
def ensure_shape
|
170
|
+
calc_dimensions
|
171
|
+
@data.map! { |ar| ar.length == cols ? ar : ar + Array.new( cols - ar.length, nil) }
|
172
|
+
@data
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
class Section
|
178
|
+
|
179
|
+
include Address
|
180
|
+
|
181
|
+
attr_reader :sheet, :idx, :data
|
182
|
+
alias parent sheet
|
183
|
+
|
184
|
+
def initialize( sheet )
|
185
|
+
@sheet = sheet
|
186
|
+
@data = sheet.data
|
187
|
+
end
|
188
|
+
|
189
|
+
def delete
|
190
|
+
data.delete( self )
|
191
|
+
end
|
192
|
+
|
193
|
+
def empty?
|
194
|
+
all? { |val| val.to_s.empty? }
|
195
|
+
end
|
196
|
+
|
197
|
+
def find
|
198
|
+
each.with_index { |val,i| return translate_address( i + 1 ) if yield val }; nil
|
199
|
+
end
|
200
|
+
|
201
|
+
def inspect
|
202
|
+
"#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ idx }"
|
203
|
+
end
|
204
|
+
|
205
|
+
def read( id )
|
206
|
+
data[ translate_address( id ) ]
|
207
|
+
end
|
208
|
+
alias [] read
|
209
|
+
|
210
|
+
def to_s
|
211
|
+
to_a.join ( self.is_a?( Row ) ? "\t" : "\n" )
|
212
|
+
end
|
213
|
+
|
214
|
+
def write( id, val )
|
215
|
+
data[ translate_address( id ) ] = val
|
216
|
+
end
|
217
|
+
alias []= write
|
218
|
+
|
219
|
+
include Enumerable
|
220
|
+
|
221
|
+
def each
|
222
|
+
return to_enum(:each) unless block_given?
|
223
|
+
each_address { |addr| yield data[ addr ] }
|
224
|
+
end
|
225
|
+
|
226
|
+
def each_cell
|
227
|
+
return to_enum(:each_cell) unless block_given?
|
228
|
+
each_address { |addr| yield Element.new( sheet, addr ) }
|
229
|
+
end
|
230
|
+
|
231
|
+
def map!
|
232
|
+
return to_enum(:map!) unless block_given?
|
233
|
+
each_address { |addr| data[addr] = ( yield data[addr] ) }
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
|
238
|
+
def translate_address( addr )
|
239
|
+
case self
|
240
|
+
when Row
|
241
|
+
col_letter( addr ) + idx.to_s
|
242
|
+
when Column
|
243
|
+
addr = addr.to_s unless addr.is_a?( String )
|
244
|
+
fail ArgumentError, "Invalid address : #{ addr }" if addr =~ /[^\d]/
|
245
|
+
idx + addr
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
class Row < Section
|
252
|
+
|
253
|
+
attr_reader :idx
|
254
|
+
|
255
|
+
def initialize( sheet, idx )
|
256
|
+
@idx = idx.to_i
|
257
|
+
super( sheet )
|
258
|
+
end
|
259
|
+
|
260
|
+
private
|
261
|
+
|
262
|
+
def each_address
|
263
|
+
( 'A'..col_letter( data.cols ) ).each { |col_id| yield "#{col_id}#{idx}" }
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
class Column < Section
|
269
|
+
|
270
|
+
attr_reader :idx
|
271
|
+
|
272
|
+
def initialize( sheet, idx )
|
273
|
+
@idx = idx
|
274
|
+
super( sheet )
|
275
|
+
end
|
276
|
+
|
277
|
+
private
|
278
|
+
|
279
|
+
def each_address
|
280
|
+
1.upto( data.rows ) { |row_id| yield idx + row_id.to_s }
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
class Element
|
286
|
+
|
287
|
+
attr_reader :sheet, :address, :data
|
288
|
+
alias parent sheet
|
289
|
+
|
290
|
+
def initialize( sheet, addr )
|
291
|
+
fail ArgumentError, "Invalid range: #{ addr }" unless addr =~ /\A[A-Z]+\d+:[A-Z]+\d+\z|\A[A-Z]+\d+\z/
|
292
|
+
@sheet = sheet
|
293
|
+
@data = sheet.data
|
294
|
+
@address = addr
|
295
|
+
end
|
296
|
+
|
297
|
+
def delete
|
298
|
+
data.delete( self )
|
299
|
+
end
|
300
|
+
|
301
|
+
include Address
|
302
|
+
|
303
|
+
def value
|
304
|
+
if address.include? ':'
|
305
|
+
expand( address ).map { |ar| ar.map { |addr| data[ addr ] } }
|
306
|
+
else
|
307
|
+
data[ address ]
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def value=( val )
|
312
|
+
if address.include? ':'
|
313
|
+
if multi_array?( val )
|
314
|
+
expand( address ).each_with_index { |row,idx| row.each_with_index { |el,i| data[ el ] = val[idx][i] } }
|
315
|
+
else
|
316
|
+
expand( address ).each { |ar| ar.each { |addr| data[ addr ] = val } }
|
317
|
+
end
|
318
|
+
else
|
319
|
+
data[ address ] = val
|
320
|
+
end
|
321
|
+
self
|
322
|
+
end
|
323
|
+
|
324
|
+
def to_s
|
325
|
+
value.is_a?( Array ) ? value.map { |ar| ar.join "\t" }.join($/) : value.to_s
|
326
|
+
end
|
327
|
+
|
328
|
+
def inspect
|
329
|
+
"#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ address }"
|
330
|
+
end
|
331
|
+
|
332
|
+
include Enumerable
|
333
|
+
|
334
|
+
def each
|
335
|
+
expand( address ).flatten.each { |addr| yield data[ addr ] }
|
336
|
+
end
|
337
|
+
|
338
|
+
def each_cell
|
339
|
+
expand( address ).flatten.each { |addr| yield Element.new( sheet, addr ) }
|
340
|
+
end
|
341
|
+
|
342
|
+
def map!
|
343
|
+
expand( address ).flatten.each { |addr| data[ addr ] = yield data[ addr ] }
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
data/lib/rubyexcel.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
require_relative 'rubyexcel/rubyexcel_components.rb'
|
2
|
+
require_relative 'rubyexcel/rubyexcel_advanced.rb'
|
3
|
+
require_relative 'rubyexcel/excel_tools.rb'
|
4
|
+
|
5
|
+
class Regexp
|
6
|
+
def to_proc
|
7
|
+
proc { |s| self =~ s.to_s }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module RubyExcel
|
12
|
+
|
13
|
+
class Workbook
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@sheets = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def <<( other )
|
20
|
+
case other
|
21
|
+
when RubyExcel
|
22
|
+
other.each { |s| @sheets << s }
|
23
|
+
when Sheet
|
24
|
+
@sheets << other
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def add( ref=nil )
|
29
|
+
case ref
|
30
|
+
when nil
|
31
|
+
s = Sheet.new( 'Sheet' + ( @sheets.count + 1 ).to_s, self )
|
32
|
+
when Sheet
|
33
|
+
s = ref
|
34
|
+
s.workbook = self
|
35
|
+
when String
|
36
|
+
s = Sheet.new( ref, self )
|
37
|
+
else
|
38
|
+
fail TypeError, "Unsupported Type: #{ ref.class }"
|
39
|
+
end
|
40
|
+
@sheets << s
|
41
|
+
s
|
42
|
+
end
|
43
|
+
alias add_sheet add
|
44
|
+
|
45
|
+
def clear_all
|
46
|
+
@sheets = []
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete( ref )
|
51
|
+
case ref
|
52
|
+
when Fixnum
|
53
|
+
@sheets.delete_at( ref - 1 )
|
54
|
+
when String
|
55
|
+
@sheets.reject! { |s| s.name == ref }
|
56
|
+
when Regexp
|
57
|
+
@sheets.reject! { |s| s.name =~ ref }
|
58
|
+
when Sheet
|
59
|
+
@sheets.reject! { |s| s == ref }
|
60
|
+
else
|
61
|
+
fail ArgumentError, "Unrecognised Argument Type: #{ ref.class }"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def dup
|
66
|
+
wb = Workbook.new
|
67
|
+
self.each {|s| wb.add s.dup }
|
68
|
+
wb
|
69
|
+
end
|
70
|
+
|
71
|
+
def empty?
|
72
|
+
@sheets.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def load( *args )
|
76
|
+
add.load( *args )
|
77
|
+
end
|
78
|
+
|
79
|
+
def sheets( ref=nil )
|
80
|
+
return to_enum (:each) if ref.nil?
|
81
|
+
ref.is_a?( Fixnum ) ? @sheets[ ref - 1 ] : @sheets.find { |s| s.name =~ /^#{ ref }$/i }
|
82
|
+
end
|
83
|
+
|
84
|
+
def sort_by!( &block )
|
85
|
+
if block_given?
|
86
|
+
@sheets = @sheets.sort_by(&block)
|
87
|
+
else
|
88
|
+
@sheets = @sheets.sort_by(&:name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
alias sort! sort_by!
|
92
|
+
|
93
|
+
include Enumerable
|
94
|
+
|
95
|
+
def each
|
96
|
+
return to_enum(:each) unless block_given?
|
97
|
+
@sheets.each { |s| yield s }
|
98
|
+
end
|
99
|
+
|
100
|
+
include Excel_Tools
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
class Sheet
|
105
|
+
|
106
|
+
attr_reader :data
|
107
|
+
attr_accessor :name, :header_rows, :header_cols, :workbook
|
108
|
+
alias parent workbook
|
109
|
+
|
110
|
+
include Address
|
111
|
+
|
112
|
+
def initialize( name, workbook )
|
113
|
+
@workbook = workbook
|
114
|
+
@name = name
|
115
|
+
@header_rows, @header_cols = nil, nil
|
116
|
+
@data = Data.new( self, [[]] )
|
117
|
+
end
|
118
|
+
|
119
|
+
def[]( addr )
|
120
|
+
range( addr ).value
|
121
|
+
end
|
122
|
+
|
123
|
+
def []=( addr, val )
|
124
|
+
range( addr ).value = val
|
125
|
+
end
|
126
|
+
|
127
|
+
def cell( row, col )
|
128
|
+
Element.new( self, indices_to_address( row, col ) )
|
129
|
+
end
|
130
|
+
alias cells cell
|
131
|
+
|
132
|
+
def column( index )
|
133
|
+
Column.new( self, col_letter( index ) )
|
134
|
+
end
|
135
|
+
|
136
|
+
def columns( start_column = 'A', end_column = data.cols )
|
137
|
+
start_column, end_column = col_letter( start_column ), col_letter( end_column )
|
138
|
+
return to_enum(:columns, start_column, end_column) unless block_given?
|
139
|
+
( start_column..end_column ).each { |idx| yield column( idx ) }
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_columns( *headers )
|
143
|
+
s = dup
|
144
|
+
s.data.get_columns!( *headers )
|
145
|
+
s
|
146
|
+
end
|
147
|
+
alias gc get_columns
|
148
|
+
|
149
|
+
def get_columns!( *headers )
|
150
|
+
data.get_columns!( *headers )
|
151
|
+
self
|
152
|
+
end
|
153
|
+
alias gc! get_columns!
|
154
|
+
|
155
|
+
def delete
|
156
|
+
workbook.delete self
|
157
|
+
end
|
158
|
+
|
159
|
+
def dup
|
160
|
+
s = Sheet.new( name, workbook )
|
161
|
+
d = data
|
162
|
+
unless d.nil?
|
163
|
+
d = d.dup
|
164
|
+
s.load( d.all, header_rows, header_cols )
|
165
|
+
d.sheet = s
|
166
|
+
end
|
167
|
+
s
|
168
|
+
end
|
169
|
+
|
170
|
+
def insert_columns( *args )
|
171
|
+
data.insert_columns( *args )
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
def insert_rows( *args )
|
176
|
+
data.insert_rows( *args )
|
177
|
+
self
|
178
|
+
end
|
179
|
+
|
180
|
+
def inspect
|
181
|
+
"#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ name }"
|
182
|
+
end
|
183
|
+
|
184
|
+
def load( input_data, header_rows=1, header_cols=0 )
|
185
|
+
@header_rows, @header_cols = header_rows, header_cols
|
186
|
+
@data = Data.new( self, input_data )
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
def match( header, &block )
|
191
|
+
row_id( column_by_header( header ).find( &block ) )
|
192
|
+
end
|
193
|
+
|
194
|
+
def maxrow
|
195
|
+
data.rows
|
196
|
+
end
|
197
|
+
|
198
|
+
def maxcol
|
199
|
+
data.cols
|
200
|
+
end
|
201
|
+
alias maxcolumn maxcol
|
202
|
+
|
203
|
+
def range( first_cell, last_cell=nil )
|
204
|
+
Element.new( self, to_range_address( first_cell, last_cell ) )
|
205
|
+
end
|
206
|
+
|
207
|
+
def row( index )
|
208
|
+
Row.new( self, index )
|
209
|
+
end
|
210
|
+
|
211
|
+
def rows( start_row = 1, end_row = data.rows )
|
212
|
+
return to_enum(:rows, start_row, end_row) unless block_given?
|
213
|
+
( start_row..end_row ).each { |idx| yield row( idx ) }
|
214
|
+
end
|
215
|
+
|
216
|
+
def to_a
|
217
|
+
data.all
|
218
|
+
end
|
219
|
+
|
220
|
+
def to_excel
|
221
|
+
workbook.dup.clear_all.add( self.dup ).workbook.to_excel
|
222
|
+
end
|
223
|
+
|
224
|
+
def to_s
|
225
|
+
data.nil? ? '' : data.map { |ar| ar.join "\t" }.join( $/ )
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubyexcel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joel Pearson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-23 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A tabular data structure, mixing Ruby with some of Excel's API.
|
15
|
+
email:
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/rubyexcel.rb
|
21
|
+
- lib/rubyexcel/excel_tools.rb
|
22
|
+
- lib/rubyexcel/rubyexcel_components.rb
|
23
|
+
- lib/rubyexcel/rubyexcel_advanced.rb
|
24
|
+
homepage:
|
25
|
+
licenses: []
|
26
|
+
post_install_message:
|
27
|
+
rdoc_options: []
|
28
|
+
require_paths:
|
29
|
+
- lib
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 1.8.24
|
45
|
+
signing_key:
|
46
|
+
specification_version: 3
|
47
|
+
summary: Spreadsheets in Ruby
|
48
|
+
test_files: []
|
49
|
+
has_rdoc:
|