File Coverage

blib/lib/Data/Phrasebook/Generic.pm
Criterion Covered Total %
statement 97 97 100.0
branch 40 50 80.0
condition 10 14 71.4
subroutine 16 16 100.0
pod 10 10 100.0
total 173 187 92.5


line stmt bran cond sub pod time code
1             package Data::Phrasebook::Generic;
2 12     12   18591 use strict;
  12         24  
  12         437  
3 12     12   58 use warnings FATAL => 'all';
  12         23  
  12         398  
4 12     12   6016 use Data::Phrasebook::Loader;
  12         34  
  12         493  
5 12     12   74 use base qw( Data::Phrasebook::Debug );
  12         20  
  12         1359  
6 12     12   66 use Carp qw( croak );
  12         20  
  12         752  
7              
8 12     12   61 use vars qw($VERSION);
  12         25  
  12         15053  
9             $VERSION = '0.35';
10              
11             =head1 NAME
12              
13             Data::Phrasebook::Generic - Base class for Phrasebook Models
14              
15             =head1 SYNOPSIS
16              
17             use Data::Phrasebook;
18              
19             my $q = Data::Phrasebook->new(
20             class => 'Fnerk',
21             loader => 'XML',
22             file => 'phrases.xml',
23             dict => 'English',
24             );
25              
26             =head1 DESCRIPTION
27              
28             This module provides a base class for phrasebook implementations.
29              
30             =head1 CONSTRUCTOR
31              
32             =head2 new
33              
34             C takes an optional hash of arguments. Each value in the hash
35             is given as an argument to a method of the same name as the key.
36              
37             This constructor should B need to be called directly as
38             Phrasebook creation should go through the L factory.
39              
40             Subclasses should provide at least an accessor method to retrieve values for
41             a named key. Further methods can be overloaded, but must retain a standard
42             API to the overloaded method.
43              
44             All, or at least I, phrasebook implementations should inherit from
45             B class.
46              
47             =cut
48              
49             sub new {
50 21     21 1 36 my $class = shift;
51 21         87 my %hash = @_;
52 21   100     130 $hash{loader} ||= 'Text';
53              
54 21 100       122 if($class->debug) {
55 1         8 $class->store(3,"$class->new IN");
56 1         9 $class->store(4,"$class->new args=[".$class->dumper(\%hash)."]");
57             }
58              
59 21         68 my $self = bless {}, $class;
60              
61             # set default delimiters, in case custom delimiters
62             # are provided in the hash
63 21         174 $self->{delimiters} = qr{ :(\w+) }x;
64              
65 21         67 foreach (keys %hash) {
66 39         173 $self->$_($hash{$_});
67             }
68              
69 21         112 return $self;
70             }
71              
72             =head1 METHODS
73              
74             =head2 loader
75              
76             Set, or get, the loader class. Uses a default if none have been
77             specified. See L.
78              
79             =head2 unload
80              
81             Called by the file() and dict() methods when a fresh file or dictionary is
82             specified, and reloading is required.
83              
84             =head2 loaded
85              
86             Accessor to determine whether the current dictionary has been loaded
87              
88             =head2 file
89              
90             A description of a file that is passed to the loader. In most cases,
91             this is a file. A loader that gets its data from a database could
92             conceivably have this as a hash like thus:
93              
94             $q->file( {
95             dsn => "dbi:SQLite:dbname=bookdb",
96             table => 'phrases',
97             } );
98              
99             That is, which loader you use determines what your C looks like.
100              
101             The default loader takes just an ordinary filename.
102              
103             =head2 dict
104              
105             Accessor to store the dictionary to be used.
106              
107             =cut
108              
109             sub loader {
110 103     103 1 629 my $self = shift;
111 103 50       463 my $load = @_ ? shift : defined $self->{loader} ? $self->{loader} : 'Text';
    100          
112 103         381 $self->{loader} = $load;
113             }
114             sub unload {
115 24     24 1 37 my $self = shift;
116 24         48 $self->{loaded} = undef;
117 24         64 $self->{'loaded-data'} = undef;
118 24         48 return;
119             }
120              
121             sub loaded {
122 55     55 1 95 my $self = shift;
123 55 100       170 my $load = @_ ? $self->{loaded} = shift : $self->{loaded} ;
124              
125             # ensure we know what loader class we are getting
126 55 100       200 $self->loader($load->class) if($load);
127 55         122 return $load;
128             }
129             sub file {
130 49     49 1 6390 my $self = shift;
131 49 100       145 if(@_) {
132 20         31 my $file = shift;
133 20 50 66     98 if(!$self->{file} || $file ne $self->{file}) {
134 20         83 $self->unload();
135 20         43 $self->{file} = $file;
136             }
137             }
138 49         274 return $self->{file};
139             }
140              
141             sub dict {
142 37     37 1 1495 my $self = shift;
143              
144 37 100       113 if(@_) {
145 4         16 my $list1 = "@_";
146 4 100       14 my $list2 = $self->{dict} ? "@{$self->{dict}}" : '';
  2         7  
147              
148 4 50       13 if($list1 ne $list2) {
149 4         14 $self->unload();
150 4 50       24 $self->{dict} = (ref $_[0] ? $_[0] : [@_]);
151             }
152             }
153              
154 37 100       205 return($self->{dict} ? @{$self->{dict}} : () ) if(wantarray);
  4 100       33  
155 7 100       43 return($self->{dict} ? $self->{dict}->[0] : undef );
156             }
157              
158             =head2 dicts
159              
160             Having instantiated the C object class, and using the C
161             attribute as a directory path, the object can return a list of the current
162             dictionaries available (provided the plugin supports it) as:
163              
164             my $pb = Data::Phrasebook->new(
165             loader => 'Text',
166             file => '/tmp/phrasebooks',
167             );
168              
169             my @dicts = $pb->dicts;
170              
171             or
172              
173             my @dicts = $pb->dicts( $path );
174              
175             =cut
176              
177             sub dicts {
178 3     3 1 1294 my $self = shift;
179              
180 3         18 my $loader = $self->loaded;
181 3 50       10 unless($loader) {
182 3 50       12 $self->store(4,"->dicts loader=[".($self->loader)."]") if($self->debug);
183 3         10 $loader = Data::Phrasebook::Loader->new(
184             'class' => $self->loader,
185             'parent' => $self,
186             );
187 3         23 $self->loader($loader->class); # so we know what we've got
188             }
189              
190             # just in case it doesn't use D::P::Loader::Base
191 3 50       30 croak("dicts() unsupported in plugin")
192             unless($loader->can("dicts"));
193              
194 3         16 return $loader->dicts(@_);
195             }
196              
197             =head2 keywords
198              
199             Having instantiated the C object class, using the C
200             attribute as required, the object can return a list of the current keywords
201             available (provided the plugin supports it) as:
202              
203             my $pb = Data::Phrasebook->new(
204             loader => 'Text',
205             file => '/tmp/phrasebooks',
206             dict => 'TEST',
207             );
208              
209             my @keywords = $pb->keywords;
210              
211             or
212              
213             my @keywords = $pb->keywords( $dict );
214              
215             Note the list will be a combination of the default and any named dictionary.
216             However, not all Loader plugins may support the second usage.
217              
218             =cut
219              
220             sub keywords {
221 7     7 1 9762 my $self = shift;
222              
223 7         24 my $loader = $self->loaded;
224 7 100       21 if(!defined $loader) {
225 5 50       22 $self->store(4,"->keywords loader=[".($self->loader)."]") if($self->debug);
226 5         17 $loader = Data::Phrasebook::Loader->new(
227             'class' => $self->loader,
228             'parent' => $self,
229             );
230 5         26 $self->loader($loader->class); # so we know what we've got
231             }
232              
233             # just in case it doesn't use D::P::Loader::Base
234 7 50       44 croak("keywords() unsupported in plugin")
235             unless($loader->can("keywords"));
236              
237 7         36 return $loader->keywords(@_);
238             }
239              
240             =head2 data
241              
242             Loads the data source, if not already loaded, and returns the data block
243             associated with the given key.
244              
245             my $data = $self->data($key);
246              
247             This is typically only used internally by implementations, not the end user.
248              
249             =cut
250              
251             sub data {
252 25     25 1 38 my $self = shift;
253 25         42 my $id = shift;
254              
255 25 100       71 if($self->debug) {
256 2         7 $self->store(3,"->data IN");
257 2         9 $self->store(4,"->data id=[$id]");
258             }
259              
260 25 50       65 return unless($id);
261              
262 25         89 my $loader = $self->loaded;
263 25 100       63 if(!defined $loader) {
264 18 100       62 if($self->debug) {
265 1         7 $self->store(4,"->data loader=[".($self->loader)."]");
266 1   50     5 $self->store(4,"->data file=[".($self->file||'undef')."]");
267 1   50     7 $self->store(4,"->data dict=[".($self->dict||'undef')."]");
268             }
269 18         49 $loader = Data::Phrasebook::Loader->new(
270             'class' => $self->loader,
271             'parent' => $self,
272             );
273 17         100 $self->loader($loader->class); # so we know what we've got
274 17         73 $loader->load( $self->file, $self->dict );
275 16         73 $self->loaded($loader);
276             }
277              
278 23   100     130 return $self->{'loaded-data'}->{$id} ||= do { $loader->get( $id ) };
  22         85  
279             }
280              
281             =head2 delimiters
282              
283             Returns or sets the current delimiters for substitution variables. Must be a
284             regular expression with at least one capture group.
285              
286             The example below shows the default ':variable' style regex.
287              
288             $q->delimiters( qr{ :(\w+) }x );
289              
290             The example below shows a Template Toolkit style regex.
291              
292             $q->delimiters( qr{ \[% \s* (\w+) \s* %\] }x );
293              
294             In addition to the delimiter pattern, an optional setting to suppress missing
295             value errors can be passed after the pattern. If set to a true value, will
296             turn any unmatched delimiter patterns to an empty string.
297              
298             =cut
299              
300             sub delimiters {
301 25     25 1 1597 my $self = shift;
302 25 100       72 if(@_) {
303 4         9 $self->{delimiters} = shift;
304 4   50     32 $self->{blank_args} = shift || 0;
305             }
306            
307 25         85 return $self->{delimiters};
308             }
309              
310             1;
311              
312             __END__