File Coverage

lib/Synapse/CLI/Config/Object.pm
Criterion Covered Total %
statement 176 198 88.8
branch 18 32 56.2
condition 14 27 51.8
subroutine 31 35 88.5
pod 22 23 95.6
total 261 315 82.8


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Synapse::CLI::Config::Object - base class for your configuration objects
4              
5              
6             =head1 SYNOPSIS
7              
8              
9             =head2 Step 1. Write one or more config objects:
10              
11             package My::Config::User;
12             use base qw /Synapse::CLI::Config::User/;
13             use Synapse::CLI::Config;
14             use strict;
15             use warnings;
16            
17             # optional code goes here
18              
19             1;
20              
21             __END__
22              
23              
24             =head2 Step 2. Write your application CLI accessor:
25              
26             #!/usr/bin/perl
27             # this is myapp-cli. It should be installed in /usr/local/bin/myapp-cli
28             use Synapse::CLI::Config;
29             use YAML::XS;
30             use warning;
31             use strict;
32             $Synapse::CLI::Config::BASE_DIR = "/etc/myapp";
33             $Synapse::CLI::Config::ALIAS->{type} = 'Synapse::CLI::Config::Type';
34             $Synapse::CLI::Config::ALIAS->{user} = 'MyAPP::User';
35             print Dump (Synapse::CLI::Config::execute (@ARGV));
36              
37             =head2 Step 3. Have fun on the CLI
38              
39             myapp-cli type user create c-hiver "Clementine Hiver"
40             myapp-cli user chiver set password moncontenkite
41             myapp-cli user chiver set-add permission watchtv
42             myapp-cli user chiver set-add permission playoncomputer
43             myapp-cli user chiver set-add permission readbooks
44             myapp-cli user chiver show
45              
46             =cut
47             package Synapse::CLI::Config::Object;
48 3     3   515724 use Synapse::CLI::Config;
  3         9  
  3         282  
49 3     3   3494 use Time::HiRes;
  3         15989  
  3         16  
50 3     3   4036 use File::Touch;
  3         184506  
  3         862  
51 3     3   6831 use File::Copy;
  3         13988  
  3         342  
52 3     3   27 use YAML::XS;
  3         6  
  3         154  
53 3     3   16 use IO::File;
  3         6  
  3         681  
54 3     3   20 use strict;
  3         5  
  3         106  
55 3     3   17 use warnings;
  3         6  
  3         12324  
