File Coverage

blib/lib/Config/Model/Annotation.pm
Criterion Covered Total %
statement 82 82 100.0
branch 6 10 60.0
condition 1 3 33.3
subroutine 23 23 100.0
pod 2 8 25.0
total 114 126 90.4


line stmt bran cond sub pod time code
1             #
2             # This file is part of Config-Model
3             #
4             # This software is Copyright (c) 2005-2022 by Dominique Dumont.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             package Config::Model::Annotation 2.153; # TRIAL
11              
12 1     1   553 use Mouse;
  1         4  
  1         10  
13 1     1   993 use English;
  1         3698  
  1         7  
14 1     1   507 use Mouse::Util::TypeConstraints;
  1         2  
  1         9  
15              
16 1     1   100 use Path::Tiny;
  1         2  
  1         44  
17 1     1   6 use Data::Dumper;
  1         3  
  1         43  
18              
19 1     1   10 use Config::Model::TypeConstraints;
  1         2  
  1         21  
20 1     1   5 use Config::Model::Exception;
  1         2  
  1         32  
21 1     1   7 use Config::Model::Node;
  1         2  
  1         21  
22 1     1   7 use Config::Model::ObjTreeScanner;
  1         3  
  1         29  
23              
24 1     1   6 use strict ;
  1         2  
  1         28  
25 1     1   6 use warnings;
  1         2  
  1         30  
26              
27 1     1   5 use Carp qw/croak confess cluck/;
  1         2  
  1         816  
28              
29             #my $logger = get_logger("Annotation") ;
30              
31             has 'instance' => ( is => 'ro', isa => 'Config::Model::Instance', required => 1 );
32             has 'config_class_name' => ( is => 'ro', isa => 'Str', required => 1 );
33             has 'file' => ( is => 'ro', isa => 'Path::Tiny', lazy => 1, builder => '_set_file' );
34             has 'dir' => ( is => 'ro', isa => 'Path::Tiny', lazy => 1, builder => '_set_dir' );
35              
36             has 'root_dir' => (
37             is => 'ro',
38             isa => 'Config::Model::TypeContraints::Path',
39             coerce => 1
40             );
41              
42             sub _set_file {
43 2     2   777 my $self = shift;
44 2         14 return $self->dir->child( $self->config_class_name . '-note.pl');
45             }
46              
47             sub _set_dir {
48 2     2   1111 my $self = shift;
49             return
50 2 0       27 $self->root_dir ? $self->root_dir->child('config-model')
    50          
51             : $EUID ? path("/var/lib/config-model")
52             : path("~/.config-model");
53             }
54              
55             sub save {
56 1     1 1 881 my $self = shift;
57              
58 1         7 my $dir = $self->dir;
59 1         9 $dir->mkpath;
60 1         365 my $h = $self->get_annotation_hash;
61 1         10 $self->file->spew_utf8( Dumper($h) );
62             }
63              
64             sub get_annotation_hash {
65 4     4 0 730 my $self = shift;
66              
67 4         9 my %data;
68 4         52 my $scanner = Config::Model::ObjTreeScanner->new(
69             leaf_cb => \&my_leaf_cb,
70             hash_element_cb => \&my_hash_element_cb,
71             list_element_cb => \&my_list_element_cb,
72             node_element_cb => \&my_node_element_cb,
73             fallback => 'all',
74             );
75 4         33 my $root = $self->instance->config_root;
76              
77 4         22 $scanner->scan_node( \%data, $root );
78 4         78 return \%data;
79             }
80              
81             # WARNING: not a method
82             sub my_hash_element_cb {
83 24     24 0 57 my ( $scanner, $data_ref, $node, $element_name, @keys ) = @_;
84              
85             # custom code using $data_ref
86 24         61 store_note_in_data( $data_ref, $node->fetch_element($element_name) );
87              
88             # resume exploration
89 24         70 map { $scanner->scan_hash( $data_ref, $node, $element_name, $_ ) } @keys;
  18         57  
90             }
91              
92             # WARNING: not a method
93             sub my_node_element_cb {
94 54     54 0 129 my ( $scanner, $data_ref, $node, $element_name, $key, $contained_node ) = @_;
95              
96             # your custom code using $data_ref
97 54         133 store_note_in_data( $data_ref, $contained_node );
98              
99             # explore next node
100 54         174 $scanner->scan_node( $data_ref, $contained_node );
101             }
102              
103             # WARNING: not a method
104             sub my_list_element_cb {
105 12     12 0 36 my ( $scanner, $data_ref, $node, $element_name, @idx ) = @_;
106              
107             # custom code using $data_ref
108 12         36 store_note_in_data( $data_ref, $node->fetch_element($element_name) );
109              
110             # resume exploration (if needed)
111 12         32 map { $scanner->scan_list( $data_ref, $node, $element_name, $_ ) } @idx;
  29         80  
112              
113             # note: scan_list and scan_hash are equivalent
114             }
115              
116             # WARNING: not a method
117             sub my_leaf_cb {
118 271     271 0 559 my ( $scanner, $data_ref, $node, $element_name, $index, $leaf_object ) = @_;
119 271         458 store_note_in_data( $data_ref, $leaf_object );
120             }
121              
122             # WARNING: not a method
123             sub store_note_in_data {
124 361     361 0 571 my ( $data_ref, $obj ) = @_;
125              
126 361         863 my $note = $obj->annotation;
127 361 100       1304 return unless $note;
128              
129 22         78 my $key = $obj->location;
130 22         82 $data_ref->{$key} = $note;
131             }
132              
133             sub load {
134 2     2 1 51 my $self = shift;
135 2         13 my $f = $self->file;
136 2 50       128 return unless $f->exists;
137 2   33     98 my $hash = do "./$f" || croak "can't do $f:$!";
138 2         19 my $root = $self->instance->config_root;
139              
140 2         12 foreach my $path ( keys %$hash ) {
141 14         24 my $obj = eval { $root->grab( step => $path, autoadd => 0 ) };
  14         66  
142 14 100       128 next if $@; # skip annotation of unknown elements
143 8         28 $obj->annotation( $hash->{$path} );
144             }
145             }
146              
147 1     1   7 no Mouse;
  1         2  
  1         61  
148              
149             __PACKAGE__->meta->make_immutable;
150              
151             1;
152              
153             # ABSTRACT: Read and write configuration annotations
154              
155             __END__
156              
157             =pod
158              
159             =encoding UTF-8
160              
161             =head1 NAME
162              
163             Config::Model::Annotation - Read and write configuration annotations
164              
165             =head1 VERSION
166              
167             version 2.153
168              
169             =head1 SYNOPSIS
170              
171             use Config::Model ;
172              
173             # define configuration tree object
174             my $model = Config::Model->new ;
175             $model ->create_config_class (
176             name => "MyClass",
177             element => [
178             [qw/foo bar/] => {
179             type => 'leaf',
180             value_type => 'string'
181             },
182             baz => {
183             type => 'hash',
184             index_type => 'string' ,
185             cargo => {
186             type => 'leaf',
187             value_type => 'string',
188             },
189             },
190              
191             ],
192             ) ;
193              
194             my $inst = $model->instance(root_class_name => 'MyClass' );
195              
196             my $root = $inst->config_root ;
197              
198             # put some data in config tree the hard way
199             $root->fetch_element('foo')->store('yada') ;
200             $root->fetch_element('baz')->fetch_with_id('en')->store('hello') ;
201              
202             # put annotation the hard way
203             $root->fetch_element('foo')->annotation('english') ;
204             $root->fetch_element('baz')->fetch_with_id('en')->annotation('also english') ;
205              
206             # put more data the easy way
207             my $steps = 'baz:fr=bonjour#french baz:hr="dobar dan"#croatian';
208             $root->load( steps => $steps ) ;
209              
210             # dump resulting tree with annotations
211             print $root->dump_tree;
212              
213             # save annotations
214             my $annotate_saver = Config::Model::Annotation
215             -> new (
216             config_class_name => 'MyClass',
217             instance => $inst ,
218             root_dir => '/tmp/', # for test
219             ) ;
220             $annotate_saver->save ;
221              
222             # now check content of /tmp/config-model/MyClass-note.pl
223              
224             =head1 DESCRIPTION
225              
226             This module provides an object that read and write annotations (a bit
227             like comments) to and from a configuration tree and save them in a
228             file (not configuration file). This module can be used to save
229             annotation for configuration files that do not support comments.
230              
231             THis module should not be used for configuration files that support
232             comments.
233              
234             Depending on the effective id of the process, the annotation is
235             saved in:
236              
237             =over
238              
239             =item *
240              
241             C<< /var/lib/config-model/<model_name>-note.yml >> for root (EUID == 0)
242              
243             =item *
244              
245             C<< ~/.config-model/<model_name>-note.yml >> for normal user (EUID > 0)
246              
247             =back
248              
249             =head1 CONSTRUCTOR
250              
251             Quite standard. The constructor is passed a L<Config::Model::Instance>
252             object.
253              
254             =head1 METHODS
255              
256             =head2 save
257              
258             Save annotations in a file (See L<DESCRIPTION>)
259              
260             =head2 load
261              
262             Loads annotations from a file (See L<DESCRIPTION>)
263              
264             =head1 CAVEATS
265              
266             This module is currently not used.
267              
268             =head1 AUTHOR
269              
270             Dominique Dumont, (ddumont at cpan dot org)
271              
272             =head1 SEE ALSO
273              
274             L<Config::Model>,
275             L<Config::Model::Node>,
276             L<Config::Model::Loader>,
277             L<Config::Model::Searcher>,
278             L<Config::Model::Value>,
279              
280             =head1 AUTHOR
281              
282             Dominique Dumont
283              
284             =head1 COPYRIGHT AND LICENSE
285              
286             This software is Copyright (c) 2005-2022 by Dominique Dumont.
287              
288             This is free software, licensed under:
289              
290             The GNU Lesser General Public License, Version 2.1, February 1999
291              
292             =cut