File Coverage

blib/lib/Data/Interactive/Inspect.pm
Criterion Covered Total %
statement 143 410 34.8
branch 40 178 22.4
condition 3 21 14.2
subroutine 23 50 46.0
pod 13 21 61.9
total 222 680 32.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             #
3             # Copyright (c) 2015 T.v.Dein .
4             # All Rights Reserved. Std. disclaimer applies.
5             # Artistic License, same as perl itself. Have fun.
6             #
7              
8              
9             package Data::Interactive::Inspect;
10              
11 1     1   987 use Carp::Heavy;
  1         91  
  1         23  
12 1     1   4 use Carp;
  1         1  
  1         54  
13              
14 1     1   471 use Term::ReadLine;
  1         2084  
  1         30  
15 1     1   748 use File::Temp qw(tempfile); # required by the 'edit' command
  1         13972  
  1         60  
16 1     1   430 use YAML; # config + export/import + 'edit' command
  1         4939  
  1         50  
17              
18 1     1   6 use strict;
  1         1  
  1         26  
19 1     1   6 use warnings;
  1         2  
  1         32  
20 1     1   6 no strict 'refs';
  1         2  
  1         38  
21              
22 1     1   612 use Data::Dumper;
  1         4590  
  1         63  
23              
24             $Data::Interactive::Inspect::VERSION = 0.04;
25              
26 1     1   6 use vars qw(@ISA);
  1         1  
  1         39  
27              
28 1     1   4 use vars qw(@ISA @EXPORT @EXPORT_OK);
  1         1  
  1         1409  