56              
57              
58             =head1 Class methods
59              
60             Usage: cli type object-class object-method arg1 ... argN
61              
62             =head2 $class->create ($name, $label);
63              
64             Usage: cli type mytype create foo "This is a beautiful Foo"
65              
66             Creates an object of type $class of name $name. $name will be the object file
67             name on disk. Allowed names are /^[a-z0-9-_]+$/. If no 'name' is supplied,
68             $class->create() provides one.
69              
70             Optionally, sets label to "This is a beautiful Foo", which is shorter than
71             doing:
72              
73             cli type mytype create foo
74             cli mytype foo set label "This is a beautiful Foo"
75              
76             =cut
77             sub create {
78 1     1 1 10 my $class = shift;
79 1         3 my $objid = shift;
80            
81 1 50       5 defined $objid or do {
82 0         0 my $time = Time::HiRes::time();
83 0         0 $time =~ s/\./-/g;
84 0         0 $objid = $time;
85             };
86            
87 1 50       5 $objid =~ /^[a-z0-9-_]+$/i or do {
88 0         0 Synapse::CLI::Config::debug ("invalid object name $objid");
89 0         0 return;
90             };
91            
92 1         4 my $config_dir = $class->__confdir__();
93 1 50       21 -d $config_dir or mkdir $config_dir;
94 1 50       11 -d $config_dir or die "$config_dir does not exist and cannot be created";
95 1         2 File::Touch::touch ( do { my $o = bless { name => $objid }, $class; $o->__filepath__() } );
  1         4  
  1         3  
96            
97 1         240 my $self = $class->new ($objid);
98 1 50       5 @_ and do {
99 1         6 $self->set (label => join ' ', @_);
100 1         9 return $self->__save__ ('set', 'label', @_);
101             };
102 0         0 return $self;
103             }
104              
105              
106             =head2 $class->new($name);
107              
108             This is one of the few methods which is NOT to be used on the CLI (although you
109             could since "cli myclass myid show" is logically equivalent to the less
110             intuitive "cli type myclass new myid").
111              
112             Builds and returns object of type $class with name $name.
113              
114             =cut
115             sub new {
116 8     8 1 16 my $class = shift;
117 8         11 my $name = shift;
118 8         29 my $self = bless { @_ }, $class;
119 8         26 $self->{name} = $name;
120 8         21 return $self->__init__();
121             }
122              
123              
124             =head2 $class->list();
125              
126             Usage: cli type myclass list
127              
128             Returns a list of object ids for this class.
129              
130             =cut
131             sub list {
132 3     3 1 4 my $class = shift;
133 3         10 my $dir = $class->__confdir__();
134 3         97 opendir (DIR, $dir);
135 3         89 my @files = readdir (DIR);
136 3         34 closedir (DIR);
137 3         7 my @res = ();
138 3         6 for my $f (@files) {
139 8 100       29 $f =~ /^\.$/ and next;
140 5 100       20 $f =~ /^\..$/ and next;
141 2 50       77 -d "$dir/$f" and next;
142 2 50       11 $f =~ s/\.conf$// or next;
143 2         5 push @res, $f;
144             }
145 3 100       19 return wantarray ? @res : \@res;
146             }
147              
148              
149             =head2 $class->list_l();
150              
151             Usage: cli type myclass list
152              
153             Returns a list of object ids for this class, as well as the corresponding
154             labels, separated with a CSV-style semicolumn.
155              
156             =cut
157             sub list_l {
158 0     0 1 0 my $class = shift;
159 0         0 my @res = map { "$_; " . $class->new ($_)->label() } $class->list (@_);
  0         0  
160 0 0       0 return wantarray ? @res : \@res;
161             }
162              
163              
164             =head2 $class->count();
165              
166             usage: cli type myclass count
167              
168             counts how many objects of myclass exist.
169              
170             =cut
171             sub count {
172 1     1 1 2 my $class = shift;
173 1         4 my @list = $class->list();
174 1         5 return 0 + @list;
175             }
176              
177              
178             =head1 Instance methods
179              
180             Usage: cli object-class object-id object-method arg1 ... argN
181              
182              
183             =head2 $self->set($attr, $stuff);
184              
185             Usage: cli mytype myobject set foo "this is bar"
186              
187             Sets $self->{foo} to "this is bar"
188              
189             =cut
190             sub set {
191 9     9 1 16 my $self = shift;
192 9         17 my $attr = shift;
193 9         26 my $stuff = join ' ', @_;
194 9         39 $self->{$attr} = $stuff;
195 9         82 return $self;
196             }
197              
198              
199             =head2 $self->del($attr);
200              
201             Usage: cli mytype myobject del foo
202              
203             Removes $self->{foo}
204              
205             =cut
206             sub del {
207 1     1 1 2 my $self = shift;
208 1         4 my $attr = shift;
209 1         3 delete $self->{$attr};
210 1         3 return $self;
211             }
212              
213              
214             =head2 $self->list_push($attr, $stuff);
215              
216             Usage: cli mytype myobject list-push foo "this is bar"
217              
218             Makes $self->{foo} an empty list if not defined. Adds "this is bar" to the list.
219              
220             =cut
221             sub list_push {
222 3     3 1 6 my $self = shift;
223 3         6 my $attr = shift;
224 3         8 my $stuff = join ' ', @_;
225 3   100     19 $self->{$attr} ||= [];
226 3         4 push @{$self->{$attr}}, $stuff;
  3         7  
227 3         8 return $self;
228             }
229              
230              
231             =head2 $self->list_pop($attr)
232              
233             Usage: cli mytype myobject list-pop foo
234              
235             Makes $self->{foo} an empty list if not defined. Then pops the list.
236              
237             =cut
238             sub list_pop {
239 1     1 1 3 my $self = shift;
240 1         3 my $attr = shift;
241 1   50     122 $self->{$attr} ||= [];
242 1         3 pop @{$self->{$attr}};
  1         11  
243 1         4 return $self;
244             }
245              
246              
247             =head2 $self->list_shift($attr)
248              
249             Usage: cli mytype myobject list-shift foo
250              
251             Makes $self->{foo} an empty list if not defined. Then shifts the list.
252              
253             =cut
254             sub list_shift {
255 1     1 1 2 my $self = shift;
256 1         3 my $attr = shift;
257 1         3 my $stuff = join ' ', @_;
258 1   50     6 $self->{$attr} ||= [];
259 1         2 shift @{$self->{$attr}};
  1         3  
260 1         3 return $self;
261             }
262              
263              
264             =head2 $self->list_unshift($attr, $stuff)
265              
266             Usage: cli mytype myobject list-unshift foo "this is bar"
267              
268             Makes $self->{foo} an empty list if not defined. Then unshifts the list with
269             "this is bar".
270              
271             =cut
272             sub list_unshift {
273 1     1 1 2 my $self = shift;
274 1         3 my $attr = shift;
275 1         4 my $stuff = join ' ', @_;
276 1   50     6 $self->{$attr} ||= [];
277 1         3 unshift @{$self->{$attr}}, $stuff;
  1         4  
278 1         2 return $self;
279             }
280              
281              
282             =head2 $self->list_del($attr, $index)
283              
284             Usage: cli mytype myobject list-del foo
285              
286             Removes $self->{$attr}->{$index} from the list.
287              
288             =cut
289             sub list_del {
290 1     1 1 3 my $self = shift;
291 1         3 my $attr = shift;
292 1         2 my $index = shift;
293 1   50     6 $self->{$attr} ||= [];
294 1         3 splice @{$self->{$attr}}, $index, 1;
  1         7  
295             }
296              
297              
298             =head2 $self->list_add($attr, $index, $stuff)
299              
300             Usage: cli mytype myobject list-add foo "this is stuff"
301              
302             Adds "this is stuff" at position $index in $self->{$attr} list.
303              
304             =cut
305             sub list_add {
306 1     1 1 4 my $self = shift;
307 1         2 my $attr = shift;
308 1         3 my $index = shift;
309 1         4 my $stuff = join ' ', @_;
310 1   50     8 $self->{$attr} ||= [];
311 1         3 splice @{$self->{$attr}}, $index, 0, $stuff;
  1         6  
312             }
313              
314              
315             =head2 $self->set_add($attr, $stuff)
316              
317             Usage: cli mytype myobject set-add foo "this is stuff"
318              
319             Treats $self->{$attr} as a set (creating it if needed, using a hash ref), and
320             adds "this is stuff" to the set.
321              
322             =cut
323             sub set_add {
324 5     5 1 8 my $self = shift;
325 5         9 my $attr = shift;
326 5         14 my $stuff = join ' ', @_;
327 5   100     20 $self->{$attr} ||= {};
328 5         12 $self->{$attr}->{$stuff} = 1;
329 5         10 return $self;
330             }
331              
332              
333             =head2 $self->set_del($attr, $stuff)
334              
335             Usage: cli mytype myobject set-del foo "this is stuff"
336              
337             Removes "this is stuff" from the set.
338              
339             =cut
340             sub set_del {
341 0     0 1 0 my $self = shift;
342 0         0 my $attr = shift;
343 0         0 my $stuff = join ' ', @_;
344 0   0     0 $self->{$attr} ||= {};
345 0         0 delete $self->{$attr}->{$stuff};
346 0         0 return $self;
347             }
348              
349              
350             =head2 $self->set_list($attr)
351              
352             Usage: cli mytype myobject set-list foo
353              
354             Lists all items in the set.
355              
356             =cut
357             sub set_list {
358 1     1 1 2 my $self = shift;
359 1         3 my $attr = shift;
360 1   50     5 $self->{$attr} ||= {};
361             return wantarray ?
362 0         0 sort keys %{$self->{$attr}} :
  1         10  
363 1 50       4 [ sort keys %{$self->{$attr}} ];
364             }
365              
366              
367             =head2 $self->label()
368              
369             Usage: cli mytype myobject label
370              
371             Each object as an optional label associated with it, this method returns it.
372             Returns name() is label is not defined.
373              
374             =cut
375             sub label {
376 4     4 1 10 my $self = shift;
377 4   33     24 return $self->{label} || $self->name();
378             }
379              
380              
381             =head2 $self->name()
382              
383             Usage: cli mytype myobject name
384              
385             Each object as a name. Returns it.
386              
387             =cut
388             sub name {
389 20     20 1 39 my $self = shift;
390 20   33     376 return $self->{name} || $self->name();
391             }
392              
393              
394             =head2 $self->show();
395              
396             Usage: cli mytype myobject show
397              
398             From Perl, this method is not very interesting since it just returns $self. But
399             on the CLI, will display a YAML representation of $self, which is handy to view
400             your objects.
401              
402             =cut
403 0     0 1 0 sub show { return shift }
404              
405              
406             =head2 $self->rename_to ($newname);
407              
408             Usage: cli mytype foo rename-to bar
409              
410             Changes the object 'name' attribute as well as file name on disk.
411              
412             =cut
413 0     0 0 0 sub rename_to_FORCE_NOSAVE { 1 }
414             sub rename_to {
415 1     1 1 3 my $self = shift;
416 1         2 my $newid = shift;
417 1         4 my $path1 = $self->__filepath__();
418 1         3 my $path2 = $path1;
419 1         5 my @path2 = split /\//, $path2;
420 1         2 pop (@path2);
421 1         3 push @path2, "$newid.conf";
422 1         3 $path2 = join '/', @path2;
423 1         6 File::Copy::move ($path1, $path2);
424 1         123 $self->{name} = $newid;
425 1         5 return $self;
426             }
427              
428              
429             =head2 $self->copy_as ($newname);
430              
431             Usage: cli mytype foo copy-as bar
432              
433             Copies the object and names the copy $newname. Returns the newly copied object.
434              
435             =cut
436             sub copy_as {
437 1     1 1 3 my $self = shift;
438 1         3 my $newid = shift;
439 1         4 my $path1 = $self->__filepath__();
440 1         3 my $path2 = $path1;
441 1         6 my @path2 = split /\//, $path2;
442 1         3 pop (@path2);
443 1         3 push @path2, "$newid.conf";
444 1         4 $path2 = join '/', @path2;
445 1         9 File::Copy::copy ($path1, $path2);
446 1         529 my $class = ref $self;
447 1         9 return $class->new ($newid);
448             }
449              
450              
451             =head2 $self->remove();
452              
453             Usage: cli mytype foo remove
454              
455             Removes foo.
456              
457             =cut
458             sub remove {
459 2     2 1 5 my $self = shift;
460 2         3 my $new = shift;
461 2         9 unlink $self->__filepath__();
462             }
463              
464              
465             ## PRIVATE METHODS : if you made it this far and want to override them anyways,
466             ## then you probably should
467              
468              
469             sub __init__ {
470 8     8   9 my $self = shift;
471 8         21 my $path = $self->__filepath__();
472 8 50       176 -e $path or return;
473            
474 8         44 my $fh = IO::File->new();
475 8 50       294 if ($fh->open("<$path")) {
476 8         14165 while (my $line = <$fh>) {
477 7         20 chomp($line);
478 7         51 my ($timestamp, $method, @arguments) = split /\s+/, $line;
479 7 50       47 $self->can ($method) or do {
480 0         0 Synapse::CLI::Config::debug ("cannot invoke method $method on object $self - ignoring");
481             };
482 7         24 $self->$method (@arguments);
483             }
484 8         14 undef $fh;
485             }
486             else {
487 0         0 Synapse::CLI::Config::debug ("cannot instantiate : file is not readable");
488 0         0 return;
489             };
490 8         470 return $self;
491             }
492              
493              
494             sub __confdir__ {
495 18     18   25 my $self = shift;
496 18   66     57 my $class = ref $self || $self;
497 18         136 $class =~ s/::/-/g;
498 18         50 return Synapse::CLI::Config::base_dir() . "/$class";
499             }
500              
501              
502             sub __filepath__ {
503 14     14   17 my $self = shift;
504 14         105 return $self->__confdir__() . '/' . $self->name() . '.conf';
505             }
506              
507              
508             sub __save__ {
509 1     1   2 my $self = shift;
510 1         4 my $objid = $self->name();
511 1         2 my $method = shift;
512 1         4 my $args = join ' ', @_;
513 1         6 my $time = Time::HiRes::time();
514 1         3 my $path = $self->__filepath__();
515 1 50       38 open OBJECT, ">>$path" or die "cannot write-open $path";
516 1         23 print OBJECT "$time $method $args\n";
517 1         51 close OBJECT;
518 1         5 return $self;
519             }
520              
521              
522             1;
523              
524              
525             __END__