rubyexcel 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
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
+ end
@@ -0,0 +1,172 @@
1
+ module RubyExcel
2
+
3
+ require_relative 'address.rb'
4
+
5
+ class Data
6
+ attr_reader :rows, :cols
7
+ attr_accessor :sheet
8
+ alias parent sheet
9
+
10
+ include Address
11
+
12
+ def initialize( sheet, input_data )
13
+ @sheet = sheet
14
+ ( input_data.kind_of?( Array ) && input_data.all? { |el| el.kind_of?( Array ) } ) or fail ArgumentError, 'Input must be Array of Arrays'
15
+ @data = input_data.dup
16
+ calc_dimensions
17
+ end
18
+
19
+ def all
20
+ @data.dup
21
+ end
22
+
23
+ def append( multi_array )
24
+ @data
25
+ @data += multi_array
26
+ calc_dimensions
27
+ end
28
+
29
+ def colref_by_header( header )
30
+ sheet.header_rows > 0 or fail NoMethodError, 'No header rows present'
31
+ @data[ 0..sheet.header_rows-1 ].each do |r|
32
+ if ( idx = r.index( header ) )
33
+ return col_letter( idx+1 )
34
+ end
35
+ end
36
+ fail IndexError, "#{ header } is not a valid header"
37
+ end
38
+
39
+ def compact!
40
+ compact_columns!
41
+ compact_rows!
42
+ end
43
+
44
+ def compact_columns!
45
+ ensure_shape
46
+ @data = @data.transpose.delete_if { |ar| ar.all? { |el| el.to_s.empty? } || ar.empty? }.transpose
47
+ calc_dimensions
48
+ @data
49
+ end
50
+
51
+ def compact_rows!
52
+ @data.delete_if { |ar| ar.all? { |el| el.to_s.empty? } || ar.empty? }
53
+ calc_dimensions
54
+ @data
55
+ end
56
+
57
+ def delete( object )
58
+ case object
59
+ when Row
60
+ @data.slice!( object.idx - 1 )
61
+ when Column
62
+ idx = col_index( object.idx ) - 1
63
+ @data.each { |r| r.slice! idx }
64
+ when Element
65
+ addresses = expand( object.address )
66
+ indices = [ address_to_indices( addresses.first.first ), address_to_indices( addresses.last.last ) ].flatten.map { |n| n-1 }
67
+ @data[ indices[0]..indices[2] ].each { |r| r.slice!( indices[1], indices[3] - indices[1] + 1 ) }
68
+ @data.delete_if.with_index { |r,i| r.empty? && i.between?( indices[0], indices[2] ) }
69
+ else
70
+ fail NoMethodError, "#{ object.class } is not supported"
71
+ end
72
+ self
73
+ end
74
+
75
+ def delete_column( ref )
76
+ delete( Column.new( sheet, ref ) )
77
+ end
78
+
79
+ def delete_row( ref )
80
+ delete( Row.new( sheet, ref ) )
81
+ end
82
+
83
+ def delete_range( ref )
84
+ delete( Element.new( sheet, ref ) )
85
+ end
86
+
87
+ def dup
88
+ Data.new( sheet, @data.dup )
89
+ end
90
+
91
+ def empty?
92
+ no_headers.empty?
93
+ end
94
+
95
+ def filter!( header )
96
+ hrows = sheet.header_rows
97
+ idx = col_index( hrows > 0 ? colref_by_header( header ) : header )
98
+ @data = @data.select.with_index { |row, i| hrows > i || yield( row[ idx -1 ] ) }
99
+ calc_dimensions
100
+ self
101
+ end
102
+
103
+ def get_columns!( *headers )
104
+ hrow = sheet.header_rows - 1
105
+ ensure_shape
106
+ @data = @data.transpose.select{ |col| headers.include?( col[hrow] ) }
107
+ ensure_shape
108
+ @data = @data.sort_by{ |col| headers.index( col[hrow] ) || col[hrow] }.transpose
109
+ calc_dimensions
110
+ self
111
+ end
112
+
113
+ def insert_columns( before, number=1 )
114
+ a = Array.new( number, nil )
115
+ before = col_index( before ) - 1
116
+ @data.map! { |row| row.insert( before, *a ) }
117
+ end
118
+
119
+ def insert_rows( before, number=1 )
120
+ @data = @data.insert( ( col_index( before ) - 1 ), *Array.new( number, [nil] ) )
121
+ end
122
+
123
+ def no_headers
124
+ if sheet.header_cols.zero?
125
+ @data[ sheet.header_rows..-1 ].dup
126
+ else
127
+ @data[ sheet.header_rows..-1 ].map { |row| row[ sheet.header_cols..-1 ] }
128
+ end
129
+ end
130
+
131
+ def read( addr )
132
+ row_idx, col_idx = address_to_indices( addr )
133
+ @data[ row_idx-1 ][ col_idx-1 ]
134
+ end
135
+ alias [] read
136
+
137
+ def uniq!( header )
138
+ column = col_index( colref_by_header( header ) )
139
+ @data = @data.uniq { |row| row[ column - 1 ] }
140
+ self
141
+ end
142
+ alias unique! uniq!
143
+
144
+ def write( addr, val )
145
+ row_idx, col_idx = address_to_indices( addr )
146
+ ( row_idx - rows ).times { @data << [] }
147
+ @data[ row_idx-1 ][ col_idx-1 ] = val
148
+ calc_dimensions
149
+ end
150
+ alias []= write
151
+
152
+ include Enumerable
153
+
154
+ def each
155
+ @data.each { |ar| yield ar }
156
+ end
157
+
158
+ private
159
+
160
+ def calc_dimensions
161
+ @rows, @cols = @data.length, @data.max_by(&:length).length
162
+ end
163
+
164
+ def ensure_shape
165
+ calc_dimensions
166
+ @data.map! { |ar| ar.length == cols ? ar : ar + Array.new( cols - ar.length, nil) }
167
+ @data
168
+ end
169
+
170
+ end
171
+
172
+ end
@@ -0,0 +1,68 @@
1
+ module RubyExcel
2
+
3
+ class Element
4
+
5
+ attr_reader :sheet, :address, :data, :column, :row
6
+ alias parent sheet
7
+
8
+ def initialize( sheet, addr )
9
+ fail ArgumentError, "Invalid range: #{ addr }" unless addr =~ /\A[A-Z]+\d+:[A-Z]+\d+\z|\A[A-Z]+\d+\z/
10
+ @sheet = sheet
11
+ @data = sheet.data
12
+ @address = addr
13
+ @column = column_id( addr )
14
+ @row = row_id( addr )
15
+ end
16
+
17
+ def delete
18
+ data.delete( self )
19
+ end
20
+
21
+ include Address
22
+
23
+ def value
24
+ if address.include? ':'
25
+ expand( address ).map { |ar| ar.map { |addr| data[ addr ] } }
26
+ else
27
+ data[ address ]
28
+ end
29
+ end
30
+
31
+ def value=( val )
32
+ if address.include? ':'
33
+ if multi_array?( val )
34
+ expand( address ).each_with_index { |row,idx| row.each_with_index { |el,i| data[ el ] = val[idx][i] } }
35
+ else
36
+ expand( address ).each { |ar| ar.each { |addr| data[ addr ] = val } }
37
+ end
38
+ else
39
+ data[ address ] = val
40
+ end
41
+ self
42
+ end
43
+
44
+ def to_s
45
+ value.is_a?( Array ) ? value.map { |ar| ar.join "\t" }.join($/) : value.to_s
46
+ end
47
+
48
+ def inspect
49
+ "#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ address }"
50
+ end
51
+
52
+ include Enumerable
53
+
54
+ def each
55
+ expand( address ).flatten.each { |addr| yield data[ addr ] }
56
+ end
57
+
58
+ def each_cell
59
+ expand( address ).flatten.each { |addr| yield Element.new( sheet, addr ) }
60
+ end
61
+
62
+ def map!
63
+ expand( address ).flatten.each { |addr| data[ addr ] = yield data[ addr ] }
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -1,348 +1,16 @@
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
1
+ require_relative 'address.rb'
2
+ require_relative 'data.rb'
3
+ require_relative 'element.rb'
4
+ require_relative 'section.rb'
180
5
 
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
6
+ module RubyExcel
265
7
 
8
+ def self.sample_data
9
+ a=[];8.times{|t|b=[];c='A';5.times{b<<"#{c}#{t+1}";c.next!};a<<b};a
266
10
  end
267
-
268
- class Column < Section
269
11
 
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
-
12
+ def self.sample_sheet
13
+ Workbook.new.load RubyExcel.sample_data
283
14
  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
-
15
+
348
16
  end
@@ -0,0 +1,144 @@
1
+ module RubyExcel
2
+
3
+ class Section
4
+
5
+ include Address
6
+
7
+ attr_reader :sheet, :idx, :data
8
+ alias parent sheet
9
+
10
+ def initialize( sheet )
11
+ @sheet = sheet
12
+ @data = sheet.data
13
+ end
14
+
15
+ def <<( value )
16
+ if self.is_a? Row
17
+ lastone = ( col_index( idx ) == 1 ? data.cols + 1 : data.cols )
18
+ else
19
+ lastone = ( col_index( idx ) == 1 ? data.rows + 1 : data.rows )
20
+ end
21
+ data[ translate_address( lastone ) ] = value
22
+ end
23
+
24
+ def delete
25
+ data.delete( self )
26
+ end
27
+
28
+ def empty?
29
+ all? { |val| val.to_s.empty? }
30
+ end
31
+
32
+ def find
33
+ each_cell { |ce| return ce.address if yield ce.value }; nil
34
+ end
35
+
36
+ def inspect
37
+ "#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ idx }"
38
+ end
39
+
40
+ def read( id )
41
+ data[ translate_address( id ) ]
42
+ end
43
+ alias [] read
44
+
45
+ def summarise
46
+ h = Hash.new(0)
47
+ each_wh { |v| h[v]+=1 }; h
48
+ end
49
+ alias summarize summarise
50
+
51
+ def to_s
52
+ to_a.join ( self.is_a?( Row ) ? "\t" : "\n" )
53
+ end
54
+
55
+ def write( id, val )
56
+ data[ translate_address( id ) ] = val
57
+ end
58
+ alias []= write
59
+
60
+ include Enumerable
61
+
62
+ def each
63
+ return to_enum(:each) unless block_given?
64
+ each_address { |addr| yield data[ addr ] }
65
+ end
66
+
67
+ def each_without_headers
68
+ return to_enum(:each_without_headers) unless block_given?
69
+ each_address_without_headers { |addr| yield data[ addr ] }
70
+ end
71
+ alias each_wh each_without_headers
72
+
73
+ def each_cell
74
+ return to_enum(:each_cell) unless block_given?
75
+ each_address { |addr| yield Element.new( sheet, addr ) }
76
+ end
77
+
78
+ def map!
79
+ return to_enum(:map!) unless block_given?
80
+ each_address { |addr| data[addr] = ( yield data[addr] ) }
81
+ end
82
+
83
+ private
84
+
85
+ def translate_address( addr )
86
+ case self
87
+ when Row
88
+ col_letter( addr ) + idx.to_s
89
+ when Column
90
+ addr = addr.to_s unless addr.is_a?( String )
91
+ fail ArgumentError, "Invalid address : #{ addr }" if addr =~ /[^\d]/
92
+ idx + addr
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ class Row < Section
99
+
100
+ attr_reader :idx
101
+
102
+ def initialize( sheet, idx )
103
+ @idx = idx.to_i
104
+ super( sheet )
105
+ end
106
+
107
+ def getref( header )
108
+ column_id( sheet.row(1).find &/#{header}/ )
109
+ end
110
+
111
+ private
112
+
113
+ def each_address
114
+ ( 'A'..col_letter( data.cols ) ).each { |col_id| yield "#{col_id}#{idx}" }
115
+ end
116
+
117
+ def each_address_without_headers
118
+ ( col_letter( sheet.header_cols+1 )..col_letter( data.cols ) ).each { |col_id| yield "#{col_id}#{idx}" }
119
+ end
120
+
121
+ end
122
+
123
+ class Column < Section
124
+
125
+ attr_reader :idx
126
+
127
+ def initialize( sheet, idx )
128
+ @idx = idx
129
+ super( sheet )
130
+ end
131
+
132
+ private
133
+
134
+ def each_address
135
+ ( 1..data.rows ).each { |row_id| yield idx + row_id.to_s }
136
+ end
137
+
138
+ def each_address_without_headers
139
+ ( sheet.header_rows+1 ).upto( data.rows ) { |row_id| yield idx + row_id.to_s }
140
+ end
141
+
142
+ end
143
+
144
+ end
data/lib/rubyexcel.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require_relative 'rubyexcel/rubyexcel_components.rb'
2
- require_relative 'rubyexcel/rubyexcel_advanced.rb'
3
2
  require_relative 'rubyexcel/excel_tools.rb'
4
3
 
5
4
  class Regexp
@@ -18,8 +17,8 @@ module RubyExcel
18
17
 
19
18
  def <<( other )
20
19
  case other
21
- when RubyExcel
22
- other.each { |s| @sheets << s }
20
+ when Workbook
21
+ other.inject( @sheets, :<< )
23
22
  when Sheet
24
23
  @sheets << other
25
24
  end
@@ -124,6 +123,33 @@ module RubyExcel
124
123
  range( addr ).value = val
125
124
  end
126
125
 
126
+
127
+ def +( other )
128
+ dup << other
129
+ end
130
+
131
+ def -( other )
132
+ case other
133
+ when Array
134
+ Workbook.new.load( data.all - other )
135
+ when Sheet
136
+ Workbook.new.load( data.all - other.data.no_headers )
137
+ else
138
+ fail ArgumentError, "Unsupported class: #{ other.class }"
139
+ end
140
+ end
141
+
142
+ def <<( other )
143
+ case other
144
+ when Array
145
+ load( data.all + other, header_rows, header_cols )
146
+ when Sheet
147
+ load( data.all + other.data.no_headers, header_rows, header_cols )
148
+ else
149
+ fail ArgumentError, "Unsupported class: #{ other.class }"
150
+ end
151
+ end
152
+
127
153
  def cell( row, col )
128
154
  Element.new( self, indices_to_address( row, col ) )
129
155
  end
@@ -133,24 +159,21 @@ module RubyExcel
133
159
  Column.new( self, col_letter( index ) )
134
160
  end
135
161
 
162
+ def column_by_header( header )
163
+ Column.new( self, data.colref_by_header( header ) )
164
+ end
165
+ alias ch column_by_header
166
+
136
167
  def columns( start_column = 'A', end_column = data.cols )
137
168
  start_column, end_column = col_letter( start_column ), col_letter( end_column )
138
169
  return to_enum(:columns, start_column, end_column) unless block_given?
139
170
  ( start_column..end_column ).each { |idx| yield column( idx ) }
140
171
  end
141
172
 
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 )
173
+ def compact!
174
+ data.compact!
151
175
  self
152
176
  end
153
- alias gc! get_columns!
154
177
 
155
178
  def delete
156
179
  workbook.delete self
@@ -167,6 +190,32 @@ module RubyExcel
167
190
  s
168
191
  end
169
192
 
193
+ def empty?
194
+ data.empty?
195
+ end
196
+
197
+ def filter( ref, &block )
198
+ dup.filter!( ref, &block )
199
+ end
200
+
201
+ def filter!( ref, &block )
202
+ data.filter!( ref, &block )
203
+ self
204
+ end
205
+
206
+ def get_columns( *headers )
207
+ s = dup
208
+ s.data.get_columns!( *headers )
209
+ s
210
+ end
211
+ alias gc get_columns
212
+
213
+ def get_columns!( *headers )
214
+ data.get_columns!( *headers )
215
+ self
216
+ end
217
+ alias gc! get_columns!
218
+
170
219
  def insert_columns( *args )
171
220
  data.insert_columns( *args )
172
221
  self
@@ -213,6 +262,16 @@ module RubyExcel
213
262
  ( start_row..end_row ).each { |idx| yield row( idx ) }
214
263
  end
215
264
 
265
+ def sumif( find_header, sum_header )
266
+ col1 = column_by_header( find_header )
267
+ col2 = column_by_header( sum_header )
268
+ total = 0
269
+ col1.each_cell do |ce|
270
+ total += col2[ ce.row ].to_i if yield( ce.value ) && ce.row >= header_rows
271
+ end
272
+ total
273
+ end
274
+
216
275
  def to_a
217
276
  data.all
218
277
  end
@@ -225,6 +284,12 @@ module RubyExcel
225
284
  data.nil? ? '' : data.map { |ar| ar.join "\t" }.join( $/ )
226
285
  end
227
286
 
287
+ def uniq!( header )
288
+ data.uniq!( header )
289
+ self
290
+ end
291
+ alias unique! uniq!
292
+
228
293
  end
229
294
 
230
295
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyexcel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-23 00:00:00.000000000 Z
12
+ date: 2013-04-02 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A tabular data structure, mixing Ruby with some of Excel's API.
15
15
  email:
@@ -20,7 +20,10 @@ files:
20
20
  - lib/rubyexcel.rb
21
21
  - lib/rubyexcel/excel_tools.rb
22
22
  - lib/rubyexcel/rubyexcel_components.rb
23
- - lib/rubyexcel/rubyexcel_advanced.rb
23
+ - lib/rubyexcel/data.rb
24
+ - lib/rubyexcel/element.rb
25
+ - lib/rubyexcel/section.rb
26
+ - lib/rubyexcel/address.rb
24
27
  homepage:
25
28
  licenses: []
26
29
  post_install_message:
@@ -1,164 +0,0 @@
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