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