File Coverage

blib/lib/Tree/Simple/Manager.pm
Criterion Covered Total %
statement 138 142 97.1
branch 57 62 91.9
condition 12 18 66.6
subroutine 24 24 100.0
pod 8 8 100.0
total 239 254 94.0


line stmt bran cond sub pod time code
1              
2             package Tree::Simple::Manager;
3              
4 4     4   139007 use strict;
  4         11  
  4         170  
5 4     4   22 use warnings;
  4         7  
  4         121  
6              
7 4     4   24 use Scalar::Util qw(blessed);
  4         9  
  4         633  
8              
9             our $VERSION = '0.07';
10              
11 4     4   3092 use Tree::Simple::Manager::Index;
  4         10  
  4         122  
12 4     4   32 use Tree::Simple::Manager::Exceptions;
  4         17  
  4         80  
13              
14 4     4   5298 use Tree::Parser;
  4         47027  
  4         183  
15              
16 4     4   80 use Tree::Simple;
  4         8  
  4         28  
17 4     4   5452 use Tree::Simple::View::DHTML;
  4         39194  
  4         130  
18              
19 4     4   4141 use File::stat;
  4         37395  
  4         27  
20              
21 4     4   6054 use Storable ();
  4         19308  
  4         8451  
22            
23             sub new {
24 20     20 1 20709 my ($_class, @tree_configs) = @_;
25 20 100 66     164 (@tree_configs && scalar(@tree_configs) >= 2)
26             || throw Tree::Simple::Manager::InsufficientArguments "You must supply at least one tree valid config";
27 19   33     94 my $class = ref($_class) || $_class;
28 19         36 my $tree_manager = {};
29 19         45 bless($tree_manager, $class);
30 19         60 $tree_manager->_init(\@tree_configs);
31 9         35 return $tree_manager;
32             }
33              
34             sub _init {
35 19     19   30 my ($self, $tree_configs) = @_;
36 19         60 $self->{trees} = {};
37 19         28 while (@{$tree_configs}) {
  30         97  
38 21         31 my ($tree_name, $config) = splice @{$tree_configs}, 0, 2;
  21         58  
39            
40 21 100       83 (exists $config->{tree_root})
41             || throw Tree::Simple::Manager::InsufficientArguments "missing the required keys for '$tree_name' config";
42            
43 19 100       86 (!exists $self->{trees}->{$tree_name})
44             || throw Tree::Simple::Manager::DuplicateName "The tree '$tree_name' already exists";
45              
46 18         49 $self->{trees}->{$tree_name} = {};
47            
48 18         36 my $root_tree = $config->{tree_root};
49 18 100 66     215 (blessed($root_tree) && $root_tree->isa('Tree::Simple'))
50             || throw Tree::Simple::Manager::IncorrectObjectType "The 'root_tree' must be a Tree::Simple instance (or a subclass of it)";
51              
52             # load from the file or something
53 17         97 my $tree = $self->_loadTree($tree_name => $config);
54            
55             # by default we use our Index module
56            
57 13         21 my $tree_index_module;
58 13 100       45 if (exists $config->{tree_index}) {
59 2 100       27 ($config->{tree_index}->isa('Tree::Simple::Manager::Index'))
60             || throw Tree::Simple::Manager::IncorrectObjectType "the 'tree_index' must be a subclass of Tree::Simple::Manager::Index";
61 1         4 $tree_index_module = $config->{tree_index};
62             }
63             else {
64 11         26 $tree_index_module = "Tree::Simple::Manager::Index";
65             }
66            
67 12         88 $self->{trees}->{$tree_name}->{index} = $tree_index_module->new($tree);
68            
69 12 100       58 if (exists $config->{tree_meta_data}) {
70 1 50       6 (ref $config->{tree_meta_data} eq 'HASH')
71             || throw Tree::Simple::Manager::IncorrectObjectType "the 'tree_meta_data' option must be a HASH";
72 1         8 foreach my $tree_id (keys %{$config->{tree_meta_data}}) {
  1         5  
73 2         11 my $tree = $self->{trees}->{$tree_name}->{index}->getTreeByID($tree_id);
74 2 50       11 ($tree->isa('Tree::Simple::WithMetaData'))
75             || throw Tree::Simple::Manager::IncorrectObjectType "the 'tree_meta_data' node for ($tree_name) id($tree_id) must be a Tree::Simple::WithMetaData instance";
76 2         3 $tree->addMetaData(%{$config->{tree_meta_data}->{$tree_id}});
  2         11  
77             }
78             }
79            
80 12         20 my $tree_view;
81 12 100       47 if (exists $config->{tree_view}) {
82 2 100       25 ($config->{tree_view}->isa('Tree::Simple::View'))
83             || throw Tree::Simple::Manager::IncorrectObjectType "the 'tree_view' must be a subclass of Tree::Simple::View";
84 1         3 $tree_view = $config->{tree_view};
85             }
86             else {
87 10         19 $tree_view = "Tree::Simple::View::DHTML"
88             }
89            
90 11         37 $self->{trees}->{$tree_name}->{view} = $tree_view;
91             }
92             }
93              
94             sub _loadTree {
95 17     17   32 my ($self, $tree_name, $config) = @_;
96              
97 17 100       58 (exists $config->{tree_file_path})
98             || throw Tree::Simple::Manager::InsufficientArguments "missing the required keys for '$tree_name' config";
99              
100 16 100       41 if (exists $config->{tree_cache_path}) {
101 4         30 my $cache_stat = stat $config->{tree_cache_path};
102 4         3320 my $file_stat = stat $config->{tree_file_path};
103              
104 4 100 66     653 if ( $file_stat and $cache_stat and $cache_stat->mtime >= $file_stat->mtime ) {
      100        
105 2         25 my $tree;
106 2         3 eval {
107 2         11 $tree = Storable::retrieve($config->{tree_cache_path});
108             };
109 2 50       429 if ($@) {
110 0         0 warn "Unable to load tree cache, removing cache tree";
111 0         0 unlink $config->{tree_cache_path};
112 0         0 warn "Attempting to load tree with parser";
113             }
114             else {
115 2         8 $self->{trees_loaded_from_cache}->{$tree_name}++;
116 2         9 return $tree;
117             }
118             }
119             }
120              
121 14         32 my $tree;
122 14         25 eval {
123 14         93 my $tp = Tree::Parser->new($config->{tree_root});
124            
125 14 50       370 $tp->setFileEncoding($config->{tree_file_encoding})
126             if exists $config->{tree_file_encoding};
127            
128 14         56 $tp->setInput($config->{tree_file_path});
129            
130 13 100       1982 if (exists $config->{tree_parse_filter}) {
131 3 100       17 (ref($config->{tree_parse_filter}) eq 'CODE')
132             || throw Tree::Simple::Manager::IncorrectObjectType "a 'tree_parse_filter' must be a code ref";
133 2         10 $tp->setParseFilter(
134             $self->_parseFilterWrapper(
135             ref($config->{tree_root}),
136             $config->{tree_parse_filter}
137             )
138             );
139             }
140             else {
141 10         50 $tp->setParseFilter(
142             $self->_getDefaultParseFilter(
143             ref($config->{tree_root})
144             )
145             );
146             }
147 12         113 $tp->parse();
148 11         1644 $tree = $tp->getTree();
149             };
150              
151 14 100       2304 if ($@) {
152 3         298 throw Tree::Simple::Manager::OperationFailed "unable to parse tree file '" . $config->{tree_file_path}. "'" => $@;
153             }
154            
155 11 100       37 if (exists $config->{tree_cache_path}) {
156 2         3 eval {
157 2         14 Storable::store($tree, $config->{tree_cache_path});
158             };
159 2 50       791 if ($@) {
160 0         0 warn "Unable to store tree cache ... sorry";
161             }
162             }
163            
164 11         34 return $tree;
165             }
166              
167             sub _parseFilterWrapper {
168 2     2   5 my ($self, $tree_type, $filter) = @_;
169             return sub {
170 13     13   1388 my $i = shift;
171 13         31 my ($depth, $tree) = $filter->($i, $tree_type);
172 13 100 66     908 (blessed($tree) && $tree->isa('Tree::Simple'))
173             || throw Tree::Simple::Manager::IncorrectObjectType "Custom Parse filters must return Tree::Simple objects";
174 12         35 return ($depth, $tree);
175 2         15 };
176             }
177              
178             sub _getDefaultParseFilter {
179 10     10   21 my (undef, $tree_type) = @_;
180             return sub {
181 119     119   14254 my ($line_iterator) = @_;
182 119         307 my $line = $line_iterator->next();
183 119         4788 my ($id, $tabs, $node) = ($line =~ /(\d+)\t(\t+)?(.*)/);
184 119         179 my $depth = 0;
185 119 100       257 $depth = length $tabs if $tabs;
186 119         339 my $tree = $tree_type->new($node);
187 119         3096 $tree->setUID($id);
188 119         756 return ($depth, $tree);
189 10         102 };
190             }
191              
192             sub getTreeList {
193 2     2 1 3279 my ($self) = @_;
194 2 100       8 return wantarray ? keys %{$self->{trees}} : [ keys %{$self->{trees}} ];
  1         12  
  1         10  
195             }
196              
197             sub getRootTree {
198 9     9 1 1272 my ($self, $tree_name) = @_;
199 9 100       36 (defined($tree_name))
200             || throw Tree::Simple::Manager::InsufficientArguments "Tree name not specified";
201 8 100       40 (exists $self->{trees}->{$tree_name})
202             || throw Tree::Simple::Manager::KeyDoesNotExist "tree ($tree_name) does not exist";
203 7         36 return $self->{trees}->{$tree_name}->{index}->getRootTree();
204             }
205              
206             sub getTreeIndex {
207 12     12 1 3610 my ($self, $tree_name) = @_;
208 12 100       37 (defined($tree_name))
209             || throw Tree::Simple::Manager::InsufficientArguments "Tree name not specified";
210 11 100       55 (exists $self->{trees}->{$tree_name})
211             || throw Tree::Simple::Manager::KeyDoesNotExist "tree ($tree_name) does not exist";
212 10         56 return $self->{trees}->{$tree_name}->{index};
213             }
214              
215             sub getTreeByID {
216 6     6 1 2856 my ($self, $tree_name, $tree_id) = @_;
217 6         19 return $self->getTreeIndex($tree_name)->getTreeByID($tree_id);
218             }
219              
220             sub getTreeViewClass {
221 5     5 1 1709 my ($self, $tree_name) = @_;
222 5 100       18 (defined($tree_name))
223             || throw Tree::Simple::Manager::InsufficientArguments "Tree name not specified";
224 4 100       21 (exists $self->{trees}->{$tree_name})
225             || throw Tree::Simple::Manager::KeyDoesNotExist "tree ($tree_name) does not exist";
226 3         13 return $self->{trees}->{$tree_name}->{view};
227             }
228              
229             sub getNewTreeView {
230 1     1 1 25 my ($self, $tree_name, @view_args) = @_;
231 1         3 my $tree_view_class = $self->getTreeViewClass($tree_name);
232 1         4 return $tree_view_class->new($self->getRootTree($tree_name), @view_args);
233             }
234              
235             sub isTreeLoadedFromCache {
236 4     4 1 2660 my ($self, $tree_name) = @_;
237 4 100       42 exists $self->{trees_loaded_from_cache}->{$tree_name} &&
238             $self->{trees_loaded_from_cache}->{$tree_name};
239             }
240              
241             1;
242              
243             __END__