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

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

Add log() function to cli

File size: 11.6 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=head2 log
222
223    log [[-o otype ] object]
224
225Show global log or log for the object given in arguments
226
227=cut
228
229    $self->add_func('log' => {
230        proxy => '*',
231        completion => sub { },
232        code => sub {
233            my $env = shift;
234            my @args = $self->getoption({
235                'o|object=s' => \my $otype,
236            }, @_);
237            $otype ||= 'user';
238
239            my @logs = @args
240                ? $self->base->getobjectlogs($otype, $args[0])
241                : $self->base->getlogs();
242
243            foreach (@logs) {
244                $self->print(
245                    "%s (%d), %s: %s/%s (%d) %s\n",
246                    $_->{logdate},
247                    $_->{irev} || -1,
248                    $_->{username},
249                    $_->{otype},
250                    $_->{name},
251                    $_->{ikey},
252                    $_->{message}
253                );
254            }
255        },
256    } );
257
258}
259
260=head2 base
261
262Return the attached base object.
263
264=cut
265
266sub base { $_[0]->Context->base }
267sub term { $_[0]->Context->Term }
268sub print { shift->Context->print(@_) }
269
270=head2 cli
271
272Start the main loop
273
274=cut
275
276sub cli {
277    my ($self) = @_;
278
279    my $term = $_[0]->Context->Term;
280
281    while (1) {
282        $term->Attribs->{completion_function} = sub {
283            $self->complete($_[0], shellwords(substr($_[1], 0, $_[2])));
284        };
285        defined (my $line = $term->readline($self->prompt)) or do {
286            $self->print("\n");
287            return;
288        };
289        $term->addhistory($line);
290        my $res = $self->run(shellwords($line));
291        $self->rollback if (!$self->Context->TransMode);
292        if ($res && $res eq 'EXIT') { $self->print("\n"); return }
293    }
294}
295
296=head2 prompt
297
298Wait user to input command
299
300=cut
301
302sub promptPrefix { 'LA cli' }
303
304sub prompt {
305    my ($self) = @_;
306    my $pr = $self->promptPrefix;
307    return sprintf(
308        "%s%s%s ",
309        $pr,
310        $self->Context->TransStarted ? '-' : '=',
311        $self->Context->TransMode  ? '#' : '>',
312    );
313}
314
315=head2 add_func ($name, $param)
316
317Add new function in the envirronment
318
319=cut
320
321# TODO: hide this
322
323sub add_func {
324    my ($self, $name, $param) = @_;
325    my (undef, $file) = caller(0);
326    $param->{podfile} = $file;
327    $self->{funcs}{$name} = $param;
328}
329
330=head2 Help
331
332Display help of given function
333
334=cut
335
336sub Help {
337    my ($self, $name) = @_;
338    if (!$name) {
339        $self->print(join(', ', sort keys %{ $self->{funcs} || {}}) . "\n");
340    } elsif ($self->{funcs}{$name}{alias}) {
341        $self->print("$name is an alias for " . join(' ', @{$self->{funcs}{$name}{alias}}) . "\n");
342    } elsif ($self->{funcs}{$name}{help}) {
343        $self->print($self->{funcs}{$name}{help});
344    } else {
345        my $fh = File::Temp->new();
346        my $parser = Pod::Text::Termcap->new( sentence => 0, width => 78 );
347
348        podselect(
349            {-output => $fh, -sections => ["CLI FUNCTIONS/\Q$name"]},
350            $self->{funcs}{$name}{podfile}
351        );
352        seek($fh, 0, 0);
353        $parser->parse_from_filehandle($fh, $self->Context->Out);
354    }
355}
356
357=head2 getoption ($opt, @args)
358
359Parse commmand line
360
361=cut
362
363sub getoption {
364    my ($self, $opt, @args) = @_;
365    local @ARGV = @args;
366    Getopt::Long::Configure("pass_through");
367    GetOptions(%{ $opt });
368
369    return @ARGV;
370}
371
372=head2 complete
373
374Return possible words according current entered words
375
376=cut
377
378sub complete {
379    my ($self, $lastw, $name, @args) = @_;
380    if (!$name) {
381        return grep { /^\Q$lastw\E/ } sort
382            (keys %{ $self->{funcs} || {}});
383    } elsif ($self->{funcs}{$name}{alias}) {
384        $self->complete($lastw, @{$self->{funcs}{$name}{alias}}, @args);
385    } elsif ($self->{funcs}{$name}{completion}) {
386        return map { my $t = $_; $t =~ s/\s/\\ /g; $t } grep { $_ && /^\Q$lastw\E/ } $self->{funcs}{$name}{completion}->($self, $lastw, @args);
387    } else {
388        return ();
389    }
390}
391
392=head2 run ($name, @args)
393
394Run functions
395
396=cut
397
398sub run {
399    my ($self, $name, @args) = @_;
400    return if (!$name);
401
402    if (grep { m/^(-h|--help)$/ } @args) {
403        $self->Help($name);
404    } elsif (!exists($self->{funcs}{$name})) {
405        $self->print("No command $name found\n");
406    } elsif ($self->{funcs}{$name}{alias}) {
407        $self->run(@{$self->{funcs}{$name}{alias}}, @args);
408    } elsif ($self->{funcs}{$name}{code}) {
409        $self->{funcs}{$name}{code}->($self, @args);
410    } else {
411        $self->print("No command $name found\n");
412    }
413}
414
415=head2 commit
416
417Call commit to base unelss in transaction mode
418
419=cut
420
421sub commit {
422    my ($self) = @_;
423    $self->Context->commit;
424}
425
426sub _commit {
427    my ($self) = @_;
428    $self->Context->_commit;
429}
430
431=head2 rollback
432
433Perform rollback unless in transaction mode
434
435=cut
436
437sub rollback {
438    my ($self) = @_;
439    $self->Context->rollback;
440}
441
442sub _rollback {
443    my ($self) = @_;
444    $self->Context->_rollback;
445}
446
4471;
448
449__END__
450
451=head1 SEE ALSO
452
453L<LATMOS::Accounts>
454
455=head1 AUTHOR
456
457Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
458
459=head1 COPYRIGHT AND LICENSE
460
461Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
462
463This library is free software; you can redistribute it and/or modify
464it under the same terms as Perl itself, either Perl version 5.10.0 or,
465at your option, any later version of Perl 5 you may have available.
466
467=cut
Note: See TracBrowser for help on using the repository browser.