File Coverage

blib/lib/Data/Phrasebook/Loader/JSON.pm
Criterion Covered Total %
statement 64 64 100.0
branch 36 38 94.7
condition 11 12 91.6
subroutine 11 11 100.0
pod 5 5 100.0
total 127 130 97.6


line stmt bran cond sub pod time code
1             package Data::Phrasebook::Loader::JSON;
2              
3 9     9   432484 use strict;
  9         26  
  9         731  
4 9     9   51 use warnings FATAL => 'all';
  9         19  
  9         1030  
5              
6             our $VERSION = '0.02';
7              
8             #--------------------------------------------------------------------------
9              
10 9     9   51 use base qw( Data::Phrasebook::Loader::Base Data::Phrasebook::Debug );
  9         25  
  9         11392  
11              
12 9     9   40816 use Carp qw( croak );
  9         28  
  9         670  
13 9     9   12067 use File::Slurp;
  9         223809  
  9         2585  
14 9     9   26743 use JSON::XS;
  9         123411  
  9         11862  
15              
16             #--------------------------------------------------------------------------
17              
18             =head1 NAME
19              
20             Data::Phrasebook::Loader::JSON - Absract your phrases with JSON.
21              
22             =head1 SYNOPSIS
23              
24             use Data::Phrasebook;
25              
26             my $q = Data::Phrasebook->new(
27             class => 'Fnerk',
28             loader => 'JSON',
29             file => 'phrases.json',
30             );
31              
32             $q->delimiters( qr{ \[% \s* (\w+) \s* %\] }x );
33             my $phrase = $q->fetch($keyword);
34              
35             =head1 DESCRIPTION
36              
37             This class loader implements phrasebook patterns using JSON.
38              
39             Phrases can be contained within one or more dictionaries, with each phrase
40             accessible via a unique key. Phrases may contain placeholders, please see
41             L for an explanation of how to use these. Groups of phrases
42             are kept in a dictionary. In this implementation a single file is one
43             complete dictionary.
44              
45             An example JSON file:
46              
47             {
48             "dictionary1": { "key1": "value1", "key2": "value2" },
49             "dictionary2": { "key3": "value3", "key4": "value4" }
50             }
51              
52             Within the phrase text placeholders can be used, which are then replaced with
53             the appropriate values once the get() method is called. The default style of
54             placeholders can be altered using the delimiters() method.
55              
56             =head1 INHERITANCE
57              
58             L inherits from the base class
59             L.
60             See that module for other available methods and documentation.
61              
62             =head1 METHODS
63              
64             =head2 load
65              
66             Given a C, load it. C must contain a JSON map.
67              
68             $loader->load( $file, @dict );
69              
70             This method is used internally by L's
71             C method, to initialise the data store.
72              
73             It must take a C (be it a scalar, or something more complex)
74             and return a handle. The C is optional, should you wish to use the
75             dictionary support.
76              
77             =cut
78              
79             sub load {
80 11     11 1 6471 my ($class, $file, @dict) = @_;
81 11 100       527 croak "No file given as argument!" unless defined $file;
82 9 50       286 croak "Unable to read file [$file]!" unless -r $file;
83 9         269 my $json = read_file( $file );
84 9 100       1182 croak "No data in given JSON file [$file]" unless $json;
85 8         111 my $data = decode_json($json);
86 8 50       29 croak "Badly formatted JSON file [$file]" unless $data;
87 8         42 $class->{json} = $data;
88              
89             # what sections are we using?
90 8   100     56 my $key = $class->{defaultname} || ($class->dicts)[0];
91 8 100       40 $class->{default} = ($key ? $class->{json}->{$key}
92             : $class->{json});
93              
94 8         22 $class->{dict} = [];
95 8 100       29 $class->{dict} = [$class->{defaultname}] if $class->{defaultname};
96 8 100       49 $class->{dict} = (ref $dict[0] ? $dict[0] : [@dict]) if scalar @dict;
    100          
97             }
98              
99             =head2 get
100              
101             Returns the phrase stored in the phrasebook, for a given keyword.
102              
103             my $value = $loader->get( $key );
104              
105             If one or more named dictionaries have been previously selected, they will be
106             searched in order, followed by the default dictionary. The first hit on
107             C will be returned, otherwise C is returned.
108              
109             =cut
110              
111             sub get {
112 23     23 1 8574 my ($class,$key) = @_;
113 23 100       77 return unless($key);
114 18 100       58 return unless($class->{json});
115              
116 17 100       54 my @dicts = (ref $class->{dict} ? @{$class->{dict}} : ());
  16         43  
117              
118 17         41 foreach ( @dicts ) {
119 12 100 100     89 return $class->{json}->{$_}->{$key}
120             if exists $class->{json}->{$_}
121             and exists $class->{json}->{$_}->{$key};
122             }
123              
124 13 100 100     106 return $class->{default}->{$key}
125             if ref $class->{default} eq 'HASH'
126             and exists $class->{default}->{$key};
127              
128 8         24 return;
129             }
130              
131             =head2 dicts
132              
133             Returns the list of dictionaries available.
134              
135             my @dicts = $loader->dicts();
136              
137             This is the list of all dictionaries available in the source file. If multiple
138             dictionaries are not being used, then an empty list will be returned.
139              
140             =cut
141              
142             sub dicts {
143 15     15 1 1739 my $class = shift;
144              
145 15         21 my @keys = keys %{$class->{json}};
  15         70  
146 15 100       28 if ( scalar @keys ==
  36         105  
147 15         39 scalar grep {ref $_ eq 'HASH'} values %{$class->{json}} ) {
148             # data source looks like it has multiple dictionaries
149 11         91 return (sort @keys);
150             }
151              
152 4         22 return ();
153             }
154              
155             =head2 keywords
156              
157             Returns the list of keywords available. List is lexically sorted.
158              
159             my @keywords = $loader->keywords( $dict );
160              
161             If one or more named dictionaries have been previously selected, they will be
162             farmed for keywords, followed by the default dictionary.
163              
164             The C argument is optional, and may be used to override the search to a
165             single named dictionary, or a list of dictionaries if passed by reference,
166             plus the default dictionary of course.
167              
168             To find all available keywords in all available dictionaries, use the
169             following:
170              
171             $loader->keywords( [ $loader->dicts ] );
172              
173             =cut
174              
175             sub keywords {
176 18     18 1 1205 my ($class, $dict) = @_;
177 18         23 my (%keywords, @dicts);
178              
179 18 100       80 @dicts = ( (not $dict) ? (ref $class->{dict} ? @{$class->{dict}} : ())
  6 100       32  
    100          
180             : (ref $dict) ? @$dict : ($dict) );
181              
182 18         43 foreach my $d (@dicts) {
183             next unless
184 23 100 66     131 exists $class->{json}->{$d}
185             and ref $class->{json}->{$d} eq 'HASH';
186 21         26 map { $keywords{$_} = 1 } keys %{$class->{json}->{$d}};
  63         140  
  21         59  
187             }
188              
189 18 100       59 if (ref $class->{default} eq 'HASH') {
190 17         21 map { $keywords{$_} = 1 } keys %{$class->{default}};
  44         71  
  17         42  
191             }
192              
193 18         88 my @keywords = sort keys %keywords;
194 18         154 return @keywords;
195             }
196              
197             =head2 set_default
198              
199             If a requested phrase is not found in the named dictionary an attempt is made
200             to find it in the I dictionary. L loaders normally
201             use the first dictionary in the phrasebook as the default, but as mentioned in
202             L this does not make sense because the dictionaries in
203             JSON phrasebooks are not ordered.
204              
205             To override the automatically selected default dictionary use this method, and
206             pass it a C. This value is only reset at phrasebook
207             load time, so you'll probably need to trigger a reload:
208              
209             $q->loader->set_default( $default_dictionary_name );
210             $q->loader->load( $file );
211              
212             To reset the loader's behaviour to automatic default dictionary selection,
213             pass this method an undefined value, and then reload.
214              
215             =cut
216              
217             sub set_default {
218 2     2 1 850 $_[0]->{defaultname} = $_[1];
219             }
220              
221             1;
222              
223             __END__