source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Cli/Base.pm @ 2213

Last change on this file since 2213 was 2213, checked in by nanardon, 5 years ago

Migrate online help to Pod format

File size: 10.7 KB
Line 
1package LATMOS::Accounts::Cli::Base;
2
3# $Id: Cli.pm 2145 2018-08-29 18:15:46Z nanardon $
4
5use strict;
6use warnings;
7use Moose;
8use LATMOS::Accounts::Cli::Context;
9use LATMOS::Accounts::Log;
10use LATMOS::Accounts::Utils;
11use Term::ReadLine;
12use Text::ParseWords;
13use Getopt::Long;
14use Pod::Select;
15use Pod::Text::Termcap;
16use File::Temp;
17
18=head1 NAME
19
20LATMOS::Accounts::Cli - Command line interface functions
21
22=head1 DESCRIPTION
23
24This module handle envirronment and functons for L<la-cli> tools.
25
26=cut
27
28has Context => ( is => 'ro', isa => 'LATMOS::Accounts::Cli::Context' );
29
30=head1 FUNCTIONS
31
32=cut
33
34=head1 CLI FUNCTIONS
35
36=cut
37
38sub BUILD {
39    my $self = shift;
40
41    my $OUT = $self->Context->Out;
42
43    if ($self->base->is_transactionnal) {
44        $self->add_func(
45            'transaction', {
46                help => 'change transaction mode',
47                code => sub {
48                    $self->Context->TransMode($_[1] eq 'on' ? 1 : 0);
49                },
50                completion => sub {
51                    $self->Context->TransMode == 0 ? 'on' : 'off';
52                },
53            }
54        );
55        $self->add_func(
56            'begin', {
57                help => 'Start transaction',
58                code => sub {
59                    $self->Context->TransStarted(1);
60                },
61            }
62        );
63        $self->add_func(
64            'commit', {
65                help => 'commit pending change',
66                code => sub {
67                    $_[0]->_commit;
68                },
69            }
70        );
71        $self->add_func(
72            'rollback', {
73                help => 'commit pending change',
74                code => sub {
75                    $_[0]->_rollback;
76                },
77            }
78        );
79    }
80    if ($self->base->can('CreateAlias')) {
81        $self->add_func(
82            'newalias', {
83                help => 'Create an alias object',
84                code => sub {
85                    my ($self, $otype, $name, $for) = @_;
86                    if ($self->base->CreateAlias($otype, $name, $for)) {
87                        print $OUT "Alias $otype/$name Created\n";
88                        $self->commit;
89                    }
90                },
91                completion => sub {
92                    if ($_[3]) {
93                        return $_[0]->base->list_objects($_[2]);
94                    } elsif (!$_[2]) {
95                        return $_[0]->base->list_supported_objects;
96                    } else {
97                        return;
98                    }
99                }
100            },
101        );
102        $self->add_func(
103            'rmalias', {
104                help => 'Remove an alias object',
105                code => sub {
106                    my ($self, $otype, $name) = @_;
107                    if ($self->base->RemoveAlias($otype, $name)) {
108                        print $OUT "Alias $otype/$name Removed\n";
109                        $self->commit;
110                    }
111                },
112                completion => sub {
113                    if (!$_[2]) {
114                        return $_[0]->base->list_supported_objects;
115                    } else {
116                        return $_[0]->base->search_objects($_[2], 'oalias=*');
117                    }
118                }
119            },
120        );
121        $self->add_func(
122            'updalias', {
123                help => 'Update an alias object',
124                code => sub {
125                    my ($self, $otype, $name, $for) = @_;
126                    my $obj = $self->base->GetAlias($otype, $name) or do {
127                        print $OUT "No alias $otype/$name found";
128                        return;
129                    };
130                    if ($obj->set_c_fields(oalias => $for)) {
131                        print $OUT "Alias $otype/$name Updated\n";
132                        $self->commit;
133                    }
134                },
135                completion => sub {
136                    if ($_[3]) {
137                        return $_[0]->base->list_objects($_[2]);
138                    } elsif($_[2]) {
139                        return $_[0]->base->search_objects($_[2], 'oalias=*');
140                    } else {
141                        return $_[0]->base->list_supported_objects;
142                    }
143                }
144            },
145        );
146    }
147    $self->add_func('quit', { help => 'quit - exit the tool',
148            code => sub { print "\n"; exit(0) }, });
149    $self->add_func('exit', { help => "exit current mode",
150            code => sub { return "EXIT" }, });
151
152
153=head2 help
154
155    help [command] - print help about command
156
157=cut
158
159    $self->add_func('help', {
160        completion => sub {
161            if (!$_[2]) { return sort keys %{ $_[0]->{funcs} || {}} }
162        },
163        code => sub {
164            my $env = shift;
165
166            $env->Help(@_);
167        },
168    });
169
170    $self->add_func( 'query' => {
171            proxy => '*',
172            help  => 'show attribute',
173            completion => sub { },
174            code => sub {
175                my $env = shift;
176                my @args = $self->getoption(
177                    {
178                        'o|object=s' => \my $otype,
179                        'e|empty'    => \my $empty_attr,
180                        'ro'         => \my $with_ro,
181                        'fmt=s'      => \my $fmt,
182                        'filefmt=s'  => \my $filefmt,
183                    }, @_
184                );
185                $otype ||= 'user';
186
187                my $objs = $self->Context->{objs};
188
189                if (! $objs ) {
190                    foreach my $name (@args) {
191                        my $obj = $self->base->get_object( $otype, $name) or do {
192                            $self->print("Cannot get object $otype/$name\n");
193                            next;
194                        };
195                        push(@{ $objs }, $obj);
196                    }
197                }
198
199                if ($filefmt){
200                    open(my $hfmt, '<', $filefmt) or die "Cannot open $filefmt\n";
201                    $fmt ||= ''; # avoid undef warning
202                    while (<$hfmt>) {
203                        chomp($fmt .= $_);
204                    }
205                    close $hfmt;
206                }
207
208                foreach (@{ $objs }) {
209                    if ($fmt) {
210                        $self->print($_->queryformat($fmt));
211                    } else {
212                        $_->text_dump( $self->Context->Out, {
213                            empty_attr => $empty_attr,
214                            only_rw => !$with_ro,
215                        } );
216                    }
217                }
218            },
219    } );
220}
221
222=head2 base
223
224Return the attached base object.
225
226=cut
227
228sub base { $_[0]->Context->base }
229sub term { $_[0]->Context->Term }
230sub print { shift->Context->print(@_) }
231
232=head2 cli
233
234Start the main loop
235
236=cut
237
238sub cli {
239    my ($self) = @_;
240
241    my $term = $_[0]->Context->Term;
242
243    while (1) {
244        $term->Attribs->{completion_function} = sub {
245            $self->complete($_[0], shellwords(substr($_[1], 0, $_[2])));
246        };
247        defined (my $line = $term->readline($self->prompt)) or do {
248            $self->print("\n");
249            return;
250        };
251        $term->addhistory($line);
252        my $res = $self->run(shellwords($line));
253        $self->rollback if (!$self->Context->TransMode);
254        if ($res && $res eq 'EXIT') { $self->print("\n"); return }
255    }
256}
257
258=head2 prompt
259
260Wait user to input command
261
262=cut
263
264sub promptPrefix { 'LA cli' }
265
266sub prompt {
267    my ($self) = @_;
268    my $pr = $self->promptPrefix;
269    return sprintf(
270        "%s%s%s ",
271        $pr,
272        $self->Context->TransStarted ? '-' : '=',
273        $self->Context->TransMode  ? '#' : '>',
274    );
275}
276
277=head2 add_func ($name, $param)
278
279Add new function in the envirronment
280
281=cut
282
283# TODO: hide this
284
285sub add_func {
286    my ($self, $name, $param) = @_;
287    my (undef, $file) = caller(0);
288    $param->{podfile} = $file;
289    $self->{funcs}{$name} = $param;
290}
291
292=head2 Help
293
294Display help of given function
295
296=cut
297
298sub Help {
299    my ($self, $name) = @_;
300    if (!$name) {
301        $self->print(join(', ', sort keys %{ $self->{funcs} || {}}) . "\n");
302    } elsif ($self->{funcs}{$name}{alias}) {
303        $self->print("$name is an alias for " . join(' ', @{$self->{funcs}{$name}{alias}}) . "\n");
304    } elsif ($self->{funcs}{$name}{help}) {
305        $self->print($self->{funcs}{$name}{help});
306    } else {
307        my $fh = File::Temp->new();
308        my $parser = Pod::Text::Termcap->new( sentence => 0, width => 78 );
309
310        podselect(
311            {-output => $fh, -sections => ["CLI FUNCTIONS/\Q$name"]},
312            $self->{funcs}{$name}{podfile}
313        );
314        seek($fh, 0, 0);
315        $parser->parse_from_filehandle($fh, $self->Context->Out);
316    }
317}
318
319=head2 getoption ($opt, @args)
320
321Parse commmand line
322
323=cut
324
325sub getoption {
326    my ($self, $opt, @args) = @_;
327    local @ARGV = @args;
328    Getopt::Long::Configure("pass_through");
329    GetOptions(%{ $opt });
330
331    return @ARGV;
332}
333
334=head2 complete
335
336Return possible words according current entered words
337
338=cut
339
340sub complete {
341    my ($self, $lastw, $name, @args) = @_;
342    if (!$name) {
343        return grep { /^\Q$lastw\E/ } sort
344            (keys %{ $self->{funcs} || {}});
345    } elsif ($self->{funcs}{$name}{alias}) {
346        $self->complete($lastw, @{$self->{funcs}{$name}{alias}}, @args);
347    } elsif ($self->{funcs}{$name}{completion}) {
348        return map { my $t = $_; $t =~ s/\s/\\ /g; $t } grep { $_ && /^\Q$lastw\E/ } $self->{funcs}{$name}{completion}->($self, $lastw, @args);
349    } else {
350        return ();
351    }
352}
353
354=head2 run ($name, @args)
355
356Run functions
357
358=cut
359
360sub run {
361    my ($self, $name, @args) = @_;
362    return if (!$name);
363
364    if (grep { m/^(-h|--help)$/ } @args) {
365        $self->Help($name);
366    } elsif (!exists($self->{funcs}{$name})) {
367        $self->print("No command $name found\n");
368    } elsif ($self->{funcs}{$name}{alias}) {
369        $self->run(@{$self->{funcs}{$name}{alias}}, @args);
370    } elsif ($self->{funcs}{$name}{code}) {
371        $self->{funcs}{$name}{code}->($self, @args);
372    } else {
373        $self->print("No command $name found\n");
374    }
375}
376
377=head2 commit
378
379Call commit to base unelss in transaction mode
380
381=cut
382
383sub commit {
384    my ($self) = @_;
385    $self->Context->commit;
386}
387
388sub _commit {
389    my ($self) = @_;
390    $self->Context->_commit;
391}
392
393=head2 rollback
394
395Perform rollback unless in transaction mode
396
397=cut
398
399sub rollback {
400    my ($self) = @_;
401    $self->Context->rollback;
402}
403
404sub _rollback {
405    my ($self) = @_;
406    $self->Context->_rollback;
407}
408
4091;
410
411__END__
412
413=head1 SEE ALSO
414
415L<LATMOS::Accounts>
416
417=head1 AUTHOR
418
419Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
420
421=head1 COPYRIGHT AND LICENSE
422
423Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
424
425This library is free software; you can redistribute it and/or modify
426it under the same terms as Perl itself, either Perl version 5.10.0 or,
427at your option, any later version of Perl 5 you may have available.
428
429=cut
Note: See TracBrowser for help on using the repository browser.