29             require Exporter;
30             @ISA = qw(Exporter);
31             @EXPORT = qw();
32             @EXPORT_OK = qw();
33              
34              
35             sub new {
36 1     1 1 706 my ($class, @param) = @_;
37              
38 1   33     7 $class = ref($class) || $class;
39              
40             # defaults (= valid parameters)
41             my $self = {
42             name => '',
43 0     0   0 begin => sub { print STDERR "unsupported\n"; },
44 0     0   0 commit => sub { print STDERR "unsupported\n"; },
45 0     0   0 rollback => sub { print STDERR "unsupported\n"; },
46 0     0   0 export => sub { my ($db) = @_; return $db; },
  0         0  
47 0     0   0 serialize => sub { my $db = shift; return YAML::Dump($db); },
  0         0  
48 0     0   0 deserialize => sub { my $db = shift; return YAML::Load($db); },
  0         0  
49 1         11 struct => {},
50             editor => 'vi',
51             more => 'more',
52             silent => 0,
53             };
54              
55 1         3 bless $self, $class;
56              
57             # by default unsupported
58 1         4 $self->{transactions} = 0;
59              
60 1 50       5 if ($#param >= 1) {
    50          
61             # hash interface
62 0         0 my %p = @param;
63 0         0 foreach my $k (keys %{$self}) {
  0         0  
64 0 0       0 if (exists $p{$k}) {
65 0         0 $self->{$k} = $p{$k};
66             }
67             }
68 0 0 0     0 if (exists $p{begin} && $p{commit} && $p{rollback}) {
      0        
69             # user supplied the relevant functions
70 0         0 $self->{transactions} = 1;
71             }
72             }
73             elsif ($#param == 0) {
74             # 1 param, the struct
75 1         3 $self->{struct} = $param[0];
76             }
77             # else: no params given, work with defaults
78              
79 1 50       3 if (! $self->{struct}) {
80 0         0 croak "Sorry param 'struct' must be set to a perl data structure\n";
81             }
82              
83             # map commands+shortcuts to functions
84 1         12 $self->{command} = {
85             l => 'list',
86             list => 'list',
87             show => 'show',
88             sh => 'show',
89             dump => 'dump',
90             d => 'dump',
91             get => 'get',
92             g => 'get',
93             enter => 'enter',
94             cd => 'enter',
95             set => 'set',
96             edit => 'edit',
97             e => 'edit',
98             append=> 'append',
99             drop => 'drop',
100             pop => 'mypop',
101             shift => 'myshift',
102             search=> 'search',
103             '/' => 'search',
104             help => 'help',
105             h => 'help',
106             '?' => 'help',
107             quit => 'quit',
108             q => 'quit',
109             };
110              
111 1 50       3 if ($self->{transactions}) {
112             # map if supported
113 0         0 foreach my $c(qw(begin commit rollback)) {
114 0         0 $self->{command}->{$c} = $c;
115             }
116             }
117              
118 1 50       2 if (! $self->{name}) {
119 1         5 $self->{name} = sprintf "data\@0x%x", $self->{struct};
120             }
121              
122             # map which commands take a key param
123 1         5 $self->{commandargs} = [qw(get set edit show append pop shift drop enter cd search /)];
124              
125             # holds current level
126 1         2 $self->{db} = $self->{struct};
127              
128             # path to current level
129 1         2 $self->{path} = [];
130              
131             # set to 1 if transactions supported and implemented
132 1         2 $self->{session} = 0;
133              
134 1         2 $self->{quit} = 0;
135              
136 1         3 return $self;
137             }
138              
139              
140              
141              
142             sub inspect {
143 7     7 1 3572 my ($self, $__cmds) = @_;
144              
145 7 50       14 if ($__cmds) {
146             # unit tests und scripts
147 7         14 $self->{silent} = 1;
148 7         18 foreach (split /\n/, $__cmds) {
149 9 50       17 if (! $self->process($_, 1) ) {
150 0         0 last;
151             }
152             }
153 7         16 return $self->{struct};
154             }
155              
156 0 0       0 if (-t STDIN) {
157             # interactive with prompt and history
158 0         0 $| = 1;
159 0         0 my $term = new Term::ReadLine 'Data::Interactive::Inspect';
160 0         0 $term->ornaments(0);
161 0         0 my $attribs = $term->Attribs;
162              
163 0         0 $self->{term} = $term;
164 0 0       0 $self->{complete_words} = [ map { if (length($_) > 2 ) {$_} } keys %{$self->{command}} ];
  0         0  
  0         0  
  0         0  
165 0         0 $attribs->{completion_entry_function} = $attribs->{list_completion_function}; # avoid file completion
166             $attribs->{attempted_completion_function} = sub {
167 0     0   0 my ($begin, $line, $start, $end, $term) = @_;
168 0         0 return $self->complete($begin, $line, $start, $end, $term);
169 0         0 };
170              
171 0         0 my $prompt = $self->prompt;
172 0         0 while ( defined ($_ = $term->readline($prompt)) ) {
173 0 0       0 if (! $self->process($_) ) {
174 0         0 print "last\n";
175 0         0 last;
176             }
177 0         0 $prompt = $self->prompt;
178             }
179             }
180             else {
181 0         0 while () {
182 0 0       0 if (! $self->process($_, 1) ) {
183 0         0 last;
184             }
185             }
186             }
187 0         0 return $self->{struct};
188             }
189              
190             sub prompt {
191 0     0 0 0 my $self = shift;
192 0         0 my $prompt = $self->{name};
193              
194 0 0       0 if (@{$self->{path}}) {
  0         0  
195 0         0 $prompt .= " " . join('->', @{$self->{path}});
  0         0  
196             }
197 0 0       0 if ($self->{session}) {
198 0         0 $prompt .= '%';
199             }
200 0         0 $prompt .= '> ';
201 0         0 return $prompt;
202             }
203              
204             sub complete {
205 0     0 0 0 my ($self, $begin, $line, $start, $end, $term) = @_;
206              
207 0         0 my @matches = ();
208              
209 0         0 my $cmd = $line;
210 0         0 $cmd =~ s/\s\s*.*$//;
211 0         0 $cmd =~ s/\s*$//;
212              
213 0 0       0 if ($start == 0) {
    0          
214             # match on a command
215             @matches = $self->{term}->completion_matches ($begin, sub {
216 0     0   0 my ($text, $state) = @_;
217 0         0 my @name = @{$self->{complete_words}};
  0         0  
218 0 0       0 unless ($state) {
219 0         0 $self->{complete_idx} = 0;
220             }
221 0         0 while ($self->{complete_idx} <= $#name) {
222 0         0 $self->{complete_idx}++;
223 0 0       0 return $name[$self->{complete_idx} - 1]
224             if ($name[$self->{complete_idx} - 1] =~ /^$text/);
225             }
226             # no match
227 0         0 return undef;
228 0         0 });
229             }
230             elsif ($line =~ /[^\s]+\s+[^\s]+\s+/) {
231             # command line is complete ($cmd $arg), stop with completion
232 0         0 @matches = undef;
233             }
234             else {
235             # match on a command arg
236 0 0       0 if (grep {$cmd eq $_} @{$self->{commandargs}}) {
  0         0  
  0         0  
237             # only match for commands which support args
238             @matches = $self->{term}->completion_matches ($begin, sub {
239 0     0   0 my ($text, $state) = @_;
240 0         0 my @name = keys %{$self->{db}};
  0         0  
241 0 0       0 unless ($state) {
242 0         0 $self->{complete_idxp} = 0;
243             }
244 0         0 while ($self->{complete_idxp} <= $#name) {
245 0         0 $self->{complete_idxp}++;
246 0 0       0 return $name[$self->{complete_idxp} - 1]
247             if ($name[$self->{complete_idxp} - 1] =~ /^$text/);
248             }
249             # no match
250 0         0 return undef;
251 0         0 });
252             }
253             else {
254             # command doesn't support args
255 0         0 @matches = undef;
256             }
257             }
258              
259 0         0 return @matches;
260             }
261              
262             sub process {
263 9     9 0 11 my ($self, $line, $failonerr) = @_;
264              
265 9 50       13 return 1 if(!defined $line);
266              
267             # special treatment to search command
268 9         25 $line =~ s|^/(.+)|/ $1|;
269              
270 9         5 my $r;
271 9         41 my ($cmd, @args) = split /\s\s*/, $line;
272              
273 9 50       19 return 1 if (!defined $cmd);
274 9 50       24 return 1 if ($cmd =~ /^\s*$/);
275 9 50       15 return 1 if ($cmd =~ /^#/);
276              
277              
278              
279 9 50       15 if ($cmd eq '..') {
280 0         0 $self->up;
281             }
282             else {
283 9 50       18 if (exists $self->{command}->{$cmd}) {
284 9         10 my $func = $self->{command}->{$cmd};
285 9 50       7 if (! grep {$cmd eq $_} @{$self->{commandargs}}) {
  108         110  
  9         17  
286 0         0 @args = ();
287             }
288 9         23 $r = $self->$func(@args);
289 9 50 33     36 return 0 if($failonerr && !$r); # fail if not interactive
290 9 50       18 return 0 if($self->{quit}); # finish
291             }
292             else {
293 0 0       0 if (ref($self->{db}) =~ /hash/i) {
294 0 0       0 if (exists $self->{db}->{$cmd}) {
295 0         0 $r = $self->enter($cmd);
296 0 0 0     0 return 0 if($failonerr && !$r); # fail if not interactive
297             }
298             else {
299 0         0 print STDERR "no such command: $cmd\n";
300 0 0       0 return 0 if $failonerr;
301             }
302             }
303             else {
304 0         0 print STDERR "no such command: $cmd\n";
305 0 0       0 return 0 if $failonerr;
306             }
307             }
308             }
309 9         29 return 1;
310             }
311              
312              
313              
314             # command implementations
315 0     0   0 sub __interactive__ {};
316              
317             sub _fail {
318 0     0   0 my ($self, $msg) = @_;
319 0         0 print STDERR $msg;
320 0         0 return 0;
321             }
322              
323             sub _failkey {
324 0     0   0 my $self = shift;
325 0         0 return $self->_fail(" parameter missing\n");
326             }
327              
328             sub _failidx {
329 0     0   0 my $self = shift;
330 0         0 return $self->_fail(" must be a number, since we're inside an array\n");
331             }
332              
333             sub quit {
334 0     0 0 0 my $self = shift;
335 0         0 $self->{quit} = 1;
336 0         0 return 0;
337             }
338              
339             sub set {
340 3     3 1 5 my($self, $key, @value) = @_;
341              
342 3 50       5 return $self->_failkey() if(! defined $key);
343              
344 3         3 my $var;
345 3         7 my $code = "\$var = @value;";
346             {
347 1     1   6 no strict;
  1         1  
  1         35  
  3         3  
348 1     1   4 no warnings;
  1         1  
  1         2147  
349 3         162 eval $code;
350             }
351 3 50       9 if ($@) {
352 0         0 return $self->_fail("failed to insert: $@\n");
353             }
354             else {
355 3         5 $self->{db}->{$key} = $var;
356 3         6 $self->done;
357             }
358 3         4 return 1;
359             }
360              
361             sub append {
362 1     1 1 3 my($self, $key, @value) = @_;
363              
364 1 50       5 if (ref($self->{db}) !~ /array/i) {
365 1 50       3 return $self->_failkey() if(! defined $key);
366              
367 1 50       4 if (exists $self->{db}->{$key}) {
368 1 50       6 if (ref($self->{db}->{$key}) !~ /array/i) {
369 0         0 return $self->_fail("\"$key\" already exists and is not an array\n");
370             }
371             }
372             }
373             else {
374             # inside an array, ignore $key
375 0         0 @value = ($key, @value);
376             }
377              
378 1         1 my $var;
379 1         2 my $code = "\$var = @value;";
380 1         60 eval $code;
381 1 50       4 if ($@) {
382 0         0 return $self->_fail("failed to evaluate: $@\n");
383             }
384             else {
385 1 50       4 if (ref($self->{db}) =~ /array/i) {
386 0         0 push @{$self->{db}}, $var;
  0         0  
387             }
388             else {
389 1         1 push @{$self->{db}->{$key}}, $var;
  1         2  
390             }
391 1         2 $self->done;
392             }
393              
394 1         2 return 1;
395             }
396              
397             sub drop {
398 1     1 1 2 my($self, $key) = @_;
399              
400 1 50       4 if (ref($self->{db}) =~ /array/i) {
401 0 0       0 return $self->_failidx if($key !~ /^\d*$/);
402 0 0       0 if (scalar @{$self->{db}} -1 < $key) {
  0         0  
403 0         0 return $self->_fail("array element $key exceeds number of elements in current array\n");
404             }
405             else {
406 0         0 splice @{$self->{db}}, $key, 1;
  0         0  
407 0         0 $self->done;
408             }
409             }
410             else {
411 1 50       2 return $self->_failkey() if(! defined $key);
412              
413 1 50       3 if (exists $self->{db}->{$key}) {
414 1         2 delete $self->{db}->{$key};
415 1         3 $self->done;
416             }
417             else {
418 0         0 return $self->_fail("no such key: \"$key\"\n");
419             }
420             }
421              
422 1         2 return 1;
423             }
424              
425             sub mypop {
426 1     1 0 2 my($self, $key) = @_;
427              
428 1 50       3 if (ref($self->{db}) !~ /array/i) {
429 1 50       2 return $self->_failkey() if(! defined $key);
430              
431 1 50       3 if (exists $self->{db}->{$key}) {
432 1 50       5 if (ref($self->{db}->{$key}) !~ /array/i) {
433 0         0 return $self->_fail("\"$key\" is not an array\n");
434             }
435             }
436 1         1 my $ignore = pop @{$self->{db}->{$key}};
  1         2  
437 1         2 $self->done;
438             }
439             else {
440 0         0 my $ignore = pop @{$self->{db}};
  0         0  
441             }
442              
443 1         1 return 1;
444             }
445              
446             sub myshift {
447 1     1 0 2 my($self, $key) = @_;
448              
449 1 50       4 if (ref($self->{db}) !~ /array/i) {
450 1 50       2 return $self->_failkey() if(! defined $key);
451              
452 1 50       3 if (exists $self->{db}->{$key}) {
453 1 50       5 if (ref($self->{db}->{$key}) !~ /array/i) {
454 0         0 return $self->_fail("\"$key\" is not an array\n");
455             }
456             }
457 1         1 my $ignore = shift @{$self->{db}->{$key}};
  1         2  
458 1         2 $self->done;
459             }
460             else {
461 0         0 my $ignore = shift @{$self->{db}};
  0         0  
462             }
463 1         2 return 1;
464             }
465              
466             sub get {
467 0     0 1 0 my($self, $key, $search) = @_;
468              
469 0 0       0 return $self->_failkey() if(! defined $key);
470              
471 0         0 my $out;
472             my @K;
473 0 0       0 if ($key =~ /^\/.*\/$/) {
474             # regex
475 0         0 $key =~ s#^/##;
476 0         0 $key =~ s#/$##;
477 0         0 foreach my $k (keys %{$self->{db}}) {
  0         0  
478 0 0       0 if ($k =~ /$key/) {
479 0         0 push @K, $k;
480             }
481             }
482             }
483             else {
484 0 0       0 if (ref($self->{db}) =~ /array/i) {
    0          
485 0 0       0 return $self->_failidx if($key !~ /^\d*$/);
486 0 0       0 if (scalar @{$self->{db}} -1 < $key) {
  0         0  
487 0         0 return $self->_fail("array element $key exceeds number of elements in current array\n");
488             }
489             else {
490 0         0 $out .= "[$key] =>\n" . $self->dump($self->{db}->[$key], 1)
491             }
492             }
493             elsif (exists $self->{db}->{$key}) {
494 0         0 push @K, $key;
495             }
496             else {
497 0         0 return $self->_fail("no such key: \"$key\"\n");
498             }
499             }
500              
501 0         0 foreach my $key (@K) {
502 0 0 0     0 if (ref($self->{db}->{$key}) =~ /hash/i || ref($self->{db}->{$key}) =~ /array/i) {
503             # FIXME: something nicer
504 0         0 $out .= "$key =>\n" . $self->dump($self->{db}->{$key}, 1)
505             }
506             else {
507 0         0 $out .= "$key => \"$self->{db}->{$key}\"\n";
508             }
509             }
510 0         0 print $out;
511              
512 0         0 return 1;
513             }
514              
515             sub dump {
516 0     0 1 0 my ($self, $obj, $noprint) = @_;
517 0         0 my $out;
518 0 0       0 if ($obj) {
519 0         0 $out = $self->{serialize}->($self->{export}->($obj));
520             }
521             else {
522 0         0 $out = $self->{serialize}->($self->{export}->($self->{db}));
523             }
524              
525 0 0       0 if ($noprint) {
526 0         0 return $out;
527             }
528             else {
529 0 0       0 if (open LESS, "|$self->{more}") {
530 0         0 print LESS $out;
531 0         0 close LESS;
532             }
533             else {
534 0         0 print $out;
535             }
536             }
537              
538 0         0 return 1;
539             }
540              
541             sub edit {
542 0     0 1 0 my ($self, $key) = @_;
543              
544 0 0       0 return $self->_failkey() if(! defined $key);
545              
546 0 0       0 if (exists $self->{db}->{$key}) {
547 0         0 my $data = $self->{serialize}->($self->{export}->($self->{db}->{$key}));
548 0         0 my ($fh, $filename) = tempfile();
549 0         0 print $fh $data;
550 0         0 close $fh;
551 0         0 system("$self->{editor}", $filename);
552 0         0 open IN, "<$filename";
553 0         0 my $newdata = join '', ;
554 0         0 close IN;
555 0 0       0 if ($newdata eq $data) {
556             # FIXME: use checksum or something else which is faster
557 0         0 print "unchanged\n";
558             }
559             else {
560 0         0 my $perl;
561 0         0 eval {
562 0         0 $perl = $self->{deserialize}->($newdata);
563             };
564 0 0       0 if ($@) {
565 0         0 return $self->_fail("$@\n");
566             }
567             else {
568 0         0 $self->{db}->{$key} = $perl;
569 0         0 $self->done;
570             }
571             }
572 0         0 unlink($filename);
573             }
574             else {
575 0         0 return $self->_fail("no such key: \"$key\"\n");
576             }
577              
578 0         0 return 1;
579             }
580              
581             sub list {
582 0     0 1 0 my $self = shift;
583 0         0 print join "\n", sort keys %{$self->{db}};
  0         0  
584 0         0 print "\n";
585              
586 0         0 return 1;
587             }
588              
589             sub show {
590 0     0 1 0 my ($self, $indent) = @_;
591              
592              
593              
594 0 0       0 if (ref($self->{db}) =~ /array/i) {
595 0         0 my $pos = 0;
596 0         0 foreach my $item (@{$self->{db}}) {
  0         0  
597 0         0 print "$pos:\n";
598 0 0       0 if (ref($item)) {
599 0         0 $self->_showhash($item, " ");
600             }
601             else {
602 0         0 print " $item\n";
603             }
604 0         0 $pos++;
605             }
606             }
607             else {
608 0         0 $self->_showhash($self->{db});
609             }
610              
611 0         0 return 1;
612             }
613              
614             sub _showhash {
615 0     0   0 my($self, $db, $indent) = @_;
616              
617 0 0       0 if (!defined $indent) {
618 0         0 $indent = '';
619             }
620              
621 0         0 foreach my $key (sort keys %{$db}) {
  0         0  
622 0         0 printf "%s%-30s", $indent, $key;
623 0 0       0 if (ref($db->{$key}) =~ /hash/i) {
    0          
624 0         0 print "{ .. }\n";
625             }
626             elsif (ref($db->{$key}) =~ /array/i) {
627 0         0 print "[ .. ]\n";
628             }
629             else {
630 0         0 print "\"$db->{$key}\"\n";
631             }
632             }
633             }
634              
635             sub enter {
636 2     2 1 3 my ($self, $key) = @_;
637              
638 2 50       6 return $self->_failkey() if(! defined $key);
639              
640 2 50       3 if ($key eq '..') {
641 0         0 $self->up;
642             }
643             else {
644 2 50 33     12 if (ref($self->{db}) =~ /array/i) {
    50          
645             # "cd" into array element
646 0 0       0 return $self->_failidx if($key !~ /^\d*$/);
647 0         0 push @{$self->{path}}, "[${key}]";
  0         0  
648 0         0 push @{$self->{prev}}, $self->{db};
  0         0  
649 0         0 $self->{db} = $self->{db}->[$key];
650             }
651             elsif (ref($self->{db}->{$key}) =~ /hash/i || ref($self->{db}->{$key}) =~ /array/i) {
652             # "cd" into the hash pointed at by $key
653 2         2 push @{$self->{prev}}, $self->{db};
  2         3  
654 2         2 push @{$self->{path}}, $key;
  2         3  
655 2         2 $self->{db} = $self->{db}->{$key};
656 2         242 print "=> $key\n";
657             }
658             else {
659 0         0 return $self->_fail("not a hash: \"$key\"\n");
660             }
661             }
662              
663 2         7 return 1;
664             }
665              
666             sub up {
667 0     0 0 0 my $self = shift;
668 0 0       0 if (@{$self->{prev}}) {
  0         0  
669 0         0 $self->{db} = pop @{$self->{prev}};
  0         0  
670 0         0 pop @{$self->{path}};
  0         0  
671 0         0 print "<=\n";
672             }
673             else {
674 0         0 return $self->_fail("already on top level\n");
675             }
676              
677 0         0 return 1;
678             }
679              
680             sub search {
681 0     0 1 0 my ($self, $regex) = @_;
682              
683 0 0       0 if (! defined $regex) {
684 0         0 $self->_fail(" parameter missing\n");
685             }
686              
687 0         0 $self->{spath} = [];
688              
689 0         0 return $self->_search($self->{db}, $regex);
690             }
691              
692             sub _search {
693 0     0   0 my($self, $db, $regex) = @_;
694              
695 0 0       0 if (ref($db) =~ /hash/i) {
    0          
696 0         0 foreach my $key (sort keys %{$db}) {
  0         0  
697 0         0 $self->_searchmatch($key, $regex);
698 0         0 push @{$self->{spath}}, $key;
  0         0  
699 0         0 $self->_search($db->{$key}, $regex);
700 0         0 pop @{$self->{spath}};
  0         0  
701             }
702             }
703             elsif (ref($db) =~ /array/i) {
704 0         0 my $pos = 0;
705 0         0 foreach my $item (@{$db}) {
  0         0  
706 0         0 push @{$self->{spath}}, "[${pos}]";
  0         0  
707 0         0 $self->_search($item, $regex);
708 0         0 pop @{$self->{spath}};
  0         0  
709 0         0 $pos++;
710             }
711             }
712             else {
713 0         0 $self->_searchmatch($db, $regex);
714             }
715              
716 0         0 return 1;
717             }
718              
719             sub _searchmatch {
720 0     0   0 my ($self, $key, $regex) = @_;
721 0 0       0 if ($key =~ /$regex/) {
722 0         0 print join(' => ', @{$self->{spath}}) . ": $key\n";
  0         0  
723             }
724             }
725              
726             sub done {
727 7     7 0 9 my $self = shift;
728 7 50       18 if (! $self->{silent}) {
729 0           print "ok\n";
730             }
731             }
732              
733             sub help {
734 0     0 1   my $self = shift;
735 0           print qq(Display commands:
736             list - list keys of current level
737             show - same as list but with values
738             dump - dump everything from current level
739             get | /regex/ - display value of , or the value
740             of all keys matching /regex/
741             search - search for
742              
743             Navigation commands:
744             enter - change level into sub-hash of
745              
746             Edit commands:
747             set - set to
748             edit - edit structure behind [1]
749             append [] - append to array , leave
750             if you are currently inside an array
751             drop - delete key , use a number if inside
752             an array
753             pop [] - remove last element of array ,
754             shift [] - remove first element of array
755             leave if inside an array
756             );
757              
758 0 0         if ($self->{transactions}) {
759 0           print qq(
760             Transaction commands:
761             begin - start a transaction session
762             commit - store everything changed within session
763             rollback - discard changes
764             );
765             }
766              
767 0           print qq(
768             Misc commands:
769             help - get help
770             ctrl-d | quit - exit
771              
772             Shortcuts:
773             .. - go one level up
774             l - list
775             d - dump
776             sh - show
777             cd - enter
778             - enter [2]
779             / - search
780              
781             Hints:
782             [1] can be perl code, e.g: set pw { user => 'max' }
783             [2] doesn't work if correlates to a command
784             );
785             }
786              
787              
788             1;
789              
790             =head1 NAME
791              
792             Data::Interactive::Inspect - Inspect and manipulate perl data structures interactively
793              
794             =head1 SYNOPSIS
795              
796             use Data::Interactive::Inspect;
797             my $data = foo(); # get a hash ref from somewhere
798              
799             # new shell object, the simple way
800             my $shell = Data::Interactive::Inspect->new($data);
801              
802             # or
803             my $shell = Data::Interactive::Inspect->new(
804             struct => $data,
805             name => 'verkehrswege',
806             begin => sub { .. },
807             commit => sub { .. },
808             rollback => sub { .. },
809             serialize => sub { .. },
810             deserialize => sub { .. },
811             editor => 'emacs',
812             more => 'less'
813             );
814              
815             $data = $shell->inspect(); # opens a shell and returns modified hash ref on quit
816              
817              
818             =head1 DESCRIPTION
819              
820             This module provides an interactive shell which can be used to inspect and modify
821             a perl data structure.
822              
823             You can browse the structure like a directory, display the contents, add and remove
824             items, whatever you like. It is possible to include complete perl data structures.
825              
826             The module works with hash and array references.
827              
828             =head1 METHODS
829              
830             =head2 new
831              
832             The B function takes either one parameter (a reference to a hash or array)
833             or a hash reference with parameters. The following parameters are supported:
834              
835             =over
836              
837             =item B
838              
839             The hash or array reference to inspect.
840              
841             =item B
842              
843             Will be displayed on the prompt of the shell.
844              
845             =item B
846              
847             By default L opens B if the user issues the B
848             command. Use this parameter to instruct it otherwise.
849              
850             =item B
851              
852             By default L uses B to display data which doesn't
853             fit the terminal window. Use this parameter to instruct it otherwise.
854              
855             =item B, B, B
856              
857             If your data is tied to some backend which supports transactions, you can provide
858             functions to implement this. If all three are defined, the user can use transaction
859             commands in the shell.
860              
861             Look at L for an example implementation.
862              
863             =item B, B
864              
865             By default L uses L for serialization, which
866             is used in the B and B commands. You can change this by assigning
867             code refs to these parameters.
868              
869             B will be called with the structure to be serialized as its sole
870             parameter and is expected to return a string.
871              
872             B will be called with a string as parameter and is expected to
873             return a structure.
874              
875             =back
876              
877             =head2 inspect
878              
879             The B method starts the shell. Ii does return if the user leaves it, otherwise
880             it runs forever.
881              
882             The shell runs on a terminal and with STDIN.
883              
884             The interactive shell supports command line editing, history and completion (for
885             commands and hash keys), if L or L is
886             installed.
887              
888             =head1 INTERACTIVE COMMANDS
889              
890             =head2 DISPLAY COMMANDS
891              
892             =over
893              
894             =item B
895              
896             Lists the keys of the current level of the structure.
897              
898             Shortcut: B.
899              
900             =item B
901              
902             Does nearly the same as B but also shows the content of the
903             keys. If a key points to a structure (like a hash or an array), B
904             whill not display anything of it, but instead indicate, that there'e
905             more behind that key.
906              
907             For arrays the array indices are displayed as well.
908              
909             Shortcut: B.
910              
911             =item B
912              
913             Dumps out everything of the current level of the structure.
914              
915             Shortcut: B.
916              
917             =item B key | /regex/>
918              
919             Displays the value of B. If you specify a regex, the values of
920             all matching keys will be shown.
921              
922             If the current structure is an array you can specify the array index
923             as the parameter.
924              
925             =item B regex | /
926              
927             Search for B through the current structure. Looks for
928             keys an values.
929              
930             Beware that this make take some time depending on the size
931             of the structure.
932              
933             =back
934              
935             =head2 NAVIGATION COMMANDS
936              
937             =over
938              
939             =item B key
940              
941             You can use this command to enter a sub hash of the current hash.
942             It works like browsing a directory structure. You can only enter
943             keys which point to sub hashes.
944              
945             Shortcuts: B
946              
947             If the key you want to enter doesn't collide with a command, then
948             you can also just directly enter the key without 'enter' or 'cd' in
949             front of it, eg:
950              
951             my.db> list
952             subhash
953             my.db> subhash
954             my.db subhash> dump
955             my.db subhash> ..
956             my.db>^D
957              
958             If the current structure is an array you can use the array index
959             to enter a specific array item.
960              
961             If you specify B<..> as parameter (or as its own command like in the
962             example below), you go one level up and leave the current sub hash.
963              
964             =back
965              
966             =head2 EDIT COMMANDS
967              
968             =over
969              
970             =item B key value
971              
972             Use the B command to add a new key or to modify the value
973             of a key. B may be a valid perl structure, which you can
974             use to create sub hashes or arrays. Example:
975              
976             my.db> set users [ { name => 'max'}, { name => 'joe' } ]
977             ok
978             mydb> get users
979             users =>
980             {
981             'name' => 'max'
982             },
983             {
984             'name' => 'joe'
985             }
986              
987             B command overwrites existing values
988             without asking>.
989              
990             =item B key
991              
992             You can edit a whole structure pointed at by B with the
993             B command. It opens an editor with the structure converted
994             to L. Modify whatever you wish, save, and the structure will
995             be saved to the database.
996              
997             =item B key value
998              
999             This command can be used to append a value to an array. As with the
1000             B command, B can be any valid perl structure.
1001              
1002             If you are currently inside an array, leave the B parameter.
1003              
1004             =item B key
1005              
1006             Delete a key.
1007              
1008             Again, note that all commands are executed without further asking
1009             or warning!
1010              
1011             If you are currently inside an array, the B parameter must be
1012             an array index.
1013              
1014             =item B key
1015              
1016             Remove the last element of the array pointed at by B.
1017              
1018             If you are currently inside an array, leave the B parameter.
1019              
1020             =item B key
1021              
1022             Remove the first element of the array pointed at by B.
1023              
1024             If you are currently inside an array, leave the B parameter.
1025              
1026             =back
1027              
1028             =head2 TRANSACTION COMMANDS
1029              
1030             B.
1031              
1032             =over
1033              
1034             =item B
1035              
1036             Start a transaction.
1037              
1038             =item B
1039              
1040             Save all changes made since the transaction began.
1041              
1042             =item B
1043              
1044             Discard all changes of the transaction.
1045              
1046             =back
1047              
1048             =head2 MISC COMMANDS
1049              
1050             =over
1051              
1052             =item B
1053              
1054             Display a short command help.
1055              
1056             Shortcuts: B or B.
1057              
1058             =item B
1059              
1060             Quit the interactive shell
1061              
1062             Shortcuts: B.
1063              
1064             =back
1065              
1066             =head1 LIMITATIONS
1067              
1068             The data structure you are inspecting with L may
1069             contain code refs. That's not a problem as long as you don't touch them.
1070              
1071             Sample:
1072              
1073             my $c = {
1074             opt => 'value',
1075             hook => sub { return 1; },
1076             };
1077             my $shell = Data::Interactive::Inspect->new($c);
1078             $shell->inspect();
1079              
1080             Execute:
1081              
1082             data@0x80140a468> dump
1083             ---
1084             hook: !!perl/code '{ "DUMMY" }'
1085             opt: value
1086             data@0x80140a468> set hook blah
1087             data@0x80140a468> edit hook
1088              
1089             Both commands would destroy the code ref. The first one would just overwrite it
1090             while the other one would remove the code (in fact it remains a code ref but
1091             it will contain dummy code only).
1092              
1093             =head1 TODO
1094              
1095             =over
1096              
1097             =item Add some kind of select command
1098              
1099             Example:
1100              
1101             struct:
1102              
1103             {
1104             users => [
1105             { login => 'max', uid => 1 },
1106             { login => 'leo', uid => 2 },
1107             ]
1108             }
1109              
1110             > select login from users where uid = 1
1111              
1112             which should return 'max'.
1113              
1114             (may require a real world parser)
1115              
1116             =item Add some kind of schema support
1117              
1118             Given the same structure as above:
1119              
1120             > update users set uid = 4 where login = 'max'
1121              
1122             =back
1123              
1124             =head1 AUTHOR
1125              
1126             T.v.Dein
1127              
1128             =head1 BUGS
1129              
1130             Report bugs to
1131             http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data::Interactive::Inspect
1132              
1133             =head1 COPYRIGHT
1134              
1135             Copyright (c) 2015 by T.v.Dein .
1136             All rights reserved.
1137              
1138             =head1 LICENSE
1139              
1140             This program is free software; you can redistribute it
1141             and/or modify it under the same terms as Perl itself.
1142              
1143             =head1 VERSION
1144              
1145             This is the manual page for L Version 0.04.
1146              
1147             =cut