File Coverage

blib/lib/App/Sqitch/Command/config.pm
Criterion Covered Total %
statement 150 150 100.0
branch 58 62 93.5
condition 35 39 89.7
subroutine 46 46 100.0
pod 15 15 100.0
total 304 312 97.4


line stmt bran cond sub pod time code
1              
2             use 5.010;
3 4     4   5714 use strict;
  4         17  
4 4     4   22 use warnings;
  4         8  
  4         94  
5 4     4   22 use utf8;
  4         6  
  4         130  
6 4     4   25 use Path::Class ();
  4         9  
  4         37  
7 4     4   100 use Try::Tiny;
  4         10  
  4         78  
8 4     4   19 use Locale::TextDomain qw(App-Sqitch);
  4         7  
  4         268  
9 4     4   40 use App::Sqitch::X qw(hurl);
  4         10  
  4         69  
10 4     4   965 use List::Util qw(first);
  4         9  
  4         41  
11 4     4   1341 use Moo;
  4         8  
  4         270  
12 4     4   41 use App::Sqitch::Types qw(Str Dir Maybe);
  4         8  
  4         35  
13 4     4   1667 use Type::Utils qw(enum);
  4         10  
  4         64  
14 4     4   4583 use namespace::autoclean;
  4         8  
  4         37  
15 4     4   2082 extends 'App::Sqitch::Command';
  4         11  
  4         40  
16              
17             our $VERSION = 'v1.3.1'; # VERSION
18              
19             has file => (
20             is => 'ro',
21             lazy => 1,
22             default => sub {
23             my $self = shift;
24             my $meth = ( $self->context || 'local' ) . '_file';
25             return $self->sqitch->config->$meth;
26             }
27             );
28              
29             has action => (
30             is => 'ro',
31             isa => enum([qw(
32             get
33             get_all
34             get_regex
35             set
36             unset
37             list
38             edit
39             add
40             replace_all
41             unset_all
42             rename_section
43             remove_section
44             )]),
45             );
46              
47             has context => (
48             is => 'ro',
49             isa => Maybe[enum([qw(
50             local
51             user
52             system
53             )])],
54             );
55              
56             has type => ( is => 'ro', isa => enum( [qw(int num bool bool-or-int)] ) );
57              
58             return qw(
59             file|config-file|f=s
60 2     2 1 5225 local
61             user|global
62             system
63              
64             int
65             bool
66             bool-or-int
67             num
68              
69             get
70             get-all
71             get-regex|get-regexp
72             add
73             replace-all
74             unset
75             unset-all
76             rename-section
77             remove-section
78             list|l
79             edit|e
80             );
81             }
82              
83             my ( $class, $config, $opt ) = @_;
84              
85             # Make sure we are accessing only one file.
86 27     27 1 18641 my @file = grep { $opt->{$_} } qw(local user system file);
87             $class->usage('Only one config file at a time.') if @file > 1;
88              
89 27         60 # Make sure we have only one type.
  108         180  
90 27 100       80 my @type = grep { $opt->{$_} } qw(bool int num bool_or_int);
91             $class->usage('Only one type at a time.') if @type > 1;
92              
93 22         35 # Make sure we are performing only one action.
  88         130  
94 22 100       52 my @action = grep { $opt->{$_} } qw(
95             get
96             get_all
97 18         33 get_regex
  198         270  
98             unset
99             list
100             edit
101             add
102             replace_all
103             unset_all
104             rename_section
105             remove_section
106             );
107             $class->usage('Only one action at a time.') if @action > 1;
108              
109             # Get the action and context.
110 18 100       56 my $context = first { $opt->{$_} } qw(local user system);
111              
112             # Make it so.
113 5     12   32 return {
  12         21  
114             ( $action[0] ? ( action => $action[0] ) : () ),
115             ( $type[0] ? ( type => $type[0] ) : () ),
116             ( $context ? ( context => $context ) : () ),
117             ( $opt->{file} ? ( file => $opt->{file} ) : () ),
118             };
119             }
120 5 50       54  
    50          
    100          
    50          
