File Coverage

blib/lib/CTK.pm
Criterion Covered Total %
statement 123 177 69.4
branch 31 88 35.2
condition 20 48 41.6
subroutine 25 39 64.1
pod 27 27 100.0
total 226 379 59.6


line stmt bran cond sub pod time code
1             package CTK; # $Id: CTK.pm 294 2020-09-02 06:36:52Z minus $
2 12     12   700180 use strict;
  12         109  
  12         398  
3 12     12   6520 use utf8;
  12         150  
  12         56  
4              
5             =encoding utf-8
6              
7             =head1 NAME
8              
9             CTK - Command-line ToolKit library (CTKlib)
10              
11             =head1 VERSION
12              
13             Version 2.06
14              
15             =head1 NOTE
16              
17             The 2.00+ versions of this library is not compatible with earlier versions
18              
19             =head1 SYNOPSIS
20              
21             use CTK;
22              
23             my $ctk = new CTK;
24             my $ctk = new CTK (
25             project => 'MyApp',
26             configfile => '/path/to/conf/file.conf',
27             logfile => '/path/to/log/file.log',
28             );
29              
30             =head1 DESCRIPTION
31              
32             CTKlib - is library that provides "extended-features" (utilities) for your robots written on Perl.
33             Most of the functions and methods this module written very simple language and easy to understand.
34             To work with CTKlib, you just need to start using it!
35              
36             =head2 new
37              
38             my $ctk = new CTK;
39             my $ctk = new CTK (
40             project => 'MyApp',
41             configfile => '/path/to/conf/file.conf',
42             logfile => '/path/to/log/file.log',
43             );
44              
45             Main constructor. All the params are optional
46              
47             =over 4
48              
49             =item B
50              
51             configfile => '/etc/myapp/myapp.conf'
52              
53             Path to the configuration file of the your project
54              
55             Default: /etc//.conf
56              
57             =item B
58              
59             datadir => '/path/to/your/data/dir'
60              
61             Directory for application data storing
62              
63             Default: (current directory)
64              
65             =item B
66              
67             debug => 1
68             debug => 'on'
69             debug => 'yes'
70              
71             Debug mode
72              
73             Default: 0
74              
75             =item B
76              
77             ident => "test"
78              
79             Ident string for logs and debugging
80              
81             Default: ""
82              
83             =item B
84              
85             log => 1
86             log => 'on'
87             log => 'yes'
88              
89             Log mode
90              
91             Default: 0
92              
93             =item B
94              
95             logdir => '/var/log/myapp'
96              
97             Log directory of project
98              
99             Default: /var/log/
100              
101             =item B
102              
103             logfile => '/var/log/myapp/myapp.log'
104              
105             Full path to the log file
106              
107             Default: /var/log//.log
108              
109             =item B
110              
111             options => {foo => 'bar'}
112              
113             Command-line options, hash-ref structure. See L
114              
115             Default: {}
116              
117             =item B
118              
119             plugins => [qw/ test /]
120             plugins => "test"
121              
122             Array ref of plugin list or plugin name as scalar:
123              
124             Default: []
125              
126             =item B
127              
128             prefix => "myapp"
129              
130             Prefix of the Your project
131              
132             Default: lc()
133              
134             =item B
135              
136             project => "MyApp"
137             name => "MyApp"
138              
139             Project name
140              
141             Default: $FindBin::Script without file extension
142              
143             =item B
144              
145             root => "/etc/myapp"
146              
147             Root dir of project
148              
149             Default: /etc/
150              
151             =item B
152              
153             suffix => "devel"
154             suffix => "alpha"
155             suffix => "beta"
156             suffix => ".dev"
157              
158             Suffix of the your project. Can use in plugins
159              
160             Default: ""
161              
162             =item B
163              
164             tempdir => "/tmp/myapp"
165              
166             Temp directory of project
167              
168             Default: /tmp/
169              
170             =item B
171              
172             tempfile => "/tmp/myapp/myapp.tmp"
173              
174             Temp file of project
175              
176             Default: /tmp//.tmp
177              
178             =item B
179              
180             test => 1
181             test => 'on'
182             test => 'yes'
183              
184             Test mode
185              
186             Default: 0
187              
188             =item B
189              
190             verbose => 1
191             verbose => 'on'
192             verbose => 'yes'
193              
194             Verbose mode
195              
196             Default: 0
197              
198             =back
199              
200             =head2 again
201              
202             For internal use only (plugins). Please not call this function
203              
204             =head2 configfile
205              
206             my $configfile = $ctk->configfile;
207             $ctk->configfile("/path/to/config/file.conf");
208              
209             Gets and sets configfile value
210              
211             =head2 datadir
212              
213             my $datadir = $ctk->datadir;
214             $ctk->datadir("/path/to/data/dir");
215              
216             Gets and sets datadir value
217              
218             =head2 debug
219              
220             $ctk->debug( "Message" );
221              
222             Prints debug information on STDOUT if is set debug mode.
223             Also sends message to log if log mode is enabled
224              
225             =head2 debugmode
226              
227             $ctk->debugmode;
228              
229             Returns debug flag. 1 - on, 0 - off
230              
231             =head2 error
232              
233             my $error = $ctk->error;
234              
235             Returns error string if occurred any errors while creating the object
236              
237             $ctk->error("error text");
238              
239             Sets new error message and returns it. Also prints message on STDERR if is set debug mode
240             and sends message to log if log mode is enabled
241              
242             =head2 exedir
243              
244             my $exedir = $ctk->exedir;
245              
246             Gets exedir value
247              
248             =head2 load
249              
250             $ctk->load("My::Foo::Package");
251              
252             Internal method for loading modules.
253              
254             Returns loading status: 0 - was not loaded; 1 - was loaded
255              
256             =head2 load_plugins
257              
258             my $summary_status = $self->load_plugins( @plugins );
259              
260             Loads list of plugins and returns summary status
261              
262             =head2 logdir
263              
264             my $logdir = $ctk->logdir;
265             $ctk->logdir("/path/to/log/dir");
266              
267             Gets and sets logdir value
268              
269             =head2 logfile
270              
271             my $logfile = $ctk->logfile;
272             $ctk->logfile("/path/to/log/file.log");
273              
274             Gets and sets logfile value
275              
276             =head2 logmode
277              
278             $ctk->logmode;
279              
280             Returns log flag. 1 - on, 0 - off
281              
282             =head2 origin
283              
284             my $args = $ctk->origin();
285              
286             Returns hash-ref structure to all origin arguments
287              
288             =head2 option
289              
290             my $value = $ctk->option("key");
291              
292             Returns option value by key
293              
294             my $options = $ctk->option;
295              
296             Returns hash-ref structure to all options
297              
298             See L
299              
300             =head2 project, prefix, suffix
301              
302             my $project_name = $ctk->projtct;
303             my $prefix = $ctk->prefix;
304             my $suffix = $ctk->suffix;
305              
306             Returns project, prefix and suffix values
307              
308             =head2 revision
309              
310             my $revision = $ctk->revision;
311              
312             Returns revision value
313              
314             =head2 root
315              
316             my $my_root = $ctk->root; # /etc/
317              
318             Gets my root dir value
319              
320             =head2 silentmode
321              
322             $ctk->silentmode;
323              
324             Returns the verbose flag in the opposite value. 0 - verbose, 1 - silent.
325              
326             See L
327              
328             =head2 status
329              
330             my $status = $ctk->status;
331              
332             Returns boolean status of creating and using the object
333              
334             my $status = $ctk->status( 1 );
335              
336             Sets new status and just returns it
337              
338             =head2 tempfile
339              
340             my $tempfile = $ctk->tempfile;
341             $ctk->tempfile("/path/to/temp/file.tmp");
342              
343             Gets and sets tempfile value
344              
345             =head2 tempdir
346              
347             my $tempdir = $ctk->tempdir;
348             $ctk->tempdir("/path/to/temp/dir");
349              
350             Gets and sets tempdir value
351              
352             =head2 testmode
353              
354             $ctk->testmode;
355              
356             Returns test flag. 1 - on, 0 - off
357              
358             =head2 tms
359              
360             print $ctk->tms; # +0.0080 sec
361              
362             Returns formatted timestamp
363              
364             print $ctk->tms(1); # 0.008000
365              
366             Returns NOT formatted timestamp
367              
368             =head2 verbosemode
369              
370             $ctk->verbosemode;
371              
372             Returns verbose flag. 1 - on, 0 - off
373              
374             See L
375              
376             =head1 VARIABLES
377              
378             use CTK qw/ WIN NULL TONULL ERR2OUT PREFIX /;
379             use CTK qw/ :constants /
380              
381             =over 4
382              
383             =item B
384              
385             Returns string:
386              
387             2>&1
388              
389             =item B
390              
391             Returns NULL device path or name for Windows platforms
392              
393             =item B<%PLUGIN_ALIAS_MAP>
394              
395             This hash is using for sets aliases of plugins, e.g.:
396              
397             use CTK qw/ %PLUGIN_ALIAS_MAP /;
398             $PLUGIN_ALIAS_MAP{myplugin} = "My::Custom::Plugin::Module";
399              
400             =item B
401              
402             Return default prefix: ctk
403              
404             =item B
405              
406             Returns string:
407              
408             >/dev/null 2>&1
409              
410             =item B
411              
412             Returns 1 if Windows platform
413              
414             =back
415              
416             =head1 TAGS
417              
418             =over 4
419              
420             =item B<:constants>
421              
422             Will be exported following variables:
423              
424             WIN, NULL, TONULL, ERR2OUT, PREFIX
425              
426             =item B<:variables>
427              
428             Will be exported following variables:
429              
430             %PLUGIN_ALIAS_MAP
431              
432             =back
433              
434             =head1 HISTORY
435              
436             =over 4
437              
438             =item B<1.00 / 18.06.2012>
439              
440             Init version
441              
442             =item B<2.00 Mon Apr 29 10:36:06 MSK 2019>
443              
444             New edition of the library
445              
446             =back
447              
448             See C file
449              
450             =head1 DEPENDENCIES
451              
452             L,
453             L,
454             L,
455             L,
456             L,
457             L,
458             L,
459             L,
460             L,
461             L,
462             L,
463             L,
464             L,
465             L,
466             L,
467             L,
468             L,
469             L,
470             L,
471             L,
472             L
473             L,
474             L,
475             L,
476             L,
477             L,
478             L,
479             L,
480             L,
481             L
482              
483             =head1 TO DO
484              
485             See C file
486              
487             =head1 BUGS
488              
489             * none noted
490              
491             =head1 SEE ALSO
492              
493             C
494              
495             =head1 AUTHOR
496              
497             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
498              
499             =head1 COPYRIGHT
500              
501             Copyright (C) 1998-2020 D&D Corporation. All Rights Reserved
502              
503             =head1 LICENSE
504              
505             This program is free software; you can redistribute it and/or
506             modify it under the same terms as Perl itself.
507              
508             See C file and L
509              
510             =cut
511              
512 12     12   1336 use vars qw/ $VERSION %PLUGIN_ALIAS_MAP %EXPORT_TAGS @EXPORT_OK /;
  12         25  
  12         965  
513             $VERSION = '2.06';
514              
515 12     12   75 use base qw/Exporter/;
  12         20  
  12         1851  
516              
517 12     12   87 use Carp;
  12         22  
  12         701  
518 12     12   6962 use Time::HiRes qw(gettimeofday);
  12         18054  
  12         61  
519 12     12   8310 use FindBin qw($RealBin $Script);
  12         13741  
  12         1392  
520 12     12   101 use Cwd qw/getcwd/;
  12         23  
  12         472  
521 12     12   67 use File::Spec ();
  12         21  
  12         336  
522 12     12   8138 use CTK::Util qw/ sysconfdir syslogdir isTrueFlag /;
  12         42  
  12         16155  
523              
524             my @exp_constants = qw(
525             WIN NULL TONULL ERR2OUT PREFIX
526             );
527              
528             my @exp_variables = qw(
529             %PLUGIN_ALIAS_MAP
530             );
531              
532             @EXPORT_OK = (
533             @exp_constants,
534             @exp_variables,
535             );
536              
537             %EXPORT_TAGS = (
538             constants => [@exp_constants],
539             variables => [@exp_variables],
540             );
541              
542             %PLUGIN_ALIAS_MAP = (
543             cli => "CTK::Plugin::CLI",
544             configuration => "CTK::Plugin::Config",
545             files => "CTK::Plugin::File",
546             arc => "CTK::Plugin::Archive",
547             compress => "CTK::Plugin::Archive",
548             ftp => "CTK::Plugin::FTP",
549             sftp => "CTK::Plugin::SFTP",
550             );
551              
552             use constant {
553 12 50       25443 WIN => $^O =~ /mswin/i ? 1 : 0,
    50          
    50          
554             NULL => $^O =~ /mswin/i ? 'NUL' : '/dev/null',
555             TONULL => $^O =~ /mswin/i ? '>NUL 2>&1' : '>/dev/null 2>&1',
556             ERR2OUT => '2>&1',
557             PREFIX => "ctk",
558             PLUGIN_FORMAT => "CTK::Plugin::%s",
559             ALOWED_MODES => [qw/debug log test verbose/],
560 12     12   103 };
  12         23  
561              
562             sub new {
563 5     5 1 675 my $class = shift;
564 5         23 my %args = @_;
565 5   50     43 my $options = $args{options} // {};
566 5 50       48 croak("Can't use \"non hash\" struct for the \"options\" param") unless ref($options) eq "HASH";
567 5 50 66     87 my $project = $args{project} // $args{name} // ($Script =~ /^(.+?)\.(pl|t|pm|cgi)$/ ? $1 : $Script);
      66        
568 5   66     46 my $prefix = $args{prefix} // _prj2pfx($project) // PREFIX;
      50        
569 5   100     24 my $plugins = $args{plugins} // [];
570 5 50       20 $plugins = [$plugins] unless ref($plugins);
571 5 50       22 croak("Can't use \"non array\" for the \"plugins\" param") unless ref($plugins) eq "ARRAY";
572              
573             # Create CTK object
574             my $self = bless {
575             status => 0,
576             error => "",
577              
578             # General
579             invocant => scalar(caller(0)),
580             origin => {%args},
581             created => time(),
582             hitime => gettimeofday() * 1,
583             revision => q/$Revision: 294 $/,
584             options => $options,
585             plugins => {},
586              
587             # Modes (defaults)
588             debugmode => 0,
589             logmode => 0,
590             testmode => 0,
591             verbosemode => 0,
592              
593             # Information
594             ident => $args{ident}, # For logs and debugging
595             script => $Script,
596             project => $project,
597             prefix => $prefix,
598             suffix => $args{suffix} // "",
599              
600             # Dirs
601             exedir => $RealBin, # Script dir
602             datadir => $args{datadir} // getcwd(), # Data dir of project. Defaut: current dir
603             tempdir => $args{tempdir}, # Temp dir of project. Default: /tmp/prefix
604             logdir => $args{logdir}, # Log dir of project. Default: /var/log/prefix
605             root => $args{root}, # Root dir of project. Default: /etc/prefix
606              
607             # Files
608             tempfile => $args{tempfile}, # Temp file of project. Default: /tmp/prefix/prefix.tmp
609             logfile => $args{logfile}, # Log file of project. Default: /var/log/prefix/prefix.log
610             configfile => $args{configfile}, # Config file of project. Default: /etc/prefix/prefix.conf
611              
612 5   50     265 }, $class;
      33        
613              
614             # Modes
615 5         20 foreach my $mode ( @{(ALOWED_MODES)}) {
  5         41  
616 20 50       76 $self->{$mode."mode"} = 1 if isTrueFlag($args{$mode});
617             }
618              
619             # Root dir
620 5         41 my $root = $self->{root};
621 5 100 66     28 unless (defined($root) && length($root)) {
622 4         18 $self->{root} = File::Spec->catdir(sysconfdir(), $prefix);
623             }
624              
625             # Config file
626 5         15 my $configfile = $self->{configfile};
627 5 100 66     25 unless (defined($configfile) && length($configfile)) {
628 4         14 $self->{configfile} = File::Spec->catfile(sysconfdir(), $prefix, sprintf("%s.conf", $prefix));
629             }
630              
631             # Temp dir
632 5         16 my $temp = $self->{tempdir};
633 5 50 33     24 unless (defined($temp) && length($temp)) {
634 5         504 $self->{tempdir} = File::Spec->catdir(File::Spec->tmpdir(), $prefix);
635             }
636              
637             # Temp file
638 5         24 my $tempfile = $self->{tempfile};
639 5 50 33     43 unless (defined($tempfile) && length($tempfile)) {
640 5         101 $self->{tempfile} = File::Spec->catfile(File::Spec->tmpdir(), $prefix, sprintf("%s.tmp", $prefix));
641             }
642              
643             # Log dir
644 5         41 my $ldir = $self->{logdir};
645 5 50 33     25 unless (defined($ldir) && length($ldir)) {
646 5         24 $self->{logdir} = File::Spec->catdir(syslogdir(), $prefix);
647             }
648              
649             # Log file
650 5         19 my $logfile = $self->{logfile};
651 5 50 33     23 unless (defined($logfile) && length($logfile)) {
652 5         18 $self->{logfile} = File::Spec->catfile(syslogdir(), $prefix, sprintf("%s.log", $prefix));
653             }
654              
655             # Loading plugins and set status!
656 5         58 $self->{status} = $self->load_plugins(@$plugins);
657              
658 5         29 return $self->again;
659             }
660             sub again {
661 3     3 1 7 my $self = shift;
662 3         13 return $self;
663             }
664              
665             ########################
666             ## Base methods
667             ########################
668             sub debug {
669 0     0 1 0 my $self = shift;
670 0         0 my @dbg = @_;
671 0 0       0 return unless @dbg;
672 0   0     0 my $ident = $self->{ident} // "";
673 0         0 my $msg = join("", @dbg);
674 0 0       0 return unless length($msg);
675 0 0 0     0 $self->log_debug("%s", $msg) if $self->logmode && $self->can("log_debug"); # To log
676 0 0       0 if ($self->debugmode) { # To STDOUT
677 0 0       0 unshift(@dbg, sprintf("%s ", $ident)) if length($ident);
678 0         0 print STDOUT @dbg, "\n";
679             }
680 0         0 return 1;
681             }
682             sub tms {
683 2     2 1 6 my $self = shift;
684 2         4 my $no_format = shift;
685 2         12 my $v = gettimeofday()*1 - $self->{hitime}*1;
686 2 50       8 return $v if $no_format;
687 2         34 return sprintf("%+.*f sec", 4, $v);
688             }
689             sub error {
690 1     1 1 3 my $self = shift;
691 1         2 my @err = @_;
692 1 50       4 if (@err) {
693 0         0 $self->{error} = join("", @err);
694 0   0     0 my $ident = $self->{ident} // "";
695 0 0       0 if (length($self->{error})) {
696 0 0 0     0 $self->log_error("%s", $self->{error}) if $self->logmode && $self->can("log_error"); # To log
697 0 0       0 if ($self->debugmode) { # To STDERR
698 0 0       0 unshift(@err, sprintf("%s ", $ident)) if length($ident);
699 0         0 printf STDERR "%s\n", join("", @err);
700             }
701             }
702             }
703 1         3 return $self->{error};
704             }
705             sub status {
706 5     5 1 616 my $self = shift;
707 5         9 my $s = shift;
708 5 50       48 $self->{status} = $s if defined $s;
709 5 50       49 return $self->{status} ? 1 : 0;
710             }
711              
712             # Modes
713 0     0 1 0 sub testmode { shift->{testmode} }
714 1     1 1 15 sub debugmode { shift->{debugmode} }
715 2     2 1 13 sub logmode { shift->{logmode} }
716 0     0 1 0 sub verbosemode { shift->{verbosemode} }
717 0     0 1 0 sub silentmode { !shift->{verbosemode} }
718              
719             # Information
720             sub revision { # lasy
721 1     1 1 2 my $self = shift;
722 1         2 my $rev = $self->{revision};
723 1 50       12 return $rev =~ /(\d+\.?\d*)/ ? $1 : '0';
724             }
725             sub option {
726 0     0 1 0 my $self = shift;
727 0         0 my $key = shift;
728 0         0 my $opts = $self->{options};
729 0 0       0 return undef unless $opts;
730 0 0       0 return $opts unless defined $key;
731 0         0 return $opts->{$key};
732             }
733 0     0 1 0 sub project { shift->{project} }
734 0     0 1 0 sub prefix { shift->{prefix} }
735 0     0 1 0 sub suffix { shift->{suffix} }
736 4     4 1 13 sub origin { shift->{origin} }
737              
738             # Dirs
739 0     0 1 0 sub exedir { shift->{exedir} }
740 2     2 1 18 sub root { shift->{root} }
741             sub datadir {
742 0     0 1 0 my $self = shift;
743 0         0 my $dir = shift;
744 0 0       0 $self->{datadir} = $dir if defined $dir;
745 0         0 return $self->{datadir};
746             }
747             sub logdir {
748 0     0 1 0 my $self = shift;
749 0         0 my $dir = shift;
750 0 0       0 $self->{logdir} = $dir if defined $dir;
751 0         0 return $self->{logdir};
752             }
753             sub tempdir {
754 0     0 1 0 my $self = shift;
755 0         0 my $dir = shift;
756 0 0       0 $self->{tempdir} = $dir if defined $dir;
757 0         0 return $self->{tempdir};
758             }
759              
760             # Files
761             sub tempfile {
762 0     0 1 0 my $self = shift;
763 0         0 my $file = shift;
764 0 0       0 $self->{tempfile} = $file if defined $file;
765 0         0 return $self->{tempfile};
766             }
767             sub logfile {
768 0     0 1 0 my $self = shift;
769 0         0 my $file = shift;
770 0 0       0 $self->{logfile} = $file if defined $file;
771 0         0 return $self->{logfile};
772             }
773             sub configfile {
774 2     2 1 6 my $self = shift;
775 2         7 my $file = shift;
776 2 50       9 $self->{configfile} = $file if defined $file;
777 2         15 return $self->{configfile};
778             }
779              
780             # Loading plugin's module
781             sub load_plugins {
782 7     7 1 16 my $self = shift;
783 7         19 my @plugins = @_;
784 7         15 my $in = $self->{plugins};
785 7         25 my $ret = 1;
786 7         15 my %seen = ();
787 7         18 for (@plugins) {$seen{lc($_)} = 1}
  8         19  
788 7         24 foreach my $plugin (keys %seen) {
789 8 50       35 next if $in->{$plugin}->{inited};
790             my $module = exists($PLUGIN_ALIAS_MAP{$plugin})
791 8 100       50 ? $PLUGIN_ALIAS_MAP{$plugin}
792             : sprintf(PLUGIN_FORMAT, ucfirst($plugin));
793 8         42 my $loading_status = $self->load($module);
794 8         18 my $inited = 0;
795 8 50       27 if ($loading_status) {
796 8 50       96 if (my $init = $module->can("init")) {
797 8         29 $inited = $init->($self);
798             }
799             } else {
800 0         0 $ret = 0;
801             }
802 8         60 $in->{$plugin} = {
803             module => $module,
804             loaded => $loading_status,
805             inited => $inited,
806             };
807             };
808 7         49 return $ret;
809             }
810             sub load {
811 8     8 1 18 my $self = shift;
812 8         12 my $module = shift;
813 8         77 my $file = sprintf("%s.pm", join('/', split('::', $module)));
814 8 50       35 return 1 if exists $INC{$file};
815 8         26 eval { require $file; };
  8         3905  
816 8 50       45 if ($@) {
817 0         0 $self->error("Failed to load $file: $@");
818 0         0 return 0;
819             }
820 8         28 return 1;
821             }
822              
823             sub _prj2pfx {
824 4     4   9 my $prj = shift;
825 4 50       15 return unless defined($prj);
826 4         14 $prj =~ s/[^a-z0-9_\-.]/_/ig;
827 4         10 $prj =~ s/_{2,}/_/g;
828 4 50       18 return unless length($prj);
829 4         26 return lc($prj);
830             }
831              
832             1;
833              
834             __END__