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