File Coverage

blib/lib/Crane.pm
Criterion Covered Total %
statement 81 101 80.2
branch 14 34 41.1
condition 0 6 0.0
subroutine 18 18 100.0
pod 0 2 0.0
total 113 161 70.1


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2              
3              
4             package Crane;
5              
6              
7 1     1   2096 use Crane::Base;
  1         15  
  1         6  
8 1     1   557 use Crane::Config;
  1         5  
  1         166  
9 1     1   760 use Crane::Options qw( :opts options );
  1         5  
  1         179  
10              
11 1     1   12 use File::Basename qw( basename dirname );
  1         1  
  1         59  
12 1     1   5 use File::Find qw( find );
  1         2  
  1         80  
13 1     1   7 use File::Spec::Functions qw( catdir splitdir );
  1         21  
  1         316  
14              
15              
16             our $VERSION = '1.03.0011';
17              
18              
19             sub get_package_path {
20            
21 10     10 0 14 my ( $package ) = @_;
22            
23 10         30 my @path = split m{::}si, $package;
24 10         15 $path[-1] .= '.pm';
25            
26 10         59 return catdir(@path);
27            
28             }
29              
30              
31             sub create_package_alias {
32            
33 5     5 0 9 my ( $original, $alias ) = @_;
34            
35             eval qq{
36             package $alias;
37            
38             use $original;
39            
40             1;
41 1 50   1   9 } or do {
  1     1   2  
  1     1   11  
  1     1   6  
  1     1   2  
  1         5  
  1         5  
  1         2  
  1         78  
  1         694  
  1         3  
  1         163  
  1         9  
  1         4  
  1         42  
  5         470  
42 0         0 confess($EVAL_ERROR);
43             };
44            
45             # Tell to Perl that module is read
46 5         24 $INC{ get_package_path($alias) } = $INC{ get_package_path($original) };
47            
48             {
49 1     1   7 no strict 'refs';
  1         2  
  1         222  
  5         10  
50            
51             # Create alias
52 5         4 *{ "${alias}::" } = \*{ "${original}::" };
  5         48  
  5         15  
53             }
54            
55 5         33 return;
56            
57             }
58              
59              
60             sub import {
61            
62 3     3   1309 my ( undef, %params ) = @_;
63            
64 3         9 my $caller = caller;
65            
66 3 100       29 Crane::Base->import(ref $params{'base'} eq 'ARRAY' ? @{ $params{'base'} } : ());
  1         11  
67 3         1271 Crane::Base->export_to_level(1, $caller);
68            
69             {
70 1     1   6 no strict 'refs';
  1         2  
  1         335  
  3         7  
71 3         5 push @{ "${caller}::ISA" }, @{ __PACKAGE__ . '::ISA' };
  3         15  
  3         34  
72             }
73            
74             # Predefined options
75 3 50       49 my @options = (
76             [ 'daemon|M!', 'Run as daemon.', { 'default' => $params{'name'} ? 1 : 0 } ],
77             $OPT_SEPARATOR,
78             [ 'config|C=s', 'Path to configuration file.' ],
79             [ 'pid|P=s', 'Path to PID file.' ],
80             $OPT_SEPARATOR,
81             [ 'log|O=s', 'Path to log file.' ],
82             [ 'log-error|E=s', 'Path to error log file.' ],
83             $OPT_SEPARATOR,
84             [ 'debug|D!', 'Debug output.' ],
85             [ 'verbose|V!', 'Verbose output.' ],
86             $OPT_SEPARATOR,
87             $OPT_VERSION,
88             $OPT_HELP,
89             );
90            
91             # Custom options are going to the head
92 3 100       14 if ( ref $params{'options'} eq 'ARRAY' ) {
93 1         2 unshift @options, @{ $params{'options'} }, $OPT_SEPARATOR;
  1         3  
94             }
95            
96 3         17 options(@options);
97            
98             # User defined settings
99 3 100       11 if ( ref $params{'config'} eq 'HASH' ) {
100 1 50       5 config(
101             $params{'config'},
102             options->{'config'} ? options->{'config'} : (),
103             );
104             }
105            
106             # Create namespace
107 3 100       12 if ( defined $params{'namespace'} ) {
108 1     1   8 no warnings 'File::Find';
  1         2  
  1         838  
109            
110 1         54 my $path = catdir(dirname(__FILE__), __PACKAGE__);
111            
112             # Create alias for root package
113 1         6 create_package_alias(__PACKAGE__, $params{'namespace'});
114            
115             # Create alias for each subpackage
116 1         3 my @packages = ();
117            
118             find(
119             sub {
120 5 100   5   217 if ( my ( $filename ) = $File::Find::name =~ m{^\Q$path\E/?(.+)[.]pm$}si ) {
121 4         13 push @packages, join '::', splitdir($filename);
122             }
123             },
124            
125 1         170 $path,
126             );
127            
128 1         38 foreach my $package ( @packages ) {
129 4         16 create_package_alias(__PACKAGE__ . "::$package", $params{'namespace'} . "::$package");
130             }
131             }
132            
133             # Run as daemon
134 3 50       10 if ( options->{'daemon'} ) {
135 0         0 local $OUTPUT_AUTOFLUSH = 1;
136            
137 0   0     0 $params{'name'} //= basename($PROGRAM_NAME) =~ s{[.]p[lm]$}{}rsi;
138            
139             # Prepare PID file
140 0   0     0 my $pid_filename = options->{'pid'} || catdir('run', "$params{'name'}.pid");
141 0         0 my $pid_prev = undef;
142            
143 0 0       0 open my $fh_pid, '+>>:encoding(UTF-8)', $pid_filename or confess($OS_ERROR);
144 0         0 seek $fh_pid, 0, 0;
145            
146 0         0 $pid_prev = <$fh_pid>;
147            
148 0 0       0 if ( $pid_prev ) {
149 0         0 chomp $pid_prev;
150             }
151            
152             # Check if process is already running
153 0 0       0 my $is_working = $pid_prev ? kill 0, $pid_prev : 0;
154            
155 0 0       0 if ( not $is_working ) {
156             # Fork
157 0 0       0 if ( my $pid = fork ) {
158 0         0 truncate $fh_pid, 0;
159 0 0       0 print { $fh_pid } "$pid\n" or confess($OS_ERROR);
  0         0  
160 0 0       0 close $fh_pid or confess($OS_ERROR);
161            
162 0         0 exit 0;
163             }
164             } else {
165 0         0 die "Process is already running: $pid_prev\n";
166             }
167            
168 0 0       0 close $fh_pid or confess($OS_ERROR);
169             }
170            
171 3         108 return;
172            
173             }
174              
175              
176             1;
177              
178              
179             =head1 NAME
180              
181             Crane - Helpers for development in Perl
182              
183              
184             =head1 SYNOPSIS
185              
186             use Crane;
187            
188             ...
189            
190             use Crane ( 'name' => 'example' );
191              
192              
193             =head1 DESCRIPTION
194              
195             Helpers for development in Perl. Includes the most modern technics and rules.
196              
197             Also imports modules as L;
198              
199              
200             =head2 Import options
201              
202             You can specify these options when using module:
203              
204             =over
205              
206             =item B
207              
208             Script name, used when run as daemon.
209              
210             If defined, run as daemon by default. Use B<--no-daemon> command line option to
211             cancel this behavior.
212              
213             =item B
214              
215             Array (reference) to list of base modules.
216              
217             =item B
218              
219             Array (reference) of options which will be added to the head of L list.
220              
221             =item B
222              
223             Hash (reference) with user defined default settings.
224              
225             =item B
226              
227             Custom namespace. Please, look at L below.
228              
229             =back
230              
231              
232             =head1 OPTIONS
233              
234             These options are available by default. You can define your custom options if
235             specify it in the import options.
236              
237             =over
238              
239             =item B<-M>, B<--daemon>, B<--no-daemon>
240              
241             Runs as daemon.
242              
243             =item B<-C> I, B<--config>=I
244              
245             Path to configuration file.
246              
247             =item B<-P> I, B<--pid>=I
248              
249             Path to PID file.
250              
251             =item B<-O> I, B<--log>=I
252              
253             Path to messages log file.
254              
255             =item B<-E> I, B<--log-error>=I
256              
257             Path to errors log file.
258              
259             =item B<-D>, B<--debug>, B<--no-debug>
260              
261             Debug output.
262              
263             =item B<-V>, B<--verbose>, B<--no-verbose>
264              
265             Verbose output.
266              
267             =item B<--version>
268              
269             Shows version information and exits.
270              
271             =item B<--help>
272              
273             Shows help and exits.
274              
275             =back
276              
277              
278             =head1 RETURN VALUE
279              
280             In case of running as daemon will return 1 if process is already running.
281              
282              
283             =head1 DIAGNOSTICS
284              
285             =over
286              
287             =item Process is already running: I<%d>
288              
289             Where I<%d> is a PID.
290              
291             You tried to run application as daemon while another copy is running.
292              
293             =back
294              
295              
296             =head1 EXAMPLES
297              
298              
299             =head2 Singleton usage
300              
301             use Crane;
302            
303             ...
304            
305             use Crane ( 'base' => qw( Mojolicious::Controller ) );
306              
307              
308             =head2 Daemon usage
309              
310             use Crane ( 'name' => 'example' );
311              
312              
313             =head2 Configure options
314              
315             use Crane ( 'options' => [
316             [ 'from|F=s', 'Start of the interval.', { 'required' => 1 } ],
317             [ 'to|F=s', 'End of the interval.', { 'required' => 1 } ],
318             ] );
319              
320             As a result you have these two options, a separator and default options.
321              
322              
323             =head2 Basic namespace usage
324              
325             package My;
326            
327             use Crane (
328             'namespace' => 'My',
329            
330             'config' => {
331             'my' => {
332             'autorun' => 1,
333            
334             'hosts' => [
335             '127.0.0.1',
336             '127.0.0.2',
337             ],
338             },
339             },
340             );
341            
342             1;
343            
344             ...
345            
346             use My;
347             use My::Config;
348             use My::Logger;
349            
350             log_info(config->{'log'});
351              
352              
353             =head2 Advanced namespace usage
354              
355             package My;
356            
357             use Crane::Base;
358             use Crane::Options qw( :opts );
359            
360             require Crane;
361            
362             sub import {
363             my ( $package, $name ) = @_;
364            
365             Crane->import(
366             'namespace' => 'My',
367             'name' => $name,
368            
369             'options' => [
370             [ 'run!', 'Do action at startup.' ],
371             $OPT_SEPARATOR,
372             [ 'host=s@', 'Host name(s).' ],
373             ],
374            
375             'config' => {
376             'my' => {
377             'autorun' => 1,
378            
379             'hosts' => [
380             '127.0.0.1',
381             '127.0.0.2',
382             ],
383             },
384             },
385             );
386            
387             return;
388             }
389            
390             1;
391            
392             ...
393            
394             use My 'my_script';
395            
396             sub main {
397             ...
398            
399             return 0;
400             }
401            
402             exit main();
403              
404              
405             =head1 ENVIRONMENT
406              
407             See L.
408              
409              
410             =head1 FILES
411              
412             =over
413              
414             =item F
415              
416             Configuration files. See L.
417              
418             =item F
419              
420             Log files. See L.
421              
422             =item F
423              
424             Script's PID file.
425              
426             =back
427              
428              
429             =head1 BUGS
430              
431             Please report any bugs or feature requests to
432             L or to
433             L.
434              
435              
436             =head1 AUTHOR
437              
438             Tema Novikov,
439              
440              
441             =head1 COPYRIGHT AND LICENSE
442              
443             Copyright (C) 2013-2014 Tema Novikov.
444              
445             This library is free software; you can redistribute it and/or modify it under
446             the terms of the Artistic License 2.0. For details, see the full text of the
447             license in the file LICENSE.
448              
449              
450             =head1 SEE ALSO
451              
452             =over
453              
454             =item * B
455              
456             L
457              
458             =item * B
459              
460             L
461              
462             =back