121             my $self = shift;
122             my $action = $self->action || ( @_ > 1 ? 'set' : 'get' );
123             $action =~ s/-/_/g;
124             my $meth = $self->can($action) or hurl config => __x(
125 94     94 1 115604 'Unknown config action: {action}',
126 94   66     610 action => $action,
127 94         363 );
128 94 50       678 return $self->$meth(@_);
129             }
130              
131             my ( $self, $key, $rx ) = @_;
132 94         460 $self->usage('Wrong number of arguments.') if !defined $key || $key eq '';
133              
134             my $val = try {
135             $self->sqitch->config->get(
136 31     31 1 4329 key => $key,
137 31 100 100     238 filter => $rx,
138             as => $self->type,
139             human => 1,
140 29     29   3846 );
141             }
142             catch {
143             hurl config => __x(
144             'More then one value for the key "{key}"',
145             key => $key,
146             ) if /^\QMultiple values/i;
147             hurl config => $_;
148 5 100   5   1095 };
149              
150             hurl {
151             ident => 'config',
152 4         41 message => '',
153 29         381 exitval => 1,
154             } unless defined $val;
155 24 100       9818 $self->emit($val);
156             return $self;
157             }
158              
159             my ( $self, $key, $rx ) = @_;
160 19         82 $self->usage('Wrong number of arguments.') if !defined $key || $key eq '';
161 19         245  
162             my @vals = try {
163             $self->sqitch->config->get_all(
164             key => $key,
165 20     20 1 155 filter => $rx,
166 20 100 100     127 as => $self->type,
167             human => 1,
168             );
169 18     18   1989 }
170             catch {
171             hurl config => $_;
172             };
173             hurl {
174             ident => 'config',
175             message => '',
176             exitval => 1,
177 4     4   768 } unless @vals;
178 18         171 $self->emit( join "\n", @vals );
179 14 100       2710 return $self;
180             }
181              
182             my ( $self, $key, $rx ) = @_;
183             $self->usage('Wrong number of arguments.') if !defined $key || $key eq '';
184 13         90  
185 13         143 my $config = $self->sqitch->config;
186             my %vals = try {
187             $config->get_regexp(
188             key => $key,
189 19     19 1 159 filter => $rx,
190 19 100 100     132 as => $self->type,
191             human => 1,
192 17         447 );
193             }
194 17     17   1508 catch {
195             hurl config => $_;
196             };
197             hurl {
198             ident => 'config',
199             message => '',
200             exitval => 1,
201             } unless %vals;
202 4     4   621 my @out;
203 17         251 for my $key ( sort keys %vals ) {
204 13 100       2323 if ( defined $vals{$key} ) {
205             if ( $config->is_multiple($key) ) {
206             push @out => "$key=[" . join( ', ', @{ $vals{$key} } ) . ']';
207             }
208             else {
209 12         19 push @out => "$key=$vals{$key}";
210 12         54 }
211 19 100       61 }
212 18 100       62 else {
213 1         86 push @out => $key;
  1         12  
214             }
215             }
216 17         1727 $self->emit( join "\n" => @out );
217              
218             return $self;
219             }
220 1         10  
221             my ( $self, $key, $value, $rx ) = @_;
222             $self->_set( $key, $value, $rx, multiple => 0 );
223 12         67 }
224              
225 12         120 my ( $self, $key, $value ) = @_;
226             $self->_set( $key, $value, undef, multiple => 1 );
227             }
228              
229 9     9 1 15778 my ( $self, $key, $value, $rx ) = @_;
230 9         41 $self->_set( $key, $value, $rx, multiple => 1, replace_all => 1 );
231             }
232              
233             my ( $self, $key, $value, $rx, @p ) = @_;
234 15     15 1 34884 $self->usage('Wrong number of arguments.')
235 15         111 if !defined $key || $key eq '' || !defined $value;
236              
237             $self->_touch_dir;
238             try {
239 2     2 1 23 $self->sqitch->config->set(
240 2         18 key => $key,
241             value => $value,
242             filename => $self->file,
243             filter => $rx,
244 26     26   179 as => $self->type,
245 26 100 100     385 @p,
      100        
246             );
247             }
248 20         145 catch {
249             hurl config => __(
250 20     20   3086 'Cannot overwrite multiple values with a single value'
251             ) if /^Multiple occurrences/i;
252             hurl config => $_;
253             };
254             return $self;
255             }
256              
257             my $file = shift->file;
258             return unless -e $file;
259             my $config = App::Sqitch::Config->new;
260 2 100   2   800 $config->load_file($file);
261             return $config;
262             }
263 1         20  
264 20         785 my ( $self, $key, $rx ) = @_;
265 18         24857 $self->usage('Wrong number of arguments.') if !defined $key || $key eq '';
266             $self->_touch_dir;
267              
268             try {
269 5     5   163 $self->sqitch->config->set(
270 5 100       1154 key => $key,
271 3         298 filename => $self->file,
272 3         2517 filter => $rx,
273 3         35411 multiple => 0,
274             );
275             }
276             catch {
277 8     8 1 13998 hurl config => __(
278 8 100 100     98 'Cannot unset key with multiple values'
279 6         33 ) if /^Multiple occurrences/i;
280             hurl config => $_;
281             };
282 6     6   1042 return $self;
283             }
284              
285             my ( $self, $key, $rx ) = @_;
286             $self->usage('Wrong number of arguments.') if !defined $key || $key eq '';
287              
288             $self->_touch_dir;
289             $self->sqitch->config->set(
290 2 100   2   1042 key => $key,
291             filename => $self->file,
292             filter => $rx,
293 1         17 multiple => 1,
294 6         329 );
295 4         5801 return $self;
296             }
297              
298             my $self = shift;
299 5     5 1 20724 my $config = $self->context
300 5 100 100     96 ? $self->_file_config
301             : $self->sqitch->config;
302 3         25 $self->emit( scalar $config->dump ) if $config;
303 3         185 return $self;
304             }
305              
306             my $self = shift;
307              
308             # Let the editor deal with locking.
309 3         3878 $self->shell(
310             $self->sqitch->editor . ' ' . $self->quote_shell( $self->file )
311             );
312             }
313 6     6 1 31  
314 6 100       92 my ( $self, $old_name, $new_name ) = @_;
315             $self->usage('Wrong number of arguments.')
316             unless defined $old_name && $old_name ne ''
317 6 100       139 && defined $new_name && $new_name ne '';
318 6         8544  
319             try {
320             $self->sqitch->config->rename_section(
321             from => $old_name,
322 1     1 1 3 to => $new_name,
323             filename => $self->file
324             );
325 1         44 }
326             catch {
327             hurl config => __ 'No such section!' if /\Qno such section/i;
328             hurl config => $_;
329             };
330             return $self;
331 6     6 1 3072 }
332 6 100 66     101  
      100        
      100        
