File Coverage

blib/lib/App/Cme/Common.pm
Criterion Covered Total %
statement 80 126 63.4
branch 17 42 40.4
condition 7 21 33.3
subroutine 18 22 81.8
pod 0 10 0.0
total 122 221 55.2


line stmt bran cond sub pod time code
1             #
2             # This file is part of App-Cme
3             #
4             # This software is Copyright (c) 2014-2022 by Dominique Dumont <ddumont@cpan.org>.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             #ABSTRACT: Common methods for App::Cme
11              
12             $App::Cme::Common::VERSION = '1.038';
13             use strict;
14 2     2   14 use warnings;
  2         2  
  2         49  
15 2     2   8 use 5.10.1;
  2         3  
  2         39  
16 2     2   17  
  2         5  
17             use Config::Model 2.124 qw/initialize_log4perl/;
18 2     2   48 use Config::Model::Lister;
  2         48  
  2         108  
19 2     2   13 use Pod::POM;
  2         3  
  2         37  
20 2     2   7 use Pod::POM::View::Text;
  2         2  
  2         98  
21 2     2   861 use Scalar::Util qw/blessed/;
  2         3234  
  2         58  
22 2     2   11 use Path::Tiny;
  2         3  
  2         80  
23 2     2   9 use Encode qw(decode_utf8);
  2         3  
  2         68  
24 2     2   9  
  2         3  
  2         1861  
25             my @store;
26              
27             my ( $class, $app ) = @_;
28              
29 24     24 0 52 my @global_options = (
30             [ "model-dir=s" => "Specify an alternate directory to find model files"],
31 24         194 [ "try-app-as-model!" => "try to load a model using directly the application name "
32             . "specified as 3rd parameter on the command line"],
33             [ "save!" => "Force a save even if no change was done" ],
34             [ "force-load!" => "Load file even if error are found in data. Errors must be fixed before saving."],
35             [ "create!" => "start from scratch."],
36             [ "root-dir=s" => "Change root directory. Mostly used for test"],
37             [ "file=s" => "Specify a target file"],
38             # to be deprecated
39             [ "canonical!" => "write back config data according to canonical order" ],
40             [ "trace|stack-trace!" => "Provides a full stack trace when exiting on error"],
41             [ "verbose!" => "Show what's going on"],
42             [ "quiet" => "Suppress all output except error messages"],
43             # no bundling
44             { getopt_conf => [ qw/no_bundling/ ] }
45             );
46              
47             return (
48             @global_options,
49             );
50 24         110 }
51              
52             my ($self, $args) = @_;
53              
54             my @unknown_options = grep { /^-/ } @$args ;
55 24     24 0 50 # $self->usage_error("Unknown option: @unknown_options") if @unknown_options;
56             warn("Unknown option: @unknown_options. Unknown option will soon be a fatal error.\n") if @unknown_options;
57 24         42 return;
  30         90  
