File Coverage

lib/Getargs/Original.pm
Criterion Covered Total %
statement 90 92 97.8
branch 4 6 66.6
condition n/a
subroutine 9 9 100.0
pod 3 3 100.0
total 106 110 96.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Getargs::Original - remember the original arguments a program was invoked
4             with
5              
6             =head1 SYNOPSIS
7              
8             In your main program:
9              
10             use Getargs::Original;
11              
12             Later on somewhere else
13              
14             require Getargs::Original;
15             exec @{ Getargs::Original->args };
16              
17             =head1 DESCRIPTION
18              
19             Common behaviour for a daemon is to re-exec itself upon receipt of a signal
20             (typically SIGHUP). It is also common to use modules like Getopt::Long to
21             parse command line arguments when the program first starts. To achieve both
22             of these tasks one must store the original contents of C<$0> and C<@ARGV>,
23             as argument processing usually removes elements from @ARGV.
24              
25             B simplifies this task by storing the contents of $0 and
26             @ARGV when it is first used in a program. Later on when the original
27             arguments are required, a singleton instance of B can be
28             used to retrieve the arguments.
29              
30             B is not meant to be instantiated as an object. All of
31             the methods are called as class methods.
32              
33             =begin testing
34 1         41  
35             # damn lexical scoping in pod2test...
36 1     1   618 use vars qw|$dollar_zero @orig_argv|;
  1         105  
  1         53573  
37              
38             # stick a couple of things onto @ARGV
39 0         0 push @ARGV, qw|foo bar baz|;
40              
41             # stash away our $0 and @ARGV for testing purposes
42 0         0 $dollar_zero = $0;
43 1         7 @orig_argv = @ARGV;
44              
45             # use the module then clear out @ARGV
46 1     1   4 use_ok('Getargs::Original');
  1         3  
  1         2503  
  1         974  
  1         5  
47 1         4 undef @ARGV;
48              
49             # make sure that the program was stored
50 1         7 my $rx = qr/$dollar_zero/;
51 1         2418 like( Getargs::Original->program, $rx, 'program name looks correct');
52              
53             # make sure the args were stored
54 1         26 is_deeply( scalar Getargs::Original->args, \@orig_argv, 'args look correct');
55              
56             =end testing
57 1         7  
58 1         751 =cut
59              
60             package Getargs::Original;
61              
62 1     1   1053 use strict;
  1         6  
  1         4  
  1         19  
  1         5  
63 1     1   4 use warnings;
  1         2  
  1         38  
  1         6  
64              
65             our $VERSION = 0.001_000;
66              
67 1     1   3 use File::Spec;
  1         59  
  1         5  
68              
69             use Class::MethodMaker(
70 1         945 static_list => '_argv',
71             static_get_set => [ qw|
72             orig_program
73             base_dir
74             resolved
75             |],
76 1     1   2 );
  1         40  
77              
78             # remember how this program was run
79             Getargs::Original->orig_program($0);
80             Getargs::Original->_argv_push($0, @ARGV);
81              
82             =head1 RESOLVING THE PATH OF $0
83              
84             In normal operation, the path of $0 is made absolute using
85             Crel2abs()>. Sometimes it is desireable for the canonical
86             name of the program run to be rooted in a particular directory.
87              
88             Take for example a scenario where the canonical path to programs is
89             F but F is a symlink to another filesystem which
90             can differ from machine to machine. When the full path to $0 is resolved,
91             the path will be the true filesystem and not F.
92              
93             This distinction may not matter to most, but if system monitoring tools are
94             looking for a program to be running with a specific path then things will
95             break. F is not the same as F
96             after all.
97              
98             To address this, B provides a way to specify the base
99             directory used for resolution of C<$0>. By passing a directory to the
100             B method the resolved path to C<$0> will be calculated relative to
101             that directory.
102              
103             =head1 METHODS
104              
105             =head2 argv()
106              
107             Returns the original value of $0 and @ARGV as a list reference in scalar
108             context and a list in array context.
109              
110             If the B method has been called then the first element of the
111             list returned will be a relative path rooted in the directory that
112             B was called with. If B has not been called then the
113             first element of the list will be the absolute path to $0.
114              
115             Resolution of $0 is performed the first time that the B method (or
116             the shortcuts described below) are called. As such if relative resolution is
117             desired then the B method must be called prior to the first use
118             of B, B or B.
119              
120             =begin testing
121              
122 1         3 # test without base dir set
123 1         1 Getargs::Original->resolved(0);
124 1         37 my $expected = File::Spec->rel2abs($0);
125             is( Getargs::Original->program, $expected,
126 1         41 'program without base dir set is correct');
127 1         6  
128 1         737 # test with base dir set
129 1         53 Getargs::Original->resolved(0);
130             Getargs::Original->base_dir('foo');
131 1         145 $expected = File::Spec->catfile('foo', File::Spec->abs2rel($0, 'foo'));
132 1         33 is( Getargs::Original->program, $expected,
133 1         711 'program with base dir set is correct');
134 1         45  
135             # another base dir test (this may break on non-UNIX - have to
136             # see what CPAN-Testers comes up with)
137             Getargs::Original->resolved(0);
138 1         195 Getargs::Original->base_dir('/opt/foo/');
139 1         6 $expected = File::Spec->catfile('/opt/foo/', File::Spec->abs2rel($0, '/opt/foo/'));
140             is( Getargs::Original->program, $expected,
141             'program with base dir set is correct');
142              
143 1         717 =end testing
  1         3  
144 1         2  
145             =cut
146              
147             sub argv
148             {
149              
150             # if $0 has been resolved, just return the args
151 1 50   6 1 24631 return Getargs::Original->_argv if( Getargs::Original->resolved );
152            
153             # otherwise resolve $0 as relative or absolute
154 1         11 my $program = Getargs::Original->orig_program;
155 6 100       220 if( my $base_dir = Getargs::Original->base_dir ) {
156 6         249 $program = File::Spec->catfile(
157             $base_dir,
158             File::Spec->abs2rel($program, $base_dir),
159             );
160             }
161             else {
162 6         233 $program = File::Spec->rel2abs($program);
163             }
164            
165             # set the resolved value
166 3         356 Getargs::Original->_argv_set(0, $program);
167            
168             # note that we have completed resolution
169 3         103 Getargs::Original->resolved(1);
170            
171             # return the args
172 6         208 return Getargs::Original->_argv;
173            
174             }
175              
176             =head2 program()
177 1         2  
178 1         2 Returns the original value of $0. A shortcut to saying
179 1         37  
180 1         44 $originalargs->argv->[0];
181              
182 1         33 =for testing
183 1         6 Getargs::Original->clear_resolved;
184             Getargs::Original->clear_base_dir;
185             my $expected = File::Spec->rel2abs($0);
186             is( Getargs::Original->program, $expected, '$0 is correct');
187 1         698  
  1         2  
188 1         2 =cut
189              
190             sub program
191             {
192            
193 6     5 1 274 return Getargs::Original->argv->[0];
194            
195             }
196              
197             =head2 args()
198              
199             Returns the original value of @ARGV. A shortcut to saying
200              
201             my $numargs = $originalargs->_argv_count;
202 1         2 $originalargs->argv->[1..$numargs]
203              
204 1         2 As with B arguments are returned as a list or list reference
205 1         6 depending on calling context.
206              
207             =for testing
208             is_deeply( scalar Getargs::Original->args, \@orig_argv, 'args are correct');
209 1         878  
  1         2  
210 1         2 =cut
211              
212             sub args
213             {
214              
215 6     2 1 319 my $num_args = Getargs::Original->_argv_count - 1;
216 5         17 my @args = Getargs::Original->_argv;
217 2         73 @args = @args[1..$num_args];
218 2 50       75 return wantarray ? @args : \@args;
219            
220             }
221              
222             =head2 base_dir()
223              
224 1         1 Sets or gets the base directory used for resolution of $0. See L<"RESOLVING
225             THE PATH OF $0"> above for more detail. Returns the previous base directory.
226              
227 1         2 =begin testing
228 1         36  
229 1         699 # base dir shouldn't be defined yet
230             ok( ! defined Getargs::Original->base_dir, 'base dir not defined');
231              
232 1         39 # set and test
233 1         557 Getargs::Original->base_dir('foo');
234             ok( defined Getargs::Original->base_dir, 'base dir is defined');
235             is( Getargs::Original->base_dir(), 'foo', 'base dir set to foo');
236              
237 1         677 =end testing
  1         2  
238 1         3  
239             =head2 resolved()
240              
241             Sets or gets the flag indicating whether $0 has been resolved. Returns the
242             previous state of the flag.
243              
244             Using this method as a set accessor should only be required if the B
245 1         2 method or one of it's shortcuts was inadvertently called prior to the
246 1         2 B method being called.
247              
248             =begin testing
249 1         41  
250             # reset state
251             Getargs::Original->resolved(0);
252 1         46 is( Getargs::Original->resolved, 0, '$0 has not been resolved');
253              
254             # cause $0 to be resolved
255 1         854 Getargs::Original->argv;
256 1         53  
257             # make sure things are now resolved
258             is( Getargs::Original->resolved, 1, '$0 has been resolved');
259              
260             =end testing
261              
262             =cut
263              
264             # keep require happy
265             1;
266              
267              
268             __END__