333             my ( $self, $section ) = @_;
334             $self->usage('Wrong number of arguments.')
335             unless defined $section && $section ne '';
336             try {
337 3     3   516 $self->sqitch->config->remove_section(
338             section => $section,
339             filename => $self->file
340             );
341             }
342             catch {
343             hurl config => __ 'No such section!' if /\Qno such section/i;
344 2 100   2   866 hurl config => $_;
345 1         360 };
346 3         81 return $self;
347 1         1132 }
348              
349             my $self = shift;
350             unless ( -e $self->file ) {
351 4     4 1 3061 require File::Basename;
352 4 100 66     77 my $dir = File::Basename::dirname( $self->file );
353             unless ( -e $dir && -d _ ) {
354             require File::Path;
355 3     3   514 File::Path::make_path($dir);
356             }
357             }
358             }
359              
360             1;
361 2 100   2   733  
362 1         14  
363 3         397 =head1 Name
364 1         1359  
365             App::Sqitch::Command::config - Get and set local, user, or system Sqitch options
366              
367             =head1 Synopsis
368 29     29   66  
369 29 100       186 my $cmd = App::Sqitch::Command::config->new(\%params);
370 2         184 $cmd->execute;
371 2         11  
372 2 100 66     282 =head1 Description
373 1         7  
374 1         223 You can query/set/replace/unset Sqitch options with this command. The name is
375             actually the section and the key separated by a dot, and the value will be
376             escaped.
377              
378             =head1 Interface
379              
380             =head2 Class Methods
381              
382             =head3 C<options>
383              
384             my @opts = App::Sqitch::Command::config->options;
385              
386             Returns a list of L<Getopt::Long> option specifications for the command-line
387             options for the C<config> command.
388              
389             =head3 C<configure>
390              
391             my $params = App::Sqitch::Command::config->configure(
392             $config,
393             $options,
394             );
395              
396             Processes the configuration and command options and returns a hash suitable
397             for the constructor. Exits with an error on option specification errors.
398              
399             =head2 Constructor
400              
401             =head3 C<new>
402              
403             my $config = App::Sqitch::Command::config->new($params);
404              
405             Creates and returns a new C<config> command object. The supported parameters
406             include:
407              
408             =over
409              
410             =item C<sqitch>
411              
412             The core L<Sqitch|App::Sqitch> object.
413              
414             =item C<file>
415              
416             Configuration file to read from and write to.
417              
418             =item C<action>
419              
420             The action to be executed. May be one of:
421              
422             =over
423              
424             =item * C<get>
425              
426             =item * C<get-all>
427              
428             =item * C<get-regexp>
429              
430             =item * C<set>
431              
432             =item * C<add>
433              
434             =item * C<replace-all>
435              
436             =item * C<unset>
437              
438             =item * C<unset-all>
439              
440             =item * C<list>
441              
442             =item * C<edit>
443              
444             =item * C<rename-section>
445              
446             =item * C<remove-section>
447              
448             =back
449              
450             If not specified, the action taken by C<execute()> will depend on the number
451             of arguments passed to it. If only one, the action will be C<get>. If two or
452             more, the action will be C<set>.
453              
454             =item C<context>
455              
456             The configuration file context. Must be one of:
457              
458             =over
459              
460             =item * C<local>
461              
462             =item * C<user>
463              
464             =item * C<system>
465              
466             =back
467              
468             =item C<type>
469              
470             The type to cast a value to be set to or fetched as. May be one of:
471              
472             =over
473              
474             =item * C<bool>
475              
476             =item * C<int>
477              
478             =item * C<num>
479              
480             =item * C<bool-or-int>
481              
482             =back
483              
484             If not specified or C<undef>, no casting will be performed.
485              
486             =back
487              
488             =head2 Instance Methods
489              
490             These methods are mainly provided as utilities for the command subclasses to
491             use.
492              
493             =head3 C<execute>
494              
495             $config->execute($property, $value);
496              
497             Executes the config command. Pass the name of the property and the value to
498             be assigned to it, if applicable.
499              
500             =head3 C<get>
501              
502             $config->get($key);
503             $config->get($key, $regex);
504              
505             Emits the value for the specified key. The optional second argument is a
506             regular expression that the value to be returned must match. Exits with an
507             error if the is more than one value for the specified key, or if the key does
508             not exist.
509              
510             =head3 C<get_all>
511              
512             $config->get_all($key);
513             $config->get_all($key, $regex);
514              
515             Like C<get()>, but emits all of the values for the given key, rather then
516             exiting with an error when there is more than one value.
517              
518             =head3 C<get_regex>
519              
520             $config->get_regex($key);
521             $config->get_regex($key, $regex);
522              
523             Like C<get_all()>, but the first parameter is a regular expression that will
524             be matched against all keys.
525              
526             =head3 C<set>
527              
528             $config->set($key, $value);
529             $config->set($key, $value, $regex);
530              
531             Sets the value for a key. Exits with an error if the key already exists and
532             has multiple values.
533              
534             =head3 C<add>
535              
536             $config->add($key, $value);
537              
538             Adds a value for a key. If the key already exists, the value will be added as
539             an additional value.
540              
541             =head3 C<replace_all>
542              
543             $config->replace_all($key, $value);
544             $config->replace_all($key, $value, $regex);
545              
546             Replace all matching values.
547              
548             =head3 C<unset>
549              
550             $config->unset($key);
551             $config->unset($key, $regex);
552              
553             Unsets a key. If the optional second argument is passed, the key will be unset
554             only if the value matches the regular expression. If the key has multiple
555             values, C<unset()> will exit with an error.
556              
557             =head3 C<unset_all>
558              
559             $config->unset_all($key);
560             $config->unset_all($key, $regex);
561              
562             Like C<unset()>, but will not exit with an error if the key has multiple
563             values.
564              
565             =head3 C<rename_section>
566              
567             $config->rename_section($old_name, $new_name);
568              
569             Renames a section. Exits with an error if the section does not exist or if
570             either name is not a valid section name.
571              
572             =head3 C<remove_section>
573              
574             $config->remove_section($section);
575              
576             Removes a section. Exits with an error if the section does not exist.
577              
578             =head3 C<list>
579              
580             $config->list;
581              
582             Lists all of the values in the configuration. If the context is C<local>,
583             C<user>, or C<system>, only the settings set for that context will be emitted.
584             Otherwise, all settings will be listed.
585              
586             =head3 C<edit>
587              
588             $config->edit;
589              
590             Opens the context-specific configuration file in a text editor for direct
591             editing. If no context is specified, the local config file will be opened. The
592             editor is determined by L<Sqitch/editor>.
593              
594             =head2 Instance Accessors
595              
596             =head3 C<file>
597              
598             my $file_name = $config->file;
599              
600             Returns the path to the configuration file to be acted upon. If the context is
601             C<system>, then the value returned is C<$($etc_prefix)/sqitch.conf>. If the
602             context is C<user>, then the value returned is C<~/.sqitch/sqitch.conf>.
603             Otherwise, the default is F<./sqitch.conf>.
604              
605             =head1 See Also
606              
607             =over
608              
609             =item L<sqitch-config>
610              
611             Help for the C<config> command to the Sqitch command-line client.
612              
613             =item L<sqitch>
614              
615             The Sqitch command-line client.
616              
617             =back
618              
619             =head1 Author
620              
621             David E. Wheeler <david@justatheory.com>
622              
623             =head1 License
624              
625             Copyright (c) 2012-2022 iovation Inc., David E. Wheeler
626              
627             Permission is hereby granted, free of charge, to any person obtaining a copy
628             of this software and associated documentation files (the "Software"), to deal
629             in the Software without restriction, including without limitation the rights
630             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
631             copies of the Software, and to permit persons to whom the Software is
632             furnished to do so, subject to the following conditions:
633              
634             The above copyright notice and this permission notice shall be included in all
635             copies or substantial portions of the Software.
636              
637             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
638             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
639             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
640             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
641             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
642             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
643             SOFTWARE.
644              
645             =cut
646