58             }
59 24 50       87  
60 24         52 # modifies $args in place
61             my ($self, $opt, $args) = @_;
62              
63             # see Debian #839593 and perlunicook(1) section X 13
64             @$args = map { decode_utf8($_, 1) } @$args;
65 19     19 0 53  
66             my ( $categories, $appli_info, $appli_map ) = Config::Model::Lister::available_models;
67             my $application = shift @$args;
68 19         36 unless ($application) {
  24         236  
69             $self->usage_error(
70 19         184 "Missing application parameter. Run 'cme list' to get the "
71 19         13328 . "list of installed cme applications\n"
72 19 50       64 );
73 0         0 }
74              
75             my $root_model = $appli_map->{$application};
76             $root_model ||= $application if $opt->{try_app_as_model};
77              
78             Config::Model::Exception::Any->Trace(1) if $opt->{trace};
79 19         50  
80 19 50 0     118 if ( not defined $root_model ) {
81             die "Can't locate model for application '$application'.\n"
82 19 50       62 . "Run 'cme list' for the list of models available on your system.\n"
83             . "You may need to install another Config::Model Perl module.\n"
84 19 50       46 . "See the available models there: https://github.com/dod38fr/config-model/wiki/Available-models-and-backends\n";
85 0         0 }
86              
87             my $command = (split('::', ref($self)))[-1] ;
88              
89             if ($appli_info->{$application}{require_config_file}
90             and $appli_info->{$application}{require_backend_argument}) {
91 19         82 die "Error in $root_model model: cannot have both require_config_file and require_backend_argument.\n";
92             }
93 19 0 33     65  
94             # @ARGV should be [ $config_file ] [ modification_instructions ]
95 0         0 my $config_file;
96             if ( $appli_info->{$application}{require_config_file} ) {
97             $config_file = $opt->{file} || shift @$args ;
98             $self->usage_error(
99 19         27 "no config file specified. Command should be 'cme $command $application configuration_file'",
100 19 50       64 ) unless $config_file;
    50          
101 0   0     0 }
102 0 0       0 elsif ( $appli_info->{$application}{allow_config_file_override}) {
103             $config_file = $opt->{file};
104             }
105              
106             if ( $appli_info->{$application}{require_backend_argument} ) {
107 0         0 # let the backend handle a missing arg and provide a clear error message
108             my $b_arg = $opt->{_backend_arg} = shift @$args ;
109             if (not $b_arg) {
110 19 50       53 my $message = $appli_info->{$application}{backend_argument_info} ;
111             my $insert = $message ? " ( $message )": '';
112 0         0 die "application $application requires a 3rd argument$insert. "
113 0 0       0 . "I.e. 'cme $command $application <backend_arg>'\n";
114 0         0 }
115 0 0       0  
116 0         0 if ( $appli_info->{$application}{use_backend_argument_as_config_file} ) {
117             $config_file = $appli_info->{$application}{config_dir} . '/' . $b_arg;
118             }
119             }
120 0 0       0  
121 0         0 # remove legacy '~~'
122             if ($args->[0] and $args->[0] eq '~~') {
123             warn "Argument '~~' was a bad idea and is now ignored. Use -file option to "
124             ."specify a target file or just forget about '~~' argument\n";
125             shift @$args;
126 19 50 66     59 }
127 0         0  
128             # override (or specify) configuration dir
129 0         0 $opt->{_config_dir} = $appli_info->{$application}{config_dir};
130              
131             $opt->{_application} = $application ;
132             $opt->{_config_file} = $config_file;
133 19         57 $opt->{_root_model} = $root_model;
134              
135 19         37 return;
136 19         31 }
137 19         37  
138             my ($self, $opt, $args) = @_;
139 19         111  
140             my %cm_args;
141             $cm_args{model_dir} = $opt->{model_dir} if $opt->{model_dir};
142              
143 38     38 0 65 if (not $self->{_model}) {
144             initialize_log4perl( verbose => $opt->{_verbose} );
145 38         45  
146 38 50       75 my $model = $self->{_model} = Config::Model->new( %cm_args );
147             push @store, $model;
148 38 100       253 }
149 19         87 return $self->{_model};
150             }
151 19         238281  
152 19         1690 my ($self, $opt, $args) = @_;
153              
154 38         240 my %instance_args = (
155             root_class_name => $opt->{_root_model},
156             instance_name => $opt->{_application},
157             application => $opt->{_application},
158 38     38 0 18382 check => $opt->{force_load} ? 'no' : 'yes',
159             auto_create => $opt->{create},
160             backend_arg => $opt->{_backend_arg},
161             config_file => $opt->{_config_file},
162             config_dir => $opt->{_config_dir},
163             );
164              
165             foreach my $param (qw/root_dir canonical backup/) {
166             $instance_args{$param} = $opt->{$param} if defined $opt->{$param};
167             }
168              
169 38 50       227 return $self->{_instance} ||= $self->model->instance(%instance_args);
170             }
171 38         77  
172 114 100       226 my ($self, @args) = @_;
173             # model and inst are deleted if not kept in a scope
174             return ( $self->model(@args) , $self->instance(@args), $self->instance->config_root );
175 38   66     240 }
176              
177             my ($self,$inst,$opt) = @_;
178              
179 19     19 0 53 $inst->say_changes unless $opt->{quiet};
180              
181 19         55 # if load was forced, must write back to clean up errors (even if they are not changes
182             # at semantic level, i.e. removed unnecessary stuff)
183             $inst->write_back( force => $opt->{force_load} || $opt->{save} );
184              
185 14     14 0 41 return;
186             }
187 14 100       69  
188             my ($self, $instance, $opt) = @_;
189              
190             require Config::Model::TkUI;
191 14   66     3422 require Tk;
192             require Tk::ErrorDialog;
193 14         124718 Tk->import;
194              
195             no warnings 'once'; ## no critic (TestingAndDebugging::ProhibitNoWarnings)
196             my $mw = MainWindow->new;
197 0     0 0   $mw->withdraw;
198              
199 0           # Thanks to Jerome Quelin for the tip
200 0           $mw->optionAdd( '*BorderWidth' => 1 );
201 0            
202 0           # -root parameter is deprecated
203             my $cmu = $mw->ConfigModelUI( -instance => $instance );
204 2     2   14  
  2         4  
  2         826  
205 0           $instance->on_message_cb(sub{$cmu->show_message(@_);});
206 0            
207             if ($opt->{open_item}) {
208             my $obj = $instance->grab($opt->{open_item});
209 0           $cmu->force_element_display($obj);
210             }
211              
212 0           &MainLoop; # Tk's
213              
214 0     0     return;
  0            
215             }
216 0 0          
217 0           my ($self, $term_class, $inst) = @_;
218 0            
219             my $shell_ui = $term_class->new (
220             root => $inst->config_root,
221 0           title => $inst->application . ' configuration',
222             prompt => ' >',
223 0           );
224              
225             # engage in user interaction
226             $shell_ui->run_loop;
227 0     0 0    
228             return;
229 0           }
230              
231             my ($self) = @_;
232              
233             my $parser = Pod::POM->new();
234             my $pkg = blessed ($self);
235             $pkg =~ s!::!/!g;
236 0           my $pom = $parser->parse_file($INC{$pkg.'.pm'})
237             || croak $parser->error();
238 0            
239             my $sections = $pom->head1();
240             my @ret ;
241             foreach my $s (@$sections) {
242 0     0 0   push (@ret ,$s) if $s->title() =~ /DESCRIPTION|USAGE|OPTIONS|EXIT/;
243             }
244 0           return join ("", map { Pod::POM::View::Text->print($_)} @ret) . "Options:\n";;
245 0           }
246 0            
247 0   0       1;
248              
249              
250 0           =pod
251 0            
252 0           =encoding UTF-8
253 0 0          
254             =head1 NAME
255 0            
  0            
256             App::Cme::Common - Common methods for App::Cme
257              
258             =head1 VERSION
259              
260             version 1.038
261              
262             =head1 SYNOPSIS
263              
264             # Internal. Used by App::Cme::Command::*
265              
266             =head1 DESCRIPTION
267              
268             Common methods for all cme commands
269              
270             =head1 AUTHOR
271              
272             Dominique Dumont
273              
274             =head1 COPYRIGHT AND LICENSE
275              
276             This software is Copyright (c) 2014-2022 by Dominique Dumont <ddumont@cpan.org>.
277              
278             This is free software, licensed under:
279              
280             The GNU Lesser General Public License, Version 2.1, February 1999
281              
282             =cut