File Coverage

blib/lib/Siffra/Tools.pm
Criterion Covered Total %
statement 58 294 19.7
branch 2 84 2.3
condition 2 20 10.0
subroutine 17 37 45.9
pod 17 17 100.0
total 96 452 21.2


line stmt bran cond sub pod time code
1             package Siffra::Tools;
2              
3 1     1   67485 use 5.014;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         19  
5 1     1   5 use warnings;
  1         2  
  1         21  
6 1     1   4 use Carp;
  1         2  
  1         77  
7 1     1   638 use utf8;
  1         15  
  1         5  
8 1     1   634 use Data::Dumper;
  1         6700  
  1         57  
9 1     1   428 use DDP;
  1         40385  
  1         11  
10 1     1   569 use Log::Any qw($log);
  1         10249  
  1         5  
11 1     1   2177 use Scalar::Util qw(blessed);
  1         2  
  1         86  
12             $Carp::Verbose = 1;
13              
14             $| = 1; #autoflush
15              
16             use constant {
17             FALSE => 0,
18             TRUE => 1,
19 1   50     136 DEBUG => $ENV{ DEBUG } // 0,
20 1     1   28 };
  1         2  
21              
22             my %driverConnections = (
23             pgsql => {
24             module => 'DBD::Pg',
25             dsn => 'DBI:Pg(AutoCommit=>1,RaiseError=>1,PrintError=>1):dbname=%s;host=%s;port=%s',
26             },
27             mysql => {
28             module => 'DBD::mysql',
29             },
30             sqlite => {
31             module => 'DBD::SQLite',
32             },
33             );
34              
35             BEGIN
36             {
37 1     1   19 binmode( STDOUT, ":encoding(UTF-8)" );
  1     1   5  
  1         2  
  1         6  
38 1         16283 binmode( STDERR, ":encoding(UTF-8)" );
39              
40 1         511 require Siffra::Base;
41 1     1   6 use Exporter ();
  1         2  
  1         30  
42 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         116  
43 1         1801 $VERSION = '0.24';
44 1         30 @ISA = qw(Siffra::Base Exporter);
45              
46             #Give a hoot don't pollute, do not export more than needed by default
47 1         3 @EXPORT = qw();
48 1         2 @EXPORT_OK = qw();
49 1         3558 %EXPORT_TAGS = ();
50             } ## end BEGIN
51              
52             #################### subroutine header begin ####################
53              
54             =head2 sample_function
55              
56             Usage : How to use this function/method
57             Purpose : What it does
58             Returns : What it returns
59             Argument : What it wants to know
60             Throws : Exceptions and other anomolies
61             Comment : This is a sample subroutine header.
62             : It is polite to include more pod and fewer comments.
63              
64             See Also :
65              
66             =cut
67              
68             #################### subroutine header end ####################
69              
70             =head2 C<new()>
71              
72             Usage : $self->block_new_method() within text_pm_file()
73             Purpose : Build 'new()' method as part of a pm file
74             Returns : String holding sub new.
75             Argument : $module: pointer to the module being built
76             (as there can be more than one module built by EU::MM);
77             for the primary module it is a pointer to $self
78             Throws : n/a
79             Comment : This method is a likely candidate for alteration in a subclass,
80             e.g., pass a single hash-ref to new() instead of a list of
81             parameters.
82              
83             =cut
84              
85             sub new
86             {
87 1     1 1 90 $log->debug( "new", { progname => $0, pid => $$, perl_version => $], package => __PACKAGE__ } );
88 1         9 my ( $class, %parameters ) = @_;
89 1         9 my $self = $class->SUPER::new( %parameters );
90              
91 1         21 return $self;
92             } ## end sub new
93              
94             sub _initialize()
95             {
96 0     0   0 $log->debug( "_initialize", { package => __PACKAGE__ } );
97 0         0 my ( $self, %parameters ) = @_;
98 0         0 $self->SUPER::_initialize( %parameters );
99              
100 0         0 eval { require JSON::XS; };
  0         0  
101 0         0 $self->{ json } = JSON::XS->new->utf8;
102             } ## end sub _initialize
103              
104             sub _finalize()
105             {
106 0     0   0 $log->debug( "_finalize", { package => __PACKAGE__ } );
107 0         0 my ( $self, %parameters ) = @_;
108 0         0 $self->SUPER::_finalize( %parameters );
109             }
110              
111             sub END
112             {
113 1     1   149 $log->debug( "END", { package => __PACKAGE__ } );
114 1         4 eval { $log->{ adapter }->{ dispatcher }->{ outputs }->{ Email }->flush; };
  1         55  
115             }
116              
117             sub DESTROY
118             {
119 1     1   582 my ( $self, %parameters ) = @_;
120 1         8 $log->debug( 'DESTROY', { package => __PACKAGE__, GLOBAL_PHASE => ${^GLOBAL_PHASE}, blessed => FALSE } );
121 1 50       8 return if ${^GLOBAL_PHASE} eq 'DESTRUCT';
122              
123 1 50 33     11 if ( blessed( $self ) && $self->isa( __PACKAGE__ ) )
124             {
125 1         7 $log->debug( "DESTROY", { package => __PACKAGE__, GLOBAL_PHASE => ${^GLOBAL_PHASE}, blessed => TRUE } );
126             }
127             else
128             {
129             # TODO
130             }
131             } ## end sub DESTROY
132              
133             =head2 C<connectDB()>
134             =cut
135              
136             sub connectDB()
137             {
138 0     0 1   my ( $self, %parameters ) = @_;
139 0           $log->debug( "connectDB", { package => __PACKAGE__ } );
140              
141 0           my ( $database, $host, $password, $port, $username, $connection );
142              
143 0 0         if ( %parameters )
    0          
144             {
145 0           $connection = $parameters{ connection };
146 0           $database = $parameters{ database };
147 0           $host = $parameters{ host };
148 0           $password = $parameters{ password };
149 0           $port = $parameters{ port };
150 0           $username = $parameters{ username };
151             } ## end if ( %parameters )
152             elsif ( defined $self->{ configurations }->{ database } )
153             {
154 0           $connection = $self->{ configurations }->{ database }->{ connection };
155 0           $database = $self->{ configurations }->{ database }->{ database };
156 0           $host = $self->{ configurations }->{ database }->{ host };
157 0           $password = $self->{ configurations }->{ database }->{ password };
158 0           $port = $self->{ configurations }->{ database }->{ port };
159 0           $username = $self->{ configurations }->{ database }->{ username };
160             } ## end elsif ( defined $self->{ ...})
161             else
162             {
163 0           $log->error( "Tentando conectar mas sem configuração de DB..." );
164 0           return FALSE;
165             }
166              
167 0           my $driverConnection = $driverConnections{ lc $connection };
168 0 0         if ( $driverConnection )
169             {
170 0           eval {
171 0           require DBI;
172 0           require "$driverConnection->{ module }";
173             };
174              
175 0           my $dsn = sprintf( $driverConnection->{ dsn }, $database, $host, $port );
176 0 0         my ( $scheme, $driver, $attr_string, $attr_hash, $driver_dsn ) = DBI->parse_dsn( $dsn ) or die "Can't parse DBI DSN '$dsn'";
177 0           my $data_source = "$scheme:$driver:$driver_dsn";
178 0           $log->info( $data_source );
179 0           $log->info( "Conectando no banco $username\@$host\:$database" );
180 0           $self->{ database }->{ connection } = eval { DBI->connect( $data_source, $username, $password, $attr_hash ); };
  0            
181              
182 0 0         if ( $@ )
183             {
184 0           $log->error( "Erro ao conectar ao banco [ $data_source ] [ $username\@$host:$port ]." );
185 0           $log->error( @_ );
186 0           return FALSE;
187             } ## end if ( $@ )
188             } ## end if ( $driverConnection...)
189             else
190             {
191 0           $log->error( "Connection [ $connection ] não existe configuração..." );
192 0           return FALSE;
193             }
194              
195 0           return $self->{ database }->{ connection };
196             } ## end sub connectDB
197              
198             =head2 C<begin_work()>
199             =cut
200              
201             sub begin_work()
202             {
203 0     0 1   my ( $self, %parameters ) = @_;
204 0 0         if ( !defined $self->{ database }->{ connection } )
205             {
206 0           $log->error( "Tentando começar uma transação sem uma conexão com DB..." );
207 0           return FALSE;
208             }
209 0 0         my $rc = $self->{ database }->{ connection }->begin_work or die $self->{ database }->{ connection }->errstr;
210 0           return $rc;
211             } ## end sub begin_work
212              
213             =head2 C<commit()>
214             =cut
215              
216             sub commit()
217             {
218 0     0 1   my ( $self, %parameters ) = @_;
219 0 0         if ( !defined $self->{ database }->{ connection } )
220             {
221 0           $log->error( "Tentando commitar uma transação sem uma conexão com DB..." );
222 0           return FALSE;
223             }
224 0 0         my $rc = $self->{ database }->{ connection }->commit or die $self->{ database }->{ connection }->errstr;
225 0           return $rc;
226             } ## end sub commit
227              
228             =head2 C<rollback()>
229             =cut
230              
231             sub rollback()
232             {
233 0     0 1   my ( $self, %parameters ) = @_;
234 0 0         if ( !defined $self->{ database }->{ connection } )
235             {
236 0           $log->error( "Tentando reverter uma transação sem uma conexão com DB..." );
237 0           return FALSE;
238             }
239 0 0         my $rc = $self->{ database }->{ connection }->rollback or die $self->{ database }->{ connection }->errstr;
240 0           return $rc;
241             } ## end sub rollback
242              
243             =head2 C<prepareQuery()>
244             =cut
245              
246             sub prepareQuery
247             {
248 0     0 1   my ( $self, %parameters ) = @_;
249 0           my $sql = $parameters{ sql };
250              
251 0 0         my $sth = $self->{ database }->{ connection }->prepare( $sql ) or die $self->{ database }->{ connection }->errstr;
252 0           return $sth;
253             } ## end sub prepareQuery
254              
255             =head2 C<doQuery()>
256             =cut
257              
258             sub doQuery
259             {
260 0     0 1   my ( $self, %parameters ) = @_;
261 0           my $sql = $parameters{ sql };
262              
263 0 0         my $sth = $self->{ database }->{ connection }->do( $sql ) or die $self->{ database }->{ connection }->errstr;
264 0           return $sth;
265             } ## end sub doQuery
266              
267             =head2 C<executeQuery()>
268             =cut
269              
270             sub executeQuery()
271             {
272 0     0 1   my ( $self, %parameters ) = @_;
273 0           my $sql = $parameters{ sql };
274              
275 0 0         $self->connectDB() unless ( defined( $self->{ database }->{ connection } ) );
276              
277 0           my $sth = $self->prepareQuery( sql => $sql );
278 0 0         my $res = $sth->execute() or die $self->{ database }->{ connection }->errstr;
279              
280 0           my @rows;
281             my $line;
282 0           push( @rows, $line ) while ( $line = $sth->fetchrow_hashref );
283              
284 0           return @rows;
285             } ## end sub executeQuery
286              
287             =head2 C<teste()>
288             =cut
289              
290             sub teste()
291             {
292 0     0 1   my ( $self, %parameters ) = @_;
293              
294 0           $self->{ configurations }->{ teste } = 'LALA';
295 0           return $self;
296             } ## end sub teste
297              
298             =head2 C<getFileMD5()>
299             -------------------------------------------------------------------------------
300             Retorna o MD5 do arquivo
301             Parametro 1 - Caminho e nome do arquivo a ser calculado
302             Retorna o MD5 do arquivo informado
303             -------------------------------------------------------------------------------
304             =cut
305              
306             sub getFileMD5()
307             {
308 0     0 1   my ( $self, %parameters ) = @_;
309 0           my $file = $parameters{ file };
310              
311 0 0         return FALSE unless ( -e $file );
312              
313 0           my $return;
314              
315 0           eval { require Digest::MD5; };
  0            
316 0 0         if ( $@ )
317             {
318 0           $log->error( 'Package Digest::MD5 não encontrado...' );
319 0           return FALSE;
320             }
321              
322 0 0         if ( open( my $fh, $file ) )
323             {
324 0           binmode( $fh );
325 0           $return = Digest::MD5->new->addfile( $fh )->hexdigest;
326 0           close( $fh );
327             } ## end if ( open( my $fh, $file...))
328             else
329             {
330 0           $log->error( "Não foi possível abrir o arquivo [ $file ]..." );
331             }
332              
333 0           return $return;
334             } ## end sub getFileMD5
335              
336             =head2 C<parseBlockText()>
337             =cut
338              
339             sub parseBlockText()
340             {
341 0     0 1   my $me = ( caller( 0 ) )[ 3 ];
342 0           my $parent = ( caller( 1 ) )[ 3 ];
343 0           $log->debug( "parseBlockText", { package => __PACKAGE__, file => __FILE__, me => $me, parent => $parent } );
344              
345 0           my ( $self, %parameters ) = @_;
346 0           my $file = $parameters{ file };
347 0           my $layout = $parameters{ layout };
348 0           my $length_type = $parameters{ length_type };
349 0           my $retorno = { rows => undef, error => 0, message => undef, };
350              
351 0 0 0       if ( !$file || !-e $file )
352             {
353 0           $log->error( "O arquivo [ $file ] não existe..." );
354 0           $retorno->{ message } = 'Arquivo não existe';
355 0           $retorno->{ error } = TRUE;
356 0           return $retorno;
357             } ## end if ( !$file || !-e $file...)
358              
359 0           $log->info( "Começando a parsear o arquivo [ $file ]..." );
360 0 0         open FH, "<:encoding(UTF-8)", $file or die "Erro ao abrir o arquivo [ $file ]...";
361 0           while ( my $linha = <FH> )
362             {
363 0           $linha =~ s/\n|\r//g;
364              
365 0           my $tipo_de_registro = substr( $linha, 0, $length_type );
366 0           my $posicao = 0;
367 0           my $auxiliar = ();
368              
369 0 0         if ( !$layout->{ $tipo_de_registro } )
370             {
371 0           my $tipos = join ",", sort keys %{ $layout };
  0            
372 0           my $msg = "Não existe o tipo de registro [ $tipo_de_registro ] no layout cadastrado [ $tipos ] na linha [ $. ]...";
373 0           $log->error( $msg );
374 0           $log->error( "Linha [ $. ] = [$linha]..." );
375 0           $retorno->{ rows } = undef;
376 0           $retorno->{ message } = $msg;
377 0           $retorno->{ error } = 1;
378 0           return $retorno;
379             } ## end if ( !$layout->{ $tipo_de_registro...})
380              
381 0           my $tamanho_da_linha_no_layout = $layout->{ $tipo_de_registro }->{ total_length };
382 0           my $tamanho_da_linha_no_arquivo = length( $linha );
383              
384 0 0         if ( $tamanho_da_linha_no_arquivo != $tamanho_da_linha_no_layout )
385             {
386 0           my $msg = "Tamanho da linha [ $. ] do tipo [ $tipo_de_registro ] no arquivo [ $file ] ($tamanho_da_linha_no_arquivo) esta diferente do layout ($tamanho_da_linha_no_layout)";
387              
388             #my $msg = "Tamanho da linha [ %d ] do tipo [ %s ] no arquivo [ %s ] ( %d ) esta diferente do layout ( %d )";
389             #$log->error( $msg );
390 0           $retorno->{ rows } = undef;
391 0           $retorno->{ message } = $msg;
392 0           $retorno->{ error } = 1;
393 0           return $retorno;
394             } ## end if ( $tamanho_da_linha_no_arquivo...)
395              
396 0           foreach my $field ( @{ $layout->{ $tipo_de_registro }->{ fields } } )
  0            
397             {
398 0           $auxiliar->{ $field->{ field } } = $self->trim( substr( $linha, $posicao, $field->{ length } ) );
399 0           $posicao += $field->{ length };
400              
401 0           my $out = $field->{ out };
402              
403 0 0 0       if ( $field->{ match } )
    0          
404             {
405 0 0         if ( $auxiliar->{ $field->{ field } } !~ /$field->{match}/ )
406             {
407 0           my $msg = "O campo [ $field->{ field } ] com o valor [ $auxiliar->{ $field->{ field } } ] não corresponde a regra de validação [ $field->{match} ] no registro [ $. ]...";
408 0           $log->error( $msg );
409 0           $retorno->{ rows } = undef;
410 0           $retorno->{ message } = $msg;
411 0           $retorno->{ error } = 1;
412 0           return $retorno;
413             } ## end if ( $auxiliar->{ $field...})
414              
415 0 0         if ( $out )
416             {
417 0           $out =~ s/\?\?\?/$auxiliar->{ $field->{field} }/g;
418 0           $auxiliar->{ $field->{ field } } = eval( $out );
419             }
420             } ## end if ( $field->{ match }...)
421             elsif ( $out && $out !~ /\$/ )
422             {
423 0           $out =~ s/\?\?\?/$auxiliar->{ $field->{field} }/g;
424 0           $auxiliar->{ $field->{ field } } = eval( $out );
425             }
426             } ## end foreach my $field ( @{ $layout...})
427              
428 0           push( @{ $retorno->{ rows }->{ $tipo_de_registro } }, $auxiliar );
  0            
429             } ## end while ( my $linha = <FH> ...)
430 0           return $retorno;
431             } ## end sub parseBlockText
432              
433             =head2 C<parseCSV()>
434             =cut
435              
436             sub parseCSV()
437             {
438 0     0 1   my ( $self, %parameters ) = @_;
439 0           my $file = $parameters{ file };
440 0   0       my $sep_char = $parameters{ sep_char } // ',';
441 0   0       my $quote_char = $parameters{ quote_char } // '"';
442 0   0       my $encoding = $parameters{ encoding } // 'iso-8859-1';
443 0           my $originalHeader = $parameters{ originalHeader };
444 0           my $retorno = { rows => undef, error => 0, message => undef, };
445              
446 0           $log->info( "Começando a parsear o arquivo [ $file ]..." );
447              
448             # Read/parse CSV
449 0           eval { require Text::CSV_XS; };
  0            
450              
451 0 0         if ( $@ )
452             {
453 0           $log->error( $@ );
454 0           return $retorno;
455             }
456              
457 0           my $csv = eval {
458              
459 0           Text::CSV_XS->new(
460             {
461             binary => 1,
462             auto_diag => 0,
463             diag_verbose => 0,
464             blank_is_undef => 1,
465             empty_is_undef => 1,
466             allow_loose_quotes => 0,
467             sep_char => $sep_char,
468             quote_char => $quote_char,
469             strict => 1,
470             }
471             );
472              
473             };
474              
475 0 0         if ( $@ )
476             {
477 0           $log->error( $@ );
478 0           $retorno->{ error } = 1;
479 0           $retorno->{ message } = $@;
480             } ## end if ( $@ )
481             else
482             {
483             $csv->callbacks(
484             after_parse => sub {
485              
486             # Limpar os espaços em branco no começo e no final de cada campo.
487 0 0   0     map { defined $_ ? ( ( $_ =~ /^\s+$/ ) ? ( $_ = undef ) : ( s/^\s+|\s+$//g ) ) : undef } @{ $_[ 1 ] };
  0 0          
  0            
488             },
489              
490             #error => sub {
491             # my ( $err, $msg, $pos, $recno, $fldno ) = @_;
492             # Text::CSV_XS->SetDiag(0);
493             # return;
494             #}
495 0           );
496              
497 0           my @rows;
498 0 0         open my $fh, "<:encoding($encoding)", $file or die "$file->{path}: $!";
499              
500 0           my @header = eval {
501             $csv->header(
502             $fh,
503             {
504             detect_bom => ( $encoding =~ /utf-8/i ) ? 1 : 0,
505             sep_set => [ $sep_char ],
506             munge_column_names => sub {
507 0     0     uc;
508             }
509             }
510 0 0         );
511             };
512              
513 0           my ( $errorCode, $errorMessage, $position, $line, $field ) = $csv->error_diag();
514 0 0         if ( $errorCode > 0 )
515             {
516 0           undef @rows;
517 0           $retorno->{ error } = $errorCode;
518 0           $retorno->{ message } = "$errorMessage @ linha $line, posição $position, campo $field";
519 0           return $retorno;
520             } ## end if ( $errorCode > 0 )
521 0           $retorno->{ header } = \@header;
522              
523 0 0         if ( $originalHeader )
524             {
525 0 0         if ( !$self->validaHeader( originalHeader => $originalHeader, header => \@header ) )
526             {
527 0           my $msg = 'Header do arquivo está diferente do layout...';
528 0           $retorno->{ error } = 1;
529 0           $retorno->{ message } = $msg;
530 0           return $retorno;
531             } ## end if ( !$self->validaHeader...)
532             else
533             {
534 0           $log->info( "*** Header validado com sucesso ***" );
535             }
536             } ## end if ( $originalHeader )
537              
538 0           while ( my $row = $csv->getline( $fh ) )
539             {
540 0           push @rows, $row;
541             }
542 0           close $fh;
543              
544 0           ( $errorCode, $errorMessage, $position, $line, $field ) = $csv->error_diag();
545              
546 0 0 0       if ( $errorCode > 0 && $errorCode != 2012 )
547             {
548 0           undef @rows;
549 0 0         if ( $errorMessage =~ /Inconsistent number of fields/i )
550             {
551 0           $errorMessage = 'Número inconsistente de campos';
552             }
553 0           $retorno->{ error } = $errorCode;
554 0           $retorno->{ message } = "$errorMessage @ linha $line, posição $position, campo $field";
555             } ## end if ( $errorCode > 0 &&...)
556             else
557             {
558 0           @{ $retorno->{ rows } } = @rows;
  0            
559             }
560             } ## end else [ if ( $@ ) ]
561              
562 0           return $retorno;
563             } ## end sub parseCSV
564              
565             =head2 C<validaHeader()>
566             =cut
567              
568             sub validaHeader()
569             {
570 0     0 1   $log->debug( "validaHeader", { package => __PACKAGE__ } );
571 0           my ( $self, %parameters ) = @_;
572 0           my @originalHeader = @{ $parameters{ originalHeader } };
  0            
573 0           my @header = @{ $parameters{ header } };
  0            
574              
575 0           $log->info( "Validando o header do arquivo..." );
576              
577 0           my $originalHeaderString = join( '_&_', @originalHeader );
578 0           my $headerString = join( '_&_', @header );
579              
580 0 0         return ( $originalHeaderString eq $headerString ) ? TRUE : FALSE;
581             } ## end sub validaHeader
582              
583             =head2 C<validaArCorreios()>
584             =cut
585              
586             sub validaArCorreios()
587             {
588             #$log->debug( "validaArCorreios", { package => __PACKAGE__ } );
589 0     0 1   my ( $self, %parameters ) = @_;
590 0           my $arCorreios = $parameters{ ar };
591              
592 0 0         return ( $arCorreios =~ /^([a-zA-Z]{2})(\d{9})([a-zA-Z]{2})$/ ) ? TRUE : FALSE;
593             } ## end sub validaArCorreios
594              
595             =head2 C<trim()>
596             =cut
597              
598             sub trim()
599             {
600 0     0 1   my ( $self, $string ) = @_;
601              
602 0           eval { $string =~ s/^\s+|\s+$//g; };
  0            
603              
604 0 0         if ( $@ )
605             {
606 0           $log->error( "Erro ao fazer o trim na string $string" );
607             }
608              
609 0           return $string;
610             } ## end sub trim
611              
612             =head2 C<getTimeStampHash()>
613             =cut
614              
615             #-------------------------------------------------------------------------------
616             # Retorna o timestamp atual do sistema em forma de HASH
617             #-------------------------------------------------------------------------------
618             sub getTimeStampHash
619             {
620 0     0 1   my ( $self, %parameters ) = @_;
621 0           my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime( time );
622              
623             {
624 0           year => $year + 1900,
625             month => sprintf( '%02d', ( $mon + 1 ) ),
626             day => sprintf( '%02d', $mday ),
627             hour => sprintf( '%02d', $hour ),
628             min => sprintf( '%02d', $min ),
629             sec => sprintf( '%02d', $sec ),
630             wday => $wday,
631             yday => $yday,
632             isdst => $isdst
633             };
634             } ## end sub getTimeStampHash
635              
636             =head2 C<getTimeStamp()>
637             =cut
638              
639             #-------------------------------------------------------------------------------
640             # Retorna o timestamp atual do sistema
641             #-------------------------------------------------------------------------------
642             sub getTimeStamp
643             {
644 0     0 1   my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime( time );
645 0           return sprintf( "%4d%02d%02d%02d%02d%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec );
646             }
647              
648             #################### main pod documentation begin ###################
649             ## Below is the stub of documentation for your module.
650             ## You better edit it!
651              
652             =encoding UTF-8
653              
654              
655             =head1 NAME
656              
657             Siffra::Tools - Module abstract (<= 44 characters) goes here
658              
659             =head1 SYNOPSIS
660              
661             use Siffra::Tools;
662             blah blah blah
663              
664              
665             =head1 DESCRIPTION
666              
667             Stub documentation for this module was created by ExtUtils::ModuleMaker.
668             It looks like the author of the extension was negligent enough
669             to leave the stub unedited.
670              
671             Blah blah blah.
672              
673              
674             =head1 USAGE
675              
676              
677              
678             =head1 BUGS
679              
680              
681              
682             =head1 SUPPORT
683              
684              
685              
686             =head1 AUTHOR
687              
688             Luiz Benevenuto
689             CPAN ID: LUIZBENE
690             Siffra TI
691             luiz@siffra.com.br
692             https://siffra.com.br
693              
694             =head1 COPYRIGHT
695              
696             This program is free software; you can redistribute
697             it and/or modify it under the same terms as Perl itself.
698              
699             The full text of the license can be found in the
700             LICENSE file included with this module.
701              
702              
703             =head1 SEE ALSO
704              
705             perl(1).
706              
707             =cut
708              
709             #################### main pod documentation end ###################
710              
711             1;
712              
713             # The preceding line will help the module return a true value
714