File Coverage

blib/lib/Getopt/EX/Config.pm
Criterion Covered Total %
statement 70 99 70.7
branch 13 30 43.3
condition 5 16 31.2
subroutine 16 23 69.5
pod 5 11 45.4
total 109 179 60.8


line stmt bran cond sub pod time code
1             package Getopt::EX::Config;
2              
3 3     3   584183 use v5.14;
  3         11  
4 3     3   17 use warnings;
  3         6  
  3         298  
5              
6             our $VERSION = '1.0202';
7              
8             our $REPLACE_UNDERSCORE = 1;
9              
10 3     3   2068 use Data::Dumper;
  3         30159  
  3         256  
11 3     3   2216 use Getopt::Long;
  3         48037  
  3         23  
12              
13 3     3   633 use List::Util qw(first);
  3         6  
  3         227  
14 3     3   1755 use Hash::Util qw(lock_keys);
  3         11976  
  3         20  
15              
16             our %CONFIG;
17              
18             sub import {
19 3     3   43 my $class = shift;
20 3         9 my $caller = caller;
21 3 50       15 my @names = @_ ? @_ : 'config';
22 3     3   496 no strict 'refs';
  3         5  
  3         1663  
23 3         10 for my $name (@names) {
24 3         1713 *{"$caller\::$name"} = sub {
25 0     0   0 $CONFIG{$caller}->config(@_);
26 3         14 };
27             };
28             }
29              
30             sub new {
31 9     9 1 462594 my $class = shift;
32 9 50       57 my $config = ref $_[0] eq 'HASH' ? shift : { @_ };
33 9         34 for my $key (keys %$config) {
34 14 50       77 $key =~ /^[a-zA-Z]/ or die "$key: config key must start with a letter\n";
35             }
36             # Initialize internal keys for lock_keys compatibility
37 9         32 $config->{_argv} = [];
38 9         24 $config->{_configure} = [];
39 9         26 my $caller = caller;
40 9         52 $CONFIG{$caller} = bless $config, $class;
41 9         47 $config;
42             }
43              
44             sub object {
45 0     0 0 0 $CONFIG{+caller};
46             }
47              
48             sub deal_with {
49 7     7 1 96 my $obj = shift;
50 7 50       36 if ($obj eq __PACKAGE__) {
51 0   0     0 $obj = $CONFIG{+caller} // die;
52             }
53 7         25 my($my_argv, $argv) = split_argv(shift);
54 7 50       40 $obj->getopt($my_argv, @_) if @$my_argv;
55 6         5307 $obj->{_argv} = [ @$my_argv ];
56 6         26 return $obj;
57             }
58              
59             sub argv {
60 1     1 1 3 my $obj = shift;
61 1   50     3 @{ $obj->{_argv} // [] };
  1         7  
62             }
63              
64             sub configure {
65 3     3 1 79 my $obj = shift;
66 3         5 push @{$obj->{_configure}}, @_;
  3         10  
67 3         14 $obj;
68             }
69              
70 3     3   1876 use Getopt::EX::Func;
  3         5134  
  3         3289  
71             *arg2kvlist = \&Getopt::EX::Func::arg2kvlist;
72              
73             sub getopt {
74 7     7 0 13 my $obj = shift;
75 7   50     23 my $argv = shift // [];
76 7 50       10 return if @{ $argv } == 0;
  7         23  
77              
78             # Filter out internal keys (must start with a letter)
79 7 50       17 @_ = grep { ref($_) || /^[a-zA-Z]/ } @_;
  12         85  
80              
81             # Convert underscore options to underscore|dash format
82 7 50       56 @_ = map { ref($_) ? $_ : s/^(\w*_[\w_]*)/"$1|" . ($1 =~ s:_:-:gr)/er } @_
  8 100       51  
  4         40  
83             if $REPLACE_UNDERSCORE;
84              
85             my $p = Getopt::Long::Parser->new(
86 7   50     15 config => [ 'bundling', @{$obj->{_configure} // []} ]
  7         100  
87             );
88             $p->getoptionsfromarray(
89             $argv,
90             $obj,
91             "config=s" => sub {
92 0     0   0 $obj->config(arg2kvlist($_[1]));
93             },
94             @_
95 7 100       4268 ) or die "Option parse error.\n";
96             }
97              
98             sub config {
99 0     0 1 0 my $obj = shift;
100 0 0       0 if (@_ == 1) {
101 0         0 $obj->get(@_);
102             } else {
103 0         0 $obj->set(@_);
104             }
105             }
106              
107             ######################################################################
108              
109             sub set {
110 0     0 0 0 my $obj = shift;
111 0         0 while (my($k, $v) = splice @_, 0, 2) {
112 0         0 my $c = $obj;
113 0         0 my @names = split /\./, $k;
114 0         0 my $name = pop @names;
115 0         0 for (@names) {
116 0   0     0 $c = $c->{$_} // die "$k: invalid name.\n";
117             }
118 0 0       0 exists $c->{$name} or die "$k: invalid name.\n";
119 0 0       0 if (ref $c->{$name}) {
120 0         0 ${$c->{$name}} = $v;
  0         0  
121             } else {
122 0         0 $c->{$name} = $v;
123             }
124             }
125 0         0 ();
126             }
127              
128             sub get :lvalue {
129 0     0 0 0 my $c = shift;
130 0         0 my $key = shift;
131 0 0       0 if (ref $c->{$key}) {
132 0         0 ${$c->{$key}};
  0         0  
133             } else {
134 0         0 $c->{$key};
135             }
136             }
137              
138             sub mod_argv {
139 0     0 0 0 my($mod, $argv) = splice @_, 0, 2;
140 0         0 ($mod, split_argv($argv), @_);
141             }
142              
143             sub split_argv {
144 7     7 0 12 my $argv = shift;
145 7         20 my @my_argv;
146 7 50 33     104 if (@$argv and
      33        
147             $argv->[0] !~ /^-M/ and
148 21     21   68 defined(my $i = first { $argv->[$_] eq '--' } keys @$argv)) {
149 7         28 splice @$argv, $i, 1; # remove '--'
150 7         25 @my_argv = splice @$argv, 0, $i;
151             }
152 7         46 (\@my_argv, $argv);
153             }
154              
155             1;
156              
157             =encoding utf-8
158              
159             =head1 NAME
160              
161             Getopt::EX::Config - Getopt::EX module configuration interface
162              
163             =head1 SYNOPSIS
164              
165             example -Mfoo::config(foo=yabaa,bar=dabba) ...
166              
167             example -Mfoo::config(foo=yabba) --config bar=dabba ... -- ...
168              
169             example -Mfoo::config(foo=yabba) --bar=dabba ... -- ...
170              
171             example -Mfoo --foo=yabaa --bar=dabba -- ...
172              
173             =head1 VERSION
174              
175             Version 1.0202
176              
177             =head1 DESCRIPTION
178              
179             This module provides an interface to define configuration information
180             for C modules. In the traditional way, in order to set
181             options for a module, it was necessary to define dedicated command
182             line options for them. To do so, it is necessary to avoid name
183             conflicts with existing command options or with other modules used
184             together.
185              
186             Using this module, it is possible to define configuration information
187             only for the module and to define module-specific command options.
188              
189             You can create config object like this:
190              
191             use Getopt::EX::Config;
192             my $config = Getopt::EX::Config->new(
193             char => 0,
194             width => 0,
195             code => 1,
196             name => "Franky",
197             );
198              
199             This call returns hash object and each member can be accessed like
200             C<< $config->{width} >>.
201              
202             You can set these configuration values by calling C function
203             with module declaration.
204              
205             example -Mfoo::config(width,code=0) ...
206              
207             Parameter list is given by key-value pairs, and C<1> is assumed when
208             value is not given. Above code set C to C<1> and C to
209             C<0>.
210              
211             Also module specific options can be taken care of by calling
212             C method from module startup funciton C or
213             C.
214              
215             sub finalize {
216             our($mod, $argv) = @_;
217             $config->deal_with($argv);
218             }
219              
220             Then you can use C<--config> module option like this:
221              
222             example -Mfoo --config width,code=0 -- ...
223              
224             The module startup function is executed between the C
225             and C calls. Therefore, if you want to give priority to
226             module-specific options over the startup function, you must call
227             C in the C function.
228              
229             If you want to make module private option, say C<--width> to set C<<
230             $config->{width} >> value, C method takes C
231             style option specifications.
232              
233             sub finalize {
234             our($mod, $argv) = @_;
235             $config->deal_with(
236             $argv,
237             "width!",
238             "code!",
239             "name=s",
240             );
241             }
242              
243             Then you can use module private option like this:
244              
245             example -Mcharcode --width --no-code --name=Benjy -- ...
246              
247             By default, option names with underscores are automatically aliased with
248             dash equivalents. For example, if you specify C, both C<--long_lc>
249             and C<--long-lc> will work. This conversion can be disabled by setting
250             C<$Getopt::EX::Config::REPLACE_UNDERSCORE> to 0.
251              
252             The reason why it is not necessary to specify the destination of the
253             value is that the hash object is passed when calling the
254             C library. The above code is equivalent to the
255             following code. See L
256             for detail.
257              
258             =head2 Nested Hash Configuration
259              
260             Config values can be hash references for structured configuration:
261              
262             my $config = Getopt::EX::Config->new(
263             mode => '',
264             hashed => { h3 => 0, h4 => 0, h5 => 0, h6 => 0 },
265             );
266              
267             Nested values can be accessed using dot notation in the C
268             function:
269              
270             example -Mfoo::config(hashed.h3=1,hashed.h4=1) ...
271              
272             example -Mfoo --config hashed.h3=1 -- ...
273              
274             The dot notation navigates into nested hashes: C sets
275             C<< $config->{hashed}{h3} >> to C<1>. The intermediate key
276             (C) must exist as a hash reference, and the leaf key (C

)

277             must already exist in that hash.
278              
279             Hash options can also be defined as module private options using
280             L hash type (C<%>):
281              
282             $config->deal_with($argv, "hashed=s%");
283              
284             This allows:
285              
286             example -Mfoo --hashed h3=1 --hashed h4=1 -- ...
287              
288             Note that the C hash type auto-vivifies keys, so
289             C<--hashed h3=1> works even when C

does not pre-exist in the hash.

290              
291             The dot notation and nested hash support are designed with future
292             extensibility in mind. For example, a configuration file under
293             F<~/.config> could store module settings in YAML-like format:
294              
295             # ~/.config/example/foo.yml
296             mode: dark
297             hashed:
298             h3: 1
299             h4: 1
300             h5: 1
301             h6: 1
302              
303             This would map naturally to the nested hash structure and dot notation
304             already supported by this module.
305              
306             sub finalize {
307             our($mod, $argv) = @_;
308             $config->deal_with(
309             $argv,
310             "width!" => \$config->{width},
311             "code!" => \$config->{code},
312             "name=s" => \$config->{name},
313             );
314             }
315              
316             =head1 FUNCTIONS
317              
318             =over 7
319              
320             =item B(I => I, ...)
321              
322             This module exports the function C by default. As explained
323             above, this is why the C function can be executed with module
324             declaration.
325              
326             If you want to use a function with a different name, specify it
327             explicitly. In the following example, the function C is defined
328             and can be used in the same way as C.
329              
330             use Getopt::EX::Config qw(config set);
331              
332             =item B(I)
333              
334             The C function may also be used to refer parameters in the
335             program. In this case, specify single argument.
336              
337             my $width = config('width');
338              
339             Parameter value references can also be used as left-hand side values,
340             so values can be assigned.
341              
342             config('width') = 42;
343              
344             =back
345              
346             =head1 METHODS
347              
348             =over 7
349              
350             =item B(I)
351              
352             =item B(I)
353              
354             Return configuration object.
355              
356             Call with key-value list like this:
357              
358             my $config = Getopt::EX::Config->new(
359             char => 0,
360             width => 0,
361             code => 1,
362             name => "Franky",
363             );
364              
365             Or call with hash reference.
366              
367             my %config = (
368             char => 0,
369             width => 0,
370             code => 1,
371             name => "Franky",
372             );
373             my $config = Getopt::EX::Config->new(\%config);
374              
375             In this case, C<\%config> and C<$config> should be identical.
376             Do not apply C to the hash before calling C.
377              
378             Config keys must start with a letter (a-z, A-Z). Keys starting with
379             underscore or other characters are reserved for internal use.
380              
381             =item B
382              
383             You can get argument reference in C or C
384             function declared in C module. Call C method
385             with that reference.
386              
387             sub finalize {
388             our($mod, $argv) = @_;
389             $config->deal_with($argv);
390             }
391              
392             You can define module specific options by giving L style
393             definition with that call.
394              
395             sub finalize {
396             our($mod, $argv) = @_;
397             $config->deal_with($argv,
398             "width!", "code!", "name=s");
399             }
400              
401             =item B(I)
402              
403             Set L configuration options. Returns the object itself
404             for method chaining. Internally uses L so that
405             global configuration is not affected.
406              
407             $config->configure('pass_through');
408             $config->deal_with($argv, "width!", "name=s");
409              
410             Or with method chaining:
411              
412             $config->configure('pass_through')->deal_with($argv, ...);
413              
414             =item B
415              
416             Returns the remaining arguments after C processing. When
417             used with C configuration, unrecognized options are
418             preserved and can be retrieved with this method.
419              
420             sub finalize {
421             our($mod, $argv) = @_;
422             $config->configure('pass_through')->deal_with($argv, "width!", "name=s");
423             my @extra = $config->argv; # unrecognized options
424             }
425              
426             =back
427              
428             =head1 VARIABLES
429              
430             =over 7
431              
432             =item B<$REPLACE_UNDERSCORE>
433              
434             When set to true (default), option names with underscores are automatically
435             aliased with dash equivalents. For example, C becomes
436             C, allowing both C<--long_lc> and C<--long-lc> to work.
437              
438             Set to false to disable this conversion:
439              
440             $Getopt::EX::Config::REPLACE_UNDERSCORE = 0;
441              
442             =back
443              
444             =head1 SEE ALSO
445              
446             L
447              
448             L
449              
450             =head1 AUTHOR
451              
452             Kazumasa Utashiro
453              
454             =head1 COPYRIGHT
455              
456             The following copyright notice applies to all the files provided in
457             this distribution, including binary files, unless explicitly noted
458             otherwise.
459              
460             Copyright ©︎ 2025-2026 Kazumasa Utashiro
461              
462             =head1 LICENSE
463              
464             This library is free software; you can redistribute it and/or modify
465             it under the same terms as Perl itself.
466              
467             =cut