File Coverage

blib/lib/Config/Model/TreeSearcher.pm
Criterion Covered Total %
statement 77 81 95.0
branch 16 24 66.6
condition 15 18 83.3
subroutine 14 14 100.0
pod 1 1 100.0
total 123 138 89.1


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::TreeSearcher 2.153; # TRIAL
11              
12 59     59   511 use Mouse;
  59         175  
  59         492  
13 59     59   29082 use Mouse::Util::TypeConstraints;
  59         199  
  59         560  
14              
15 59     59   7624 use List::MoreUtils qw/any/;
  59         182  
  59         801  
16 59     59   45894 use Log::Log4perl qw(get_logger :levels);
  59         189  
  59         449  
17 59     59   8025 use Config::Model::Exception;
  59         214  
  59         1872  
18 59     59   418 use Config::Model::ObjTreeScanner;
  59         145  
  59         1807  
19 59     59   384 use Carp;
  59         157  
  59         5844  
20              
21             my @search_types = qw/element value key summary description help/;
22             enum( 'SearchType' => [ @search_types, 'all' ] );
23              
24             # clean up namespace to avoid clash between MUTC keywords and
25             # my functions
26             # See http://www.nntp.perl.org/group/perl.moose/2010/10/msg1935.html
27 59     59   542 no Mouse::Util::TypeConstraints;
  59         237  
  59         568  
28              
29             has 'node' => (
30             is => 'ro',
31             isa => 'Config::Model::Node',
32             weak_ref => 1,
33             required => 1
34             );
35              
36             has 'type' => ( is => 'ro', isa => 'SearchType' );
37              
38             has '_type_hash' => (
39             is => 'rw',
40             isa => 'HashRef[Bool]',
41             builder => '_build_type_hash',
42             lazy => 1,
43             );
44              
45             my $logger = get_logger("TreeSearcher");
46              
47             sub _build_type_hash {
48 9     9   16 my $self = shift;
49 9         25 my $t = $self->type;
50 9 50       26 my $def = $t eq 'all' ? 1 : 0;
51 9         26 my %res = map { $_ => $def; } @search_types;
  54         120  
52 9 50       29 $res{$t} = 1 unless $t eq 'all';
53 9         26 return \%res;
54             }
55              
56             sub search {
57 9     9 1 175 my $self = shift;
58 9         18 my $string = shift; # string to search, can be a regexp
59              
60 9         38 $logger->trace( "TreeSearcher: creating scanner for " . $self->node->name );
61 9         181 my $reg = qr/$string/i;
62              
63 9         18 my @scanner_args;
64 9         28 my $need_search = $self->_build_type_hash;
65              
66             push @scanner_args, leaf_cb => sub {
67 549     549   1120 my ( $scanner, $data_ref, $node, $element_name, $index, $leaf_object ) = @_;
68              
69 549         1571 my $loc = $leaf_object->location;
70 549         2004 $logger->debug("TreeSearcher: scanning leaf $loc");
71              
72 549         5018 my $v = $leaf_object->fetch( check => 'no' );
73 549 100 100     2373 if ( $need_search->{value} and defined $v and $v =~ $reg ) {
      100        
74 10         38 $data_ref->($loc);
75             }
76 549 50       2210 if ( $need_search->{help} ) {
77 0         0 my $help_ref = $leaf_object->get_help;
78             $data_ref->($loc)
79 0 0       0 if any { $_ =~ $reg; } values %$help_ref;
  0         0  
80             }
81 9         63 };
82              
83             push @scanner_args, hash_element_cb => sub {
84 54     54   134 my ( $scanner, $data_ref, $node, $element_name, @keys ) = @_;
85 54         152 my $loc = $node->location;
86 54 100       124 $loc .= ' ' if $loc;
87 54         95 $loc .= $element_name;
88              
89 54         215 $logger->debug("TreeSearcher: scanning hash $loc");
90              
91 54         491 foreach my $k (@keys) {
92 45 100 100     144 if ( $need_search->{key} and $k =~ $reg ) {
93 1         4 my $hloc = $node->fetch_element($element_name)->fetch_with_id($k)->location;
94 1         4 $data_ref->($hloc);
95             }
96 45         134 $scanner->scan_hash( $data_ref, $node, $element_name, $k );
97             }
98 9         39 };
99              
100             push @scanner_args, node_content_cb => sub {
101 126     126   358 my ( $scanner, $data_ref, $node, @element ) = @_;
102 126         402 my $loc = $node->location;
103 126         455 $logger->debug("TreeSearcher: scanning node $loc");
104              
105 126         963 foreach my $e (@element) {
106 693         1129 my $store = 0;
107              
108 693         1227 for ( qw/description summary/ ) {
109 1386 100 100     3483 if ($need_search->{$_} and $node->get_help_as_text( $_ => $e ) =~ $reg) {
110 4         13 $store = 1;
111             }
112             }
113 693 50 33     1572 if ($need_search->{element} and $e =~ $reg) {
114 0         0 $store = 1;
115             }
116              
117 693 50       1319 $data_ref->( $loc ? $loc . ' ' . $e : $e ) if $store;
    100          
118              
119 693         2101 $scanner->scan_element( $data_ref, $node, $e );
120             }
121 9         32 };
122              
123 9         53 my $scan = Config::Model::ObjTreeScanner->new( @scanner_args, );
124              
125             # use hash to avoid duplication of path
126 9         34 my @loc;
127             my $store_sub = sub {
128 15     15   28 my $p = shift;
129 15 50 66     68 return if @loc and $loc[$#loc] eq $p;
130 15         71 $logger->trace("TreeSearcher: storing location '$p'");
131 15         132 push @loc, $p;
132 9         35 };
133 9         55 $scan->scan_node( $store_sub, $self->node );
134              
135 9         310 return @loc;
136             }
137              
138             __PACKAGE__->meta->make_immutable;
139              
140             1;
141              
142             # ABSTRACT: Search tree for match in value, description...
143              
144             __END__
145              
146             =pod
147              
148             =encoding UTF-8
149              
150             =head1 NAME
151              
152             Config::Model::TreeSearcher - Search tree for match in value, description...
153              
154             =head1 VERSION
155              
156             version 2.153
157              
158             =head1 SYNOPSIS
159              
160             use Config::Model ;
161              
162             # define configuration tree object
163             my $model = Config::Model->new ;
164             $model ->create_config_class (
165             name => "MyClass",
166             element => [
167             [qw/foo bar/] => {
168             type => 'leaf',
169             value_type => 'string'
170             },
171             baz => {
172             type => 'hash',
173             index_type => 'string' ,
174             cargo => {
175             type => 'leaf',
176             value_type => 'string',
177             },
178             },
179            
180             ],
181             ) ;
182              
183             my $inst = $model->instance(root_class_name => 'MyClass' );
184              
185             my $root = $inst->config_root ;
186              
187             my $steps = 'baz:fr=bonjour baz:hr="dobar dan" foo="journalled"';
188             $root->load( steps => $steps ) ;
189              
190             my @result = $root->tree_searcher(type => 'value')->search('jour');
191             print join("\n",@result),"\n" ;
192             # print
193             # baz:fr
194             # foo
195              
196             =head1 DESCRIPTION
197              
198             This class provides a way to search the content of a configuration tree.
199             Given a keyword or a pattern, the search method scans the tree to find
200             a value, a description or anything that match the given pattern (or keyword).
201              
202             =head1 Constructor
203              
204             =head2 new (type => [ value | description ... ] )
205              
206             Creates a new searcher object. The C<type> parameter can be:
207              
208             =over
209              
210             =item element
211              
212             =item value
213              
214             =item key
215              
216             =item summary
217              
218             =item description
219              
220             =item help
221              
222             =item all
223              
224             Search in all the items above
225              
226             =back
227              
228             =head1 Methods
229              
230             =head2 search
231              
232             Parameters: C<< (keyword) >>
233              
234             Search the keyword or pattern in the tree. The search is done in a case
235             insensitive manner. Returns a list of path pointing
236             to the matching tree elements. See L<Config::Model::Role::Grab/grab> for details
237             on the path syntax.
238              
239             =head1 BUGS
240              
241             Creating a class with just one search method may be overkill. OTOH, it may
242             be extended later to provide iterative search.
243              
244             =head1 AUTHOR
245              
246             Dominique Dumont, (ddumont at cpan dot org)
247              
248             =head1 SEE ALSO
249              
250             L<Config::Model>,
251             L<Config::Model::SearchElement>,
252             L<Config::Model::AnyThing>
253              
254             =head1 AUTHOR
255              
256             Dominique Dumont
257              
258             =head1 COPYRIGHT AND LICENSE
259              
260             This software is Copyright (c) 2005-2022 by Dominique Dumont.
261              
262             This is free software, licensed under:
263              
264             The GNU Lesser General Public License, Version 2.1, February 1999
265              
266             =cut