File Coverage

lib/Config/Column.pm
Criterion Covered Total %
statement 165 165 100.0
branch 89 94 94.6
condition 20 24 83.3
subroutine 11 11 100.0
pod 6 6 100.0
total 291 300 97.0


line stmt bran cond sub pod time code
1             package Config::Column;
2 8     8   218425 use utf8;
  8         20  
  8         58  
3             # use strict;
4             # use warnings;
5              
6             our $VERSION = '1.00';
7              
8             =encoding utf8
9              
10             =head1 NAME
11              
12             Config::Column - simply packages input and output of "config" / "BBS log" file whose records are separated by any delimiter.
13              
14             =head1 SYNOPSIS
15              
16             # Copy the datalist in a tab separated file to readable formatted text file.
17            
18             use utf8;
19             use lib './lib';
20             use Config::Column;
21             my $order_delim = [qw(1 subject date value)];
22             my $order_nodelim = ['' => 1 => ': [' => subject => '] ' => date => ' : ' => value => ''];
23             my $delimiter = "\t";
24            
25             # MAIN file instance
26             my $ccmain = Config::Column->new(
27             'mainfile.dat', # the data file path
28             'utf8', # character encoding of the data file (PerlIO ":encoding($layer)") or PerlIO layer (ex. ':encoding(utf8)')
29             $order_delim, # list of key names
30             $delimiter, # delimiter that separates data column
31             1, # first index for data list
32             "\0" # delimiter that separates data record ("lines")
33             );
34             # SUB file (human readable)
35             my $ccsub = Config::Column->new(
36             '', # this can be empty because data file will be opened externally and file handle is passed to this instance
37             '', # same reason
38             $order_nodelim, # list of key names and delimiters
39             undef, # do not define delimiter
40             1, # first index for data list
41             # delimiter that separates data record ("lines") is Default
42             );
43            
44             # Read data from MAIN file.
45             my $data = $ccmain->read_data;
46             # Add new data.
47             push @$data,{subject => 'YATTA!', date => '2012/03/06T23:33:00+09:00', value => 'All tests passed!'};print $data;
48             # Write data to MAIN file.
49             $ccmain->write_data($data);
50             # Write header to SUB file
51             open my $fh,'+<:encoding(utf8)','subfile.txt';
52             flock $fh,2;
53             truncate $fh,0;
54             seek $fh,0,0;
55             print $fh 'Single line diary?',"\n";
56             # Add data to SUB file. Don't close and don't truncate $fh.
57             $ccsub->write_data($data,$fh,1,1);
58             print $fh 'The end of the worl^h^h^h^hfile';
59             close $fh;
60              
61             =head1 INTRODUCTION
62              
63             This module generalizes the list of keys and delimiters that is common in "config" / "BBS log" file format and packageizes data list input and output of these files.
64              
65             It treats data list as simple array reference of hash references.
66              
67             my $datalist = [
68             {}, # If the first index for data list (see below section) is 1, 0th data is empty.
69             {title => "hoge",value => "huga"},
70             {title => "hoge2",value => "huga"},
71             {title => "hoge3",value => "huga"},
72             ];
73              
74             It manages only IO of that data list format and leaves data list manipulating to basic Perl operation.
75              
76             =head1 DESCRIPTION
77              
78             =head2 Constructor
79              
80             =head3 new()
81              
82             my $cc = Config::Column->new(
83             $datafile, # the data file path
84             $layer, # character encoding of the data file (PerlIO ":encoding($layer)") or PerlIO layer (ex. ':encoding(utf8)')
85             $order, # the "order" (see below section) (ARRAY REFERENCE)
86             $delimiter, # delimiter that separates data column
87             $indexshift, # first index for data list (may be 0 or 1 || can omit, and use 0 as default) (Integer >= 0)
88             $linedelimiter # delimiter that separates data record ("lines")(can omit, and use Perl default (may be $/ == "\n"))
89             );
90              
91             C<$indexshift> is 0 or 1 in general.
92             For example, if C<$indexshift == 1>, you can get first data record by accessing to C<< $datalist->[1] >>, and C<< $datalist->[0] >> is empty.
93              
94             There is two types of definition of C<$order> and C<$delimiter> for 2 following case.
95              
96             =over
97              
98             =item single delimiter (You must define delimiter.)
99              
100             my $cc = Config::Column->new(
101             './filename.dat', # the data file path
102             'utf8', # character encoding of the data file or PerlIO layer
103             [qw(1 author id title date summary)], # the "order" [keys]
104             "\t", # You MUST define delimiter.
105             1, # first index for data list
106             "\n" # delimiter that separates data record
107             );
108              
109             In this case, "order" is names (hash keys) of each data column.
110              
111             It is for data formats such as tab/comma separated data.
112              
113             =item multiple delimiters (Never define delimiter.)
114              
115             my $cc = Config::Column->new(
116             './filename.dat', # the data file path
117             'utf8', # character encoding of the data file or PerlIO layer
118             [qw('' 1 ': ' author "\t" id "\t" title "\t" date "\t" summary)], # [delim key delim key ...]
119             undef, # NEVER define delimiter (or omit).
120             1, # first index for data list
121             "\n" # delimiter that separates data record
122             );
123              
124             In this case, "order" is names (hash keys) of each data column and delimiters.
125              
126             C<$order>'s 0,2,4...th (even) elements are delimiter, and 1,3,5...th (odd) elements are names (hash keys).
127              
128             It is for data formats such as ...
129              
130             =over
131              
132             =item C<['', 1, ' [', subject, '] : ', date, ' : ', article]>
133              
134             1 [This is the subject] : 2012/02/07 : Article is there. HAHAHA!
135             2 [Easy to read] : 2012/02/07 : Tab separated data is for only computers.
136              
137             =item C<< ['', thread_number, '.dat<>', subject, ' (', res_number, ')'] # subject.txt (bracket delimiter is errorous) >>
138              
139             1325052927.dat<>Nurupo (988)
140             1325387590.dat<>OKOTOWARI!!!!!! Part112 [AA] (444)
141             1321698127.dat<>Marked For Death 18 (159)
142              
143             =back
144              
145             =back
146              
147             =head4 Index column
148              
149             The name "1" in C<$order> means the index of data records.
150              
151             If the name "1" exists in C<$order>, integer in the index column will be used as array references' index.
152              
153             $delimiter = "\t";
154             $order = [1,somedata1,somedata2];
155            
156             # data file
157             1 somedata other
158             2 foobar 2000
159             3 hoge piyo
160             5 English isDifficult
161            
162             |
163             | read_data()
164             v
165            
166             $datalist = [
167             {}, # 0
168             {somedata1 => 'somedata', somedata2 => 'other'}, # 1
169             {somedata1 => 'foobar', somedata2 => '2000'}, # 2
170             {somedata1 => 'hoge', somedata2 => 'piyo'}, # 3
171             {}, # 4
172             {somedata1 => 'English', somedata2 => 'isDifficult'}, # 5
173             ];
174            
175             | ^
176             | write_data() | read_data()
177             v |
178            
179             # data file
180             1 somedata other
181             2 foobar 2000
182             3 hoge piyo
183             4
184             5 English isDifficult
185              
186             =begin comment
187              
188             #=head3 Definition of delimiters
189              
190             C<$delimiter> is compiled to regular expressions finally.
191              
192             In case of single delimiter,
193              
194             my @column = split /$delimiter/,$recordline;
195              
196             In case of multiple delimiters, C<$linedelimiter> is also compiled to regular expressions.
197              
198             my $lineregexpstr = '^'.(join '(.*?)',map {quotemeta} @delimiters) . '(?:' . quotemeta($linedelimiter) . ')?$';
199             my $lineregexp = qr/$lineregexpstr/;
200              
201             =end comment
202              
203             =cut
204              
205             sub new{
206 172     172 1 556515 my $package = shift;
207 172         361 my $filename = shift;
208 172         288 my $layer = shift;
209 172         302 my $order = shift;
210 172         304 my $delimiter = shift;
211 172         358 my $indexshift = shift;
212 172         276 my $linedelimiter = shift;
213 172   100     972 $package = ref $package || $package;
214 172 100       525 $indexshift = 0 unless $indexshift;
215 172 100       1047 return unless $indexshift =~ /^\d+$/;
216 169         1791 return bless {
217             filename => $filename,
218             layer => $layer,
219             order => $order,
220             delimiter => $delimiter,
221             indexshift => $indexshift,
222             linedelimiter => $linedelimiter
223             },$package;
224             }
225              
226             =head2 Methods
227              
228             =head3 add_data_last()
229              
230             This method adds data records to the data file after previous data in the file.
231             Indexes of these data records are automatically setted by reading the data file before.
232              
233             $cc->add_data_last($data,$fh,$fhflag);
234              
235             my $data = {title => "hoge",value => "huga"} || [
236             {title => "hoge",value => "huga"},
237             {title => "hoge2",value => "huga"},
238             {title => "hoge3",value => "huga"},
239             ]; # hash reference of single data record or array reference of hash references of multiple data records
240             my $fh; # file handle (can omit)
241             my $fhflag = 1; # if true, file handle will not be closed (can omit)
242              
243             If you give a file handle to the argument, file that defined by constructor is omitted and this method uses given file handle and adds data from the place current file pointer points not from the head of file.
244              
245             Return value:
246              
247             Succeed > first: 1 , second: (if C<$fhflag> is true) file handle
248              
249             Fail > first: false (return;)
250              
251             =cut
252              
253             sub add_data_last{
254 336     336 1 586 my $self = shift;
255 336         458 my $datalist = shift;
256 336         636 my $fh = shift;
257 336         477 my $fhflag = shift;
258 336 100       1735 $datalist = [$datalist] if ref $datalist eq 'HASH';
259 336         408 my $datanum;
260 336         1027 ($datanum,$fh) = $self->read_data_num($fh,1);
261 336         1342 return $self->add_data($datalist,$datanum + 1,$fh,$fhflag);
262             }
263              
264             =head3 add_data()
265              
266             This method adds data records to the data file.
267              
268             $cc->add_data($datalist,$startindex,$fh,$fhflag);
269              
270             my $datalist = {title => "hoge",value => "huga"} || [
271             {title => "hoge",value => "huga"},
272             {title => "hoge2",value => "huga"},
273             {title => "hoge3",value => "huga"},
274             ]; # hash reference of single data record or array reference of hash references of multiple data records
275             my $startindex = 12; # first index of the data record (can omit if you don't want index numbers)
276             my $fh; # file handle (can omit)
277             my $fhflag = 1; # if true, file handle will not be closed (can omit)
278              
279             If you give a file handle to the argument, file that defined by constructor is omitted and this method uses given file handle and adds data from the place current file pointer points not from the head of file.
280              
281             Return value:
282              
283             Succeed > first: 1 , second: (if C<$fhflag> is true) file handle
284              
285             Fail > first: false (return;)
286              
287             =cut
288              
289             sub add_data{
290 1512     1512 1 2233 my $self = shift;
291 1512         1996 my $datalist = shift;
292 1512         2864 my $startindex = shift;
293 1512         2031 my $fh = shift;
294 1512         1846 my $fhflag = shift;
295 1512 100       5411 $datalist = [$datalist] if ref $datalist eq 'HASH';
296 1512 100       3757 unless(ref $fh eq 'GLOB'){
297 168         415 my $layer = $self->_layer();
298 168 50 33     7198 open $fh,'+<'.$layer,$self->{filename} or open $fh,'>'.$layer,$self->{filename} or return;
299 168         7702 flock $fh,2;
300 168         1001 seek $fh,0,2;
301             }
302 1512         4592 $self->_write_order($fh,$datalist,$startindex);
303 1512 100       69258 close $fh unless $fhflag;
304 1512 100       11445 return $fhflag ? (1,$fh) : 1;
305             }
306              
307             =head3 write_data()
308              
309             This method writes data records to the data file.
310             Before writing data, the contents of the data file will be erased.
311              
312             $cc->write_data($datalist,$fh,$fhflag,$noempty);
313              
314             my $datalist = [
315             {title => "hoge",value => "huga"},
316             {title => "hoge2",value => "huga"},
317             {title => "hoge3",value => "huga"},
318             ]; # array reference of hash references of multiple data records
319             my $fh; # file handle (can omit)
320             my $fhflag = 1; # if true, file handle will not be closed (can omit)
321             my $noempty = 1; # see below
322              
323             If you give a file handle to the argument, file that defined by constructor is omitted and this method uses given file handle.
324             If C<$noempty> is true, the contents of the data file will not be erased, and writes data from the place current file pointer points not from the head of file.
325              
326             Return value:
327              
328             Succeed > first: 1 , second: (if C<$fhflag> is true) file handle
329              
330             Fail > first: false (return;)
331              
332             =cut
333              
334             sub write_data{
335 1008     1008 1 1908807 my $self = shift;
336 1008         1587 my $datalist = shift;
337 1008         1541 my $fh = shift;
338 1008         1701 my $fhflag = shift;
339 1008         1353 my $noempty = shift;
340 1008         1423 $datalist = [@{$datalist}]; # escape destructive operation
  1008         3545  
341 1008         2809 splice @$datalist,0,$self->{indexshift};
342 1008 100       3640 unless(ref $fh eq 'GLOB'){
343 672         1837 my $layer = $self->_layer();
344 672 50 66     78248 open $fh,'+<'.$layer,$self->{filename} or open $fh,'>'.$layer,$self->{filename} or return;
345 672         37237 flock $fh,2;
346             }
347 1008 100       3654 unless($noempty){
348 840         425560 truncate $fh,0;
349 840         4977 seek $fh,0,0;
350             }
351 1008         4165 return $self->add_data($datalist,$self->{indexshift},$fh,$fhflag);
352             }
353              
354             =begin comment
355              
356             #=head3 write_data_range()
357              
358             範囲内のデータをファイルに書き出す。
359              
360             $cc->write_data_range($datalist,$startindex,$endindex,$fh,$fhflag);
361              
362             my $datalist = [
363             {title => "hoge",value => "huga"},
364             {title => "hoge2",value => "huga"},
365             {title => "hoge3",value => "huga"},
366             ];# 複数データの配列リファレンスのみ許される。
367             my $startindex = 2; # 書き出すデータリストの最初のインデックス。0番目のデータから書き出すなら省略可能。
368             my $endindex = 10; # 書き出すデータリストの最後のインデックス。最後のデータまで書き出すなら省略可能。
369             my $fh; # 省略可能。ファイルハンドル。
370             my $fhflag = 1; # 真値を与えればファイルハンドルを維持する。
371              
372             与えられたファイルハンドルのファイルポインタが先頭でないなら、その位置から書き出します。
373              
374             成功なら第一返値に1、$fhflagが真なら第二返値にファイルハンドルを返す。失敗なら偽を返す。
375              
376             =end comment
377              
378             =cut
379              
380             =begin comment
381              
382             sub write_data_range{
383             my $self = shift;
384             my $datalist = shift;
385             my $startindex = shift;
386             my $endindex = shift;
387             my $fh = shift;
388             my $fhflag = shift;
389             $datalist = [@{$datalist}]; # escape destructive operation
390             if($startindex){
391             $startindex = $#$datalist + $startindex + 1 if $startindex < 0;
392             if($startindex > $#$datalist){
393             warn 'startindex is out of index range';
394             }
395             }else{
396             $startindex = $self->{indexshift};
397             }
398             splice @$datalist,0,$startindex > $self->{indexshift} ? $startindex : $self->{indexshift};
399             if($endindex){
400             $endindex = $#$datalist + $endindex + 1 if $endindex < 0;
401             if($endindex > $#$datalist){
402             warn 'endindex is out of index range';
403             }elsif($endindex < $#$datalist){
404             splice @$datalist,$endindex + 1;
405             }
406             }
407             return $self->add_data($datalist,$startindex,$fh,$fhflag);
408             }
409              
410             =end comment
411              
412             =cut
413              
414             =head3 read_data()
415              
416             This method reads data records from the data file.
417              
418             $cc->read_data($fh,$fhflag);
419              
420             my $fh; # file handle (can omit)
421             my $fhflag = 1; # if true, file handle will not be closed (can omit)
422              
423             If you give a file handle to the argument, file that defined by constructor is omitted and this method uses given file handle and reads data from the place current file pointer points not from the head of file.
424              
425             Return value:
426              
427             Succeed > first: data list (array reference of hash references) , second: (if C<$fhflag> is true) file handle
428              
429             Fail > first: false (return;)
430              
431             =cut
432              
433             sub read_data{
434 1344     1344 1 594247 my $self = shift;
435 1344         2039 my $fh = shift;
436 1344         1560 my $data;
437 1344         1951 my $fhflag = shift;
438 1344 100       4367 unless(ref $fh eq 'GLOB'){
439 672 50       3355 open $fh,'+<'.$self->_layer(),$self->{filename} or return;
440 672         37722 flock $fh,2;
441 672         4071 seek $fh,0,0;
442             }
443 1344 100 100     20906 local $/ = $self->{linedelimiter} if defined $self->{linedelimiter} && $self->{linedelimiter} ne '';
444 1344 100       3108 if($self->{delimiter}){
445 672         968 my $indexcolumn = -1;
446 672         1177 my @key = @{$self->{order}};
  672         2861  
447 672         2676 for my $i (0..$#key){
448 4480 100       9367 if($key[$i] eq 1){$indexcolumn = $i;last;}
  448         558  
  448         741  
449             }
450 672         1731 my $cnt = $self->{indexshift} - 1;
451 672         10227 while(<$fh>){
452 3276         26881 chomp;
453 3276         27156 my @column = split /$self->{delimiter}/;
454 3276 100       7797 $indexcolumn >= 0 ? $cnt = $column[$indexcolumn] : $cnt++;
455 3276         6190 for my $i (0..$#column){
456 31164 100       108730 $data->[$cnt]->{$key[$i]} = $column[$i] unless $key[$i] eq '1';
457             }
458             }
459             }else{
460 672 100       1166 my @key = map { $_ % 2 ? $self->{order}->[$_] : () } (0..$#{$self->{order}});
  13664         26892  
  672         2229  
461 672 100       10070 my @delim = map { $_ % 2 ? () : $self->{order}->[$_] } (0..$#{$self->{order}});
  13664         31013  
  672         2067  
462 672         2021 my $lineregexpstr = '^'.(join '(.*?)',map {quotemeta} @delim) . '(?:' . quotemeta($/) . ')?$';
  7168         14226  
463 672         6921 my $lineregexp = qr/$lineregexpstr/;
464 672         1903 my $indexcolumn = -1;
465 672         1768 for my $i (0..$#key){
466 4480 100       9295 if($key[$i] eq 1){$indexcolumn = $i;last;}
  448         530  
  448         669  
467             }
468 672         1323 my $cnt = $self->{indexshift} - 1;
469 672         10994 while(<$fh>){
470 3276         29033 chomp;
471 3276         65686 my @column = /$lineregexp/;
472 3276 100       8515 $indexcolumn >= 0 ? $cnt = $column[$indexcolumn] : $cnt++;
473 3276         7057 for my $i (0..$#column){
474 31668 100       114049 $data->[$cnt]->{$key[$i]} = $column[$i] unless $key[$i] eq '1';
475             }
476             }
477             }
478 1344 100       26240 close $fh unless $fhflag;
479 1344 100       11991 return $fhflag ? ($data,$fh) : $data;
480             }
481              
482             =head3 read_data_num()
483              
484             This method reads data record's last index number from the data file.
485              
486             $cc->read_data_num($fh,$fhflag);
487              
488             my $fh; # file handle (can omit)
489             my $fhflag = 1; # if true, file handle will not be closed (can omit)
490              
491             If you give a file handle to the argument, file that defined by constructor is omitted and this method uses given file handle and reads data from the place current file pointer points not from the head of file.
492              
493             Return value:
494              
495             Succeed > first: last index , second: (if C<$fhflag> is true) file handle
496              
497             Fail > first: false (return;)
498              
499             =cut
500              
501             sub read_data_num{
502 840     840 1 185058 my $self = shift;
503 840         1905 my $fh = shift;
504 840         1349 my $fhflag = shift;
505 840 100       2373 unless(ref $fh eq 'GLOB'){
506 672 50       3147 open $fh,'+<'.$self->_layer(),$self->{filename} or return;
507 672         35117 flock $fh,2;
508 672         3836 seek $fh,0,0;
509             }
510 840 100 100     6463 local $/ = $self->{linedelimiter} if defined $self->{linedelimiter} && $self->{linedelimiter} ne '';
511 840         1748 my $datanum = $self->{indexshift} - 1;
512 840 100       2127 if($self->{delimiter}){
513 420         563 my $indexcolumn = -1;
514 420         557 for my $i (0..$#{$self->{order}}){
  420         1350  
515 2800 100       6659 if($self->{order}->[$i] eq 1){$indexcolumn = $i;last;}
  280         370  
  280         466  
516             }
517 420 100       976 if($indexcolumn < 0){$datanum++ while <$fh>;}
  140         2446  
  280         4619  
518             else{$datanum = (split /$self->{delimiter}/)[$indexcolumn] while <$fh>;}
519 420         24960 chomp $datanum;
520             }else{
521 420 100       779 my @key = map { $_ % 2 ? $self->{order}->[$_] : () } (0..$#{$self->{order}});
  8540         17736  
  420         1517  
522 420 100       1106 my @delim = map { $_ % 2 ? () : $self->{order}->[$_] } (0..$#{$self->{order}});
  8540         17879  
  420         1168  
523 420         1244 my $lineregexpstr = '^'.(join '(.*?)',map {quotemeta} @delim) . '(?:' . quotemeta($/) . ')?$';
  4480         9097  
524 420         5595 my $lineregexp = qr/$lineregexpstr/;
525 420         693 my $indexcolumn = -1;
526 420         1831 for my $i (0..$#key){
527 2800 100       5491 if($key[$i] eq 1){$indexcolumn = $i;last;}
  280         303  
  280         445  
528             }
529 420 100       873 if($indexcolumn < 0){$datanum++ while <$fh>;}
  140         3967  
  280         5004  
530             else{$datanum = (/$lineregexp/)[$indexcolumn] while <$fh>;}
531             }
532 840 100       52473 close $fh unless $fhflag;
533 840 100       5907 return $fhflag ? ($datanum,$fh) : $datanum;
534             }
535              
536             =begin comment
537              
538             #=head3 _write_order()
539              
540             $cc->_write_order($order,$delimiter,$linedelimiter);
541             $order = [1 title summary];
542             $delimiter = "\n";
543             $linedelimiter = "\n";
544              
545             =end comment
546              
547             =cut
548              
549 1512 100 66 1512   13727 sub _write_order{defined $_[0]->{delimiter} && $_[0]->{delimiter} ne '' ? goto &_write_order_has_delimiter : goto &_write_order_no_delimiter}
550              
551             sub _write_order_has_delimiter{
552 756     756   1412 my $self = shift;
553 756         879 my $fh = shift;
554 756         825 my $datalist = shift;
555 756         1039 my $index = shift;
556 756         1386 my $delimiter = $self->{delimiter};
557 756         979 my @order = @{$self->{order}};
  756         3690  
558 756 100 100     5004 local $/ = $self->{linedelimiter} if defined $self->{linedelimiter} && $self->{linedelimiter} ne '';
559 756         1322 for my $data (@$datalist){
560 2436 100       4025 print $fh (join $delimiter,map {$_ eq 1 ? $index : defined $data->{$_} ? $data->{$_} : ''} @order),$/;
  23548 100       76126  
561 2436         7716 $index ++;
562             }
563             }
564              
565             sub _write_order_no_delimiter{
566 756     756   1187 my $self = shift;
567 756         1091 my $fh = shift;
568 756         818 my $datalist = shift;
569 756         1245 my $index = shift;
570 756         1258 my $delimiter = $self->{delimiter};
571 756         820 my @order = @{$self->{order}};
  756         4643  
572 756 100 100     7755 local $/ = $self->{linedelimiter} if defined $self->{linedelimiter} && $self->{linedelimiter} ne '';
573 756         1456 for my $data (@$datalist){
574 2436 100       7659 print $fh (map {$_ % 2 ? $order[$_] eq 1 ? $index : defined $data->{$order[$_]} ? $data->{$order[$_]} : '' : $order[$_]} (0..$#order)),$/;
  49532 100       153936  
    100          
575 2436         10872 $index ++;
576             }
577             }
578              
579             sub _layer{
580 2184     2184   2974 my $self = shift;
581 2184 100       79403 return $self->{layer} ? $self->{layer} =~ /:/ ? $self->{layer} : ':encoding('.$self->{layer}.')' : '';
    50          
582             }
583              
584             1;
585              
586             =head1 DEPENDENCIES
587              
588             This module requires no other modules and libraries.
589              
590             =head1 NOTES
591              
592             This module is written in object-oriented style but treating data by naked array or file handle so you should treat data by procedural style.
593              
594             For example, if you want to delete 3,6 and 8th element in data list completely, the following code will be required.
595              
596             splice @$datalist,$_,1 for sort {$b <=> $a} qw(3 6 8);
597              
598             So, if you want more smart OO, it will be better to use another modules that wraps naked array or file handle in OO (such as Object::Array ... etc?), or create Config::Column::OO etc. which inherits this module and can use methods pop, shift, splice, delete, etc.
599              
600             =head1 TODO
601              
602             Odd Engrish
603              
604             =head1 AUTHOR
605              
606             Narazaka (http://narazaka.net/)
607              
608             =head1 COPYRIGHT AND LICENSE
609              
610             Copyright 2011-2012 by Narazaka, all rights reserved.
611              
612             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.