File Coverage

blib/lib/I22r/Translate/Request.pm
Criterion Covered Total %
statement 129 140 92.1
branch 33 48 68.7
condition 26 31 83.8
subroutine 21 21 100.0
pod 2 13 15.3
total 211 253 83.4


line stmt bran cond sub pod time code
1             package I22r::Translate::Request;
2 24     24   10608 use Moose;
  24         6008600  
  24         134  
3 24     24   102240 use Carp;
  24         34  
  24         27854  
4              
5             our $VERSION = '0.95';
6              
7             has _config => ( is => 'rw', isa => 'HashRef',
8             default => sub { {} } );
9             has results => ( is => 'rw', isa => 'HashRef', default => sub { {} } );
10             has src => ( is => 'ro', isa => 'Str', required => 1 );
11             has dest => ( is => 'ro', isa => 'Str', required => 1 );
12             has text => ( is => 'rw', isa => 'HashRef', required => 1 );
13             has start => ( is => 'ro', isa => 'Int', default => sub { time } );
14             has logger => ( is => 'rw' );
15              
16             # TODO: return_type validation: simple, object, hash
17             has return_type => ( is => 'ro', isa => 'Str', default => 'simple' );
18              
19             # TODO: backend validation:
20             has backend => ( is => 'rw', default => undef );
21              
22             our %filters_loaded = ();
23              
24             sub BUILDARGS {
25 29     29 1 81 my ($class, %opts) = @_;
26 29         38 my $config = { };
27 29         92 foreach my $key (keys %opts) {
28 98 100 100     386 if ($key eq 'src' || $key eq 'dest' || $key eq 'text') {
      100        
29 81         126 $config->{$key} = $opts{$key};
30             } else {
31 18         56 $config->{_config}{$key} = $opts{$key};
32             }
33             }
34 28         744 return $config;
35             }
36              
37             sub BUILD {
38 26     27 0 36 my $self = shift;
39 26         24 $self->{otext} = { %{$self->text} };
  26         669  
40             }
41              
42             sub config {
43 79     79 1 828 my ($self, $key) = @_;
44 79         2106 my $r = $self->_config->{$key};
45 79 100       170 return $r if defined $r;
46              
47 52 100       1218 if ($self->backend) {
48 35         827 $r = $self->_config->{ $self->backend . '::' . $key };
49 35 50       64 return $r if defined $r;
50 35         835 $r = $self->backend->config($key);
51 35 100       82 return $r if defined $r;
52             }
53              
54 41         105 return $I22r::Translate::config{$key};
55             }
56              
57             sub translations_complete {
58 21     21 0 24 my $self = shift;
59 21         20 foreach my $id (keys %{$self->text}) {
  21         508  
60 50 100       1243 if (!defined $self->results->{$id}) {
61 3         13 return 0;
62             }
63             }
64 18         63 return 1;
65             }
66              
67             sub otext {
68 36     36 0 119 my $self = shift;
69 36         77 return $self->{otext};
70             }
71              
72             # return results in accordance with the desired return_type
73             sub return_results {
74 21     21 0 24 my $self = shift;
75 21   100     42 my $return_type = $self->config('return_type') // 'simple';
76              
77 21 100       48 if ($return_type eq 'object') {
78 7         6 return %{ $self->results };
  7         159  
79             }
80 14 100       30 if ($return_type eq 'hash') {
81             return map {
82 2         42 $_ => $self->results->{$_}->to_hash
83 2         2 } keys %{$self->results};
  2         43  
84             }
85 12 50 50     64 if ($return_type eq 'simple' || 1) {
86             return map {
87 30         734 $_ => $self->results->{$_}->text
88 12         18 } keys %{$self->results};
  12         309  
89             }
90             }
91              
92             ##########################################################
93             #
94             # Filter methods
95             #
96             ##########################################################
97              
98             sub get_filters {
99 21     21 0 26 my $self = shift;
100 21   100     85 my $f1 = $I22r::Translate::config{filter} // [];
101 21   66     505 my $f2 = ($self->backend && $self->backend->config('filter')) // [];
      100        
102 21   100     516 my $f3 = $self->_config->{'filter'} // [];
103 21         45 return [ map { to_filter($_) } @$f1, @$f2, @$f3 ];
  16         20  
104             }
105              
106             sub to_filter {
107 16     16 0 16 my $filter = shift;
108 16         16 my @args = ();
109 16 50       29 if ('ARRAY' eq ref $filter) {
110 0         0 ($filter, @args) = @$filter;
111             }
112 16 50       23 if (ref $filter) {
113 0         0 return $filter;
114             }
115 16 50       37 if ($filter !~ /::/) {
116 16         30 $filter = "I22r::Translate::Filter::" . $filter;
117             }
118              
119 16     2   842 my $f = eval "use $filter; $filter->new( \@args )";
  2     2   710  
  2     2   3  
  2     2   46  
  2     2   359  
  2     2   3  
  2         40  
  2         10  
  2         2  
  2         34  
  2         13  
  2         2  
  2         33  
  2         8  
  2         2  
  2         39  
  2         10  
  2         1  
  2         32  
120 16 50       1766 if ($@) {
121             # what should we do when filter fails to load? croak or just carp?
122 0         0 carp "error loading filter $filter: $@\n";
123             }
124 16         72 return $f;
125              
126             # TODO - assert $filter fulfills the I22r::Translate::Filter role
127             }
128              
129             sub apply_filters {
130 21     21 0 25 my $self = shift;
131 21         17 $self->{otext} = { %{$self->text} };
  21         490  
132              
133             # apply filters to $self->text for any input
134             # that doesn't have a result (in $self->results )
135             my @filter_targets = grep {
136 54         1276 !defined $self->results->{$_}
137 21         40 } keys %{$self->text};
  21         489  
138              
139 21 50       50 if (@filter_targets == 0) {
140 0         0 $self->{filter_targets} = [];
141 0         0 $self->{filters} = [];
142 0         0 return;
143             }
144 21         61 $self->{filter_targets} = \@filter_targets;
145 21         62 $self->{filters} = $self->get_filters;
146              
147 21         23 foreach my $filter ( @{$self->{filters}} ) {
  21         54  
148             I22r::Translate->log(
149 16 50       68 $self->{logger}, " applying filter: ",
150             ref($filter) ? ref($filter) : "$filter" );
151 16         21 foreach my $id (@filter_targets) {
152 36         71 $filter->apply( $self, $id );
153             }
154             }
155             }
156              
157             sub unapply_filters {
158 21     21 0 29 my $self = shift;
159 21         31 my @targets = @{$self->{filter_targets}};
  21         47  
160 21         24 foreach my $filter ( reverse @{ $self->{filters} } ) {
  21         38  
161             I22r::Translate->log(
162 16 50       57 $self->{logger}, " removing filter: ",
163             ref($filter) ? ref($filter) : "$filter");
164 16         25 foreach my $id (@targets) {
165 36         64 $filter->unapply( $self, $id );
166             }
167             }
168 21         33 foreach my $id (@targets) {
169 54         1440 $self->text->{$id} = $self->{otext}{$id};
170 54 100       1387 if (defined($self->results->{$id})) {
171 47         1149 $self->results->{$id}{otext} = $self->text->{$id};
172             }
173             }
174 21         42 delete $self->{filter_targets};
175 21         67 delete $self->{filters};
176             }
177              
178             ##########################################################
179             #
180             # time out methods
181             #
182             ##########################################################
183              
184             sub timed_out {
185 42     42 0 21003255 my $self = shift;
186 42         1459 my $elapsed = time - $self->start;
187 42 100 100     1082 if ($self->_config->{timeout} && $elapsed >= $self->_config->{timeout}) {
188             I22r::Translate->log($self->{logger},
189 2         16 "request timed out after ${elapsed}s");
190 2         8 return 1;
191             }
192              
193 40 100 100     167 if ($I22r::Translate::config{timeout} &&
194             $elapsed >= $I22r::Translate::config{timeout}) {
195             I22r::Translate->log($self->{logger},
196 4         32 "request timed out after ${elapsed}s");
197 4         16 return 1;
198             }
199              
200 36 50 33     987 if ($self->backend && $self->backend->config('timeout')) {
201 0 0       0 if ($self->{backend_start}) {
202 0         0 $elapsed = time - $self->{backend_start};
203             }
204 0 0       0 if ($elapsed >= $self->backend->config('timeout')) {
205             I22r::Translate->log($self->{logger},
206 0         0 "request timed out after ${elapsed}s");
207 0         0 return 1;
208             }
209             }
210 36         66 return;
211             }
212              
213             ##########################################################
214             #
215             # Callback functions
216             #
217             ##########################################################
218              
219             sub get_callbacks {
220 21     21 0 22 my $self = shift;
221             my @callbacks = ($self->_config->{callback},
222             $self->backend
223             && $self->backend->config("callback"),
224 21   66     612 $I22r::Translate::config{callback});
225 21         66 return grep defined, @callbacks;
226             }
227              
228             sub invoke_callbacks {
229 21     21 0 33 my ($self, @ids) = @_;
230 21         40 $DB::single = 1;
231 21 50       55 return if !@ids;
232 21         53 my @callbacks = $self->get_callbacks;
233 21 100       62 return if ! @callbacks;
234             I22r::Translate->log( $self->{logger},
235 1         15 "invoking callbacks on inputs ",
236             "@ids" );
237 1         3 foreach my $id (@ids) {
238 1         3 foreach my $callback (@callbacks) {
239 3         91 $callback->( $self, $self->results->{$id} );
240             }
241             }
242             }
243              
244             ##########################################################
245              
246             __PACKAGE__->meta->make_immutable;
247             1;
248              
249             __END__
250              
251             TODO:
252              
253             src_enc, dest_enc
254              
255             return_type validation
256              
257             backend validation, must be undef or fulfill I22r::Translate::Backend role
258              
259             new_result($id, $translated_text) method so the backends don't need to
260             call the I22r::Translate::Result constructor ??
261              
262             #'
263              
264             =head1 NAME
265              
266             I22r::Translate::Request - translation request object
267              
268             =head1 DESCRIPTION
269              
270             Internal translation request object for the L<I22r::Translation>
271             distribution. If you're not developing a backend or a filter for
272             this distribution, you can stop reading now.
273              
274             Otherwise, you'll just need to know that a new C<I22r::Translate::Request>
275             object is created when you call one of the
276             L<I22r::Translate::translate_xxx|I22r::Translate/"translate_string">
277             methods.
278              
279             =head1 METHODS
280              
281             =head2 src
282              
283             =head2 dest
284              
285             The source and target languages for the translation request.
286              
287             =head2 text
288              
289             A hash reference whose values are the source strings to be
290             translated. If the request was created from a C<translate_string>
291             or C<translate_list> call, the inputs are still put into a hash
292             reference.
293              
294             =head2 _config
295              
296             All other inputs to C<I22r::Translate::translate_xxx> are put
297             into a configuration hash for the request, accessible through
298             the C<_config> method.
299              
300             =head2 config
301              
302             A special method that examines the current request's configuration,
303             configuration for the current backend (see L<"backend">), and the
304             global configuration from L<I22r::Translate>.
305              
306             =head2 backend
307              
308             Get or set the name of the active backend. The C<I22r::Translate>
309             translation process will iterate through available, qualified
310             backends until all of the inputs have been translated.
311              
312             =head2 results
313              
314             A hashref for translation results. Each key should be the same as
315             a key in L<< $request->text|"text" >>, and the value is an
316             L<I22r::Translate::Result> object.
317              
318             =head1 MORE DEVELOPER NOTES
319              
320             If you are writing a new L<filter|I22r::Translate::Filter>,
321             you will want your C<apply> method to operate on an element
322             of C<< $request->text >> (say, C<< $request->text->{$key} >>,
323             and your C<unapply> method to operate on the corresponding
324             C<< $request->results->{$key}->text >>.
325              
326             In a backend, you'll want to pass the values in
327             C<< $request->text >> the translation engine, and populate
328             C<< $request->results >> with the results of the translation.
329              
330             =head1 SEE ALSO
331              
332             L<I22r::Translate>
333              
334             =cut