File Coverage

blib/lib/CTK/App.pm
Criterion Covered Total %
statement 74 98 75.5
branch 17 44 38.6
condition 9 34 26.4
subroutine 12 15 80.0
pod 7 7 100.0
total 119 198 60.1


line stmt bran cond sub pod time code
1             package CTK::App; # $Id: App.pm 278 2020-03-24 07:24:38Z minus $
2 2     2   71105 use strict;
  2         12  
  2         62  
3 2     2   633 use utf8;
  2         16  
  2         10  
4              
5             =encoding utf-8
6              
7             =head1 NAME
8              
9             CTK::App - Application interface
10              
11             =head1 VERSION
12              
13             Version 1.01
14              
15             =head1 SYNOPSIS
16              
17             use CTK::App;
18              
19             my $ctk = new CTK::App;
20             my $ctk = new CTK::App (
21             project => 'MyApp',
22             ident => "myapp",
23             root => ".",
24             confopts => {... Config::General options ...},
25             configfile => '/path/to/conf/file.conf',
26             log => 1,
27             logfile => '/path/to/log/file.log',
28             );
29              
30             =head1 DESCRIPTION
31              
32             The module provides application functionality
33              
34             Features:
35              
36             =over 8
37              
38             =item *
39              
40             Configuration supported as CTK plugin
41              
42             =item *
43              
44             Logging supported as CTK plugin
45              
46             =back
47              
48             =head2 CONFIGURATION
49              
50             For enabling configuration specify the follow arguments in constructor:
51              
52             root => "/path/to/conf",
53             configfile => '/path/to/conf/file.conf',
54              
55             See L
56              
57             =head3 ARGUMENTS
58              
59             =over 8
60              
61             =item B
62              
63             Path to the configuration file of the your project
64              
65             Default: /etc//.conf
66              
67             See L
68              
69             =item B
70              
71             root => "/path/to/conf",
72              
73             The main directory of project (confdir)
74              
75             Default: /etc/
76              
77             See L
78              
79             =back
80              
81             =head2 LOGGER
82              
83             For enabling logger specify the follow arguments in constructor:
84              
85             log => 1,
86              
87             And include follow config-section:
88              
89             #
90             # Logging
91             #
92             # Activate or deactivate the logging: on/off (yes/no). Default: off
93             #
94             LogEnable on
95              
96             #
97             # Loglevel: debug, info, notice, warning, error,
98             # crit, alert, emerg, fatal, except
99             # Default: debug
100             #
101             LogLevel debug
102              
103             #
104             # LogIdent string. Default: none
105             #
106             #LogIdent "foo"
107              
108             #
109             # LogFile: path to log file
110             #
111             # Default: using syslog
112             #
113             #LogFile /var/log/foo.log
114              
115             For forcing disable this logger specify the follow arguments in constructor:
116              
117             no_logger_init => 1,
118              
119             =head3 ARGUMENTS
120              
121             =over 8
122              
123             =item B
124              
125             ident => "foo"
126              
127             Ident string for logs and debugging
128              
129             Default:
130              
131             See L
132              
133             =item B
134              
135             logfacility => Sys::Syslog::LOG_USER
136              
137             Sets facility. See L and L
138              
139             =item B
140              
141             logfile => '/var/log/myapp/myapp.log'
142              
143             Full path to the log file
144              
145             Default: syslog
146              
147             See L
148              
149             =item B
150              
151             Set to 1 for forcing disabling automatic logger initialization on start the your application
152              
153             Default: 0 (logger is enabled)
154              
155             =item B
156              
157             loglevel => "info"
158              
159             This directive specifies the minimum possible priority level. You can use:
160              
161             'debug'
162             'info'
163             'notice' or 'note'
164             'warning' or 'warn'
165             'error' or 'err'
166             'crit'
167             'alert'
168             'emerg' or 'emergency'
169             'fatal'
170             'except' or 'exception'
171              
172             Default: "debug"
173              
174             See L
175              
176             =item B
177              
178             logopts => {
179             utf8 => undef, # Default: 1
180             syslogopts => undef, # Defaukt: "ndelay,pid"
181             socketopts => undef, # Default: undef
182             pure => undef, # Default: 0
183             separator => undef, # Default: " "
184             }
185              
186             Default: undef
187              
188             Logger options. See See L
189              
190             =back
191              
192             =head1 METHODS
193              
194             List of application methods
195              
196             =head2 again
197              
198             This method is called immediately after creating the CTK object.
199              
200             Internal use only!
201              
202             =head2 handle
203              
204             $ctk->handle($handler, @params) or die $ctk->error;
205              
206             Runs handler with parameters
207              
208             Internal use only!
209              
210             =head2 list_handlers
211              
212             my @handlers = $ctk->list_handlers
213              
214             Returns list of registered handlers
215              
216             =head2 lookup_handler
217              
218             my $handler = $ctk->lookup_handler($name) or die "Handler lookup failed";
219              
220             Lookup handler by name. Returns handler or undef while error
221              
222             =head2 register_handler
223              
224             use base qw/ CTK::App /;
225              
226             __PACKAGE__->register_handler(
227             handler => "foo",
228             description => "Foo CLI handler",
229             parameters => {
230             param1 => "foo",
231             param2 => "bar",
232             param3 => 123,
233             },
234             code => sub {
235             ### CODE:
236             my $self = shift;
237             my $meta = shift;
238             my @params = @_;
239              
240             $self->debug(Dumper({
241             meta => $meta,
242             params => [@params],
243             }));
244              
245             return 1;
246             });
247              
248             Method for register new cli handler
249              
250             =head2 run, run_handler
251              
252             my $app = new CTK::MyApp;
253             my $result = $app->run("foo",
254             foo => "one",
255             bar => 1
256             ) or die $app->error;
257              
258             Run handler by name
259              
260             Example of result:
261              
262             {
263             'meta' => {
264             'params' => {
265             'param3' => 123,
266             'param1' => 'foo',
267             'param2' => 'bar'
268             },
269             'name' => 'foo',
270             'description' => 'Foo CLI handler'
271             },
272             'params' => [
273             'foo',
274             'one',
275             'bar',
276             1
277             ],
278             };
279              
280             =head1 HISTORY
281              
282             =over 8
283              
284             =item B<1.00 Mon 29 Apr 22:26:18 MSK 2019>
285              
286             Init version
287              
288             =back
289              
290             See C file
291              
292             =head1 TO DO
293              
294             See C file
295              
296             =head1 BUGS
297              
298             * none noted
299              
300             =head1 SEE ALSO
301              
302             L, L
303              
304             =head1 AUTHOR
305              
306             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
307              
308             =head1 COPYRIGHT
309              
310             Copyright (C) 1998-2020 D&D Corporation. All Rights Reserved
311              
312             =head1 LICENSE
313              
314             This program is free software; you can redistribute it and/or
315             modify it under the same terms as Perl itself.
316              
317             See C file and L
318              
319             =cut
320              
321 2     2   153 use vars qw($VERSION);
  2         15  
  2         108  
322             $VERSION = '1.01';
323              
324 2     2   13 use base qw/ CTK /;
  2         3  
  2         1048  
325              
326 2     2   15 use Carp;
  2         4  
  2         127  
327              
328             use constant {
329 2         1890 APP_PLUGINS => [qw/
330             cli config log
331             /],
332 2     2   13 };
  2         4  
333              
334             my %handler_registry;
335              
336             sub again {
337 2     2 1 5 my $self = shift;
338 2         13 my $args = $self->origin;
339 2         5 my $status = $self->load_plugins(@{(APP_PLUGINS)});
  2         9  
340 2 50       10 $self->{status} = 0 unless $status;
341 2         15 my $config = $self->configobj;
342              
343             # Autoloading logger (data from config)
344 2   100     11 my $log_on = $config->get("logenable") || $config->get("logenabled") || 0;
345 2 0 33     15 if ($self->logmode && (!$args->{no_logger_init}) && $log_on) {
      33        
346 0   0     0 my $logopts = $args->{logopts} || {};
347 0 0       0 my $logfile = defined($args->{logfile}) ? $self->logfile : $config->get("logfile"); # From args or config
348 0 0       0 $logopts->{facility} = $args->{logfacility} if defined($args->{logfacility}); # From args only!
349 0 0 0     0 $logopts->{file} = $logfile if defined($logfile) && length($logfile);
350             $logopts->{ident} = defined($args->{ident})
351             ? $args->{ident}
352 0 0 0     0 : ($config->get("logident") // $self->project); # From args or config
353             $logopts->{level} = defined($args->{loglevel})
354             ? $args->{loglevel}
355 0 0       0 : ($config->get("loglevel")); # From args or config
356 0 0       0 $self->logger_init(%$logopts) or do {
357 0         0 $self->error("Can't initialize logger");
358 0         0 $self->{status} = 0;
359             };
360             }
361              
362 2         19 return $self;
363             }
364              
365             sub register_handler {
366 5     5 1 662 my $class = shift;
367 5 100       12 $class = ref($class) if ref($class);
368 5         23 my %info = @_;
369 5 100       13 $handler_registry{$class} = {} unless exists($handler_registry{$class});
370 5         11 my $handlers = $handler_registry{$class};
371              
372             # Handler data
373 5   33     12 my $name = $info{handler} // $info{name} // '';
      0        
374 5 50       14 croak("Incorrect handler name") unless length($name);
375 5         8 delete $info{handler};
376 5         10 $info{name} = $name;
377             croak("The $name duplicate handler definition")
378 5 50       15 if defined($handlers->{$name});
379 5   50     12 $info{description} //= '';
380 5   50     25 my $params = $info{parameters} || $info{params} || {};
381 5         7 delete $info{parameters};
382 5 50       15 $params = {} unless ref($params) eq "HASH";
383 5         7 $info{params} = $params;
384 5   50 0   9 my $code = $info{code} || sub {return 1};
  0         0  
385 5 50       14 if (ref($code) eq 'CODE') {
386 5         7 $info{code} = $code;
387             } else {
388 0     0   0 $info{code} = sub { $code };
  0         0  
389             }
390              
391 5         20 $handlers->{$name} = {%info};
392 5         17 return 1;
393             }
394             sub lookup_handler {
395 3     3 1 625 my $self = shift;
396 3         5 my $name = shift;
397 3 50       17 return undef unless $name;
398 3   50     10 my $invocant = ref($self) || scalar(caller(0));
399 3         7 my $handlers = $handler_registry{$invocant};
400 3 50       6 return undef unless $handlers;
401 3         13 return $handlers->{$name}
402             }
403             sub list_handlers {
404 0     0 1 0 my $self = shift;
405 0   0     0 my $invocant = ref($self) || scalar(caller(0));
406 0         0 my $handlers = $handler_registry{$invocant};
407 0 0 0     0 return () unless $handlers && ref($handlers) eq 'HASH';
408 0         0 return (sort {$a cmp $b} keys %$handlers);
  0         0  
409             }
410             sub handle {
411 1     1 1 3 my $self = shift;
412 1         2 my $meta = shift;
413 1         3 my @params = @_;
414 1         3 my %info;
415             my $func;
416 1         4 foreach my $k (keys %$meta) {
417 4 50       8 next unless defined $k;
418 4 100       19 if ($k eq 'code') {
419 1         3 $func = $meta->{code};
420 1         2 next;
421             }
422 3         8 $info{$k} = $meta->{$k};
423             }
424 1 50       6 unless(ref($func) eq 'CODE') {
425 0         0 $self->error("Handler code not found!");
426 0         0 return 0;
427             }
428 1         14 my $result = &$func($self, {%info}, @params);
429 1         11 return $result;
430             }
431             sub run_handler {
432 1     1 1 3 my $self = shift;
433 1         3 my $name = shift;
434 1         4 my @params = @_;
435 1 50       4 unless($name) {
436 0         0 $self->error("Incorrect handler name");
437 0         0 return 0;
438             }
439 1 50       4 my $handler = $self->lookup_handler($name) or do {
440 0         0 $self->error(sprintf("Handler lookup failed: %s", $name));
441 0         0 return 0;
442             };
443 1         10 return $self->handle($handler, @params);
444             }
445 1     1 1 5 sub run { goto &run_handler }
446              
447             1;
448              
449             __END__