File Coverage

blib/lib/IO/Iron/IronMQ/Client.pm
Criterion Covered Total %
statement 44 261 16.8
branch 0 50 0.0
condition 0 3 0.0
subroutine 16 41 39.0
pod 14 14 100.0
total 74 369 20.0


line stmt bran cond sub pod time code
1             package IO::Iron::IronMQ::Client;
2              
3             ## no critic (Documentation::RequirePodAtEnd)
4             ## no critic (Documentation::RequirePodSections)
5             ## no critic (ControlStructures::ProhibitPostfixControls)
6             ## no critic (Subroutines::RequireArgUnpacking)
7              
8 3     3   65635 use 5.010_000;
  3         18  
9 3     3   14 use strict;
  3         6  
  3         55  
10 3     3   28 use warnings;
  3         7  
  3         96  
11              
12             # Global creator
13 0         0 BEGIN {
14 3     3   471 use parent qw( IO::Iron::ClientBase ); # Inheritance
  3     0   287  
  3         15  
15             }
16              
17             # Global destructor
18       3     END {
19             }
20              
21              
22             # ABSTRACT: IronMQ (Online Message Queue) Client.
23              
24             our $VERSION = '0.13'; # VERSION: generated by DZP::OurPkgVersion
25              
26              
27              
28 3     3   244 use Log::Any qw{$log};
  3         14  
  3         17  
29 3     3   578 use File::Spec qw{read_file};
  3         7  
  3         65  
30 3     3   522 use File::HomeDir;
  3         5407  
  3         211  
31 3     3   20 use Hash::Util 0.06 qw{lock_keys lock_keys_plus unlock_keys legal_keys};
  3         45  
  3         20  
32 3     3   215 use Carp::Assert;
  3         6  
  3         16  
33 3     3   329 use Carp::Assert::More;
  3         6  
  3         444  
34 3     3   19 use English '-no_match_vars';
  3         10  
  3         14  
35 3     3   1589 use Params::Validate qw(:all);
  3         8864  
  3         412  
36              
37              
38 3     3   1334 use IO::Iron::IronMQ::Api;
  3         6  
  3         88  
39 3     3   428 use IO::Iron::Common;
  3         6  
  3         117  
40             require IO::Iron::Connection;
41             require IO::Iron::IronMQ::Queue;
42              
43             # CONSTANTS for this package
44              
45             # DEFAULTS
46 3     3   470 use Const::Fast;
  3         2777  
  3         17  
47              
48             # Service specific!
49             const my $DEFAULT_API_VERSION => '3';
50             const my $DEFAULT_HOST => 'mq-aws-us-east-1-1.iron.io';
51              
52              
53              
54             sub new {
55 0     0 1   my $class = shift;
56             my %params = validate(
57             @_, {
58 0           map { $_ => { type => SCALAR, optional => 1 }, } IO::Iron::Common::IRON_CLIENT_PARAMETERS(), ## no critic (ValuesAndExpressions::ProhibitCommaSeparatedStatements)
  0            
59             }
60             );
61              
62 0           $log->tracef('Entering new(%s, %s)', $class, \%params);
63 0           my $self = IO::Iron::ClientBase->new();
64             # Add more keys to the self hash.
65             my @self_keys = (
66             'queues', # References to all objects created of class IO::Iron::IronMQ::Queue.
67 0           legal_keys(%{$self}),
  0            
68             );
69 0           unlock_keys(%{$self});
  0            
70 0           lock_keys_plus(%{$self}, @self_keys);
  0            
71 0           my $config = IO::Iron::Common::get_config(%params);
72 0           $log->debugf('The config: %s', $config);
73 0 0         $self->{'project_id'} = defined $config->{'project_id'} ? $config->{'project_id'} : undef;
74 0           $self->{'queues'} = [];
75 0           assert_nonblank( $self->{'project_id'}, 'self->{project_id} is not defined or is blank');
76              
77 0           unlock_keys(%{$self});
  0            
78 0           bless $self, $class;
79 0           lock_keys(%{$self}, @self_keys);
  0            
80              
81             # Set up the connection client
82             my $connection = IO::Iron::Connection->new( {
83             'project_id' => $config->{'project_id'},
84             'token' => $config->{'token'},
85             'host' => defined $config->{'host'} ? $config->{'host'} : $DEFAULT_HOST,
86             'protocol' => $config->{'protocol'},
87             'port' => $config->{'port'},
88             'api_version' => defined $config->{'api_version'} ? $config->{'api_version'} : $DEFAULT_API_VERSION,
89             'timeout' => $config->{'timeout'},
90 0 0         'connector' => $params{'connector'},
    0          
91             }
92             );
93 0           $self->{'connection'} = $connection;
94 0           $log->debugf('IronMQ Connection created with config: (project_id=%s; token=%s; host=%s; timeout=%s).', $config->{'project_id'}, $config->{'token'}, $config->{'host'}, $config->{'timeout'});
95 0           $log->tracef('Exiting new: %s', $self);
96 0           return $self;
97             }
98              
99              
100             sub get_queue {
101 0     0 1   my $self = shift;
102 0           my %params = validate(
103             @_, {
104             'name' => { type => SCALAR, }, # queue name.
105             }
106             );
107 0           $log->tracef('Entering get_queue(%s)', \%params);
108 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
109              
110 0           my $connection = $self->{'connection'};
111             my ($http_status_code, $response_message) = $connection->perform_iron_action(
112             IO::Iron::IronMQ::Api::IRONMQ_GET_QUEUE_INFO(),
113 0           { '{Queue Name}' => $params{'name'}, }
114             );
115 0           $self->{'last_http_status_code'} = $http_status_code;
116 0           my $new_queue = $response_message->{'queue'};
117 0           my $get_queue_name = $new_queue->{'name'};
118 0           my $queue = IO::Iron::IronMQ::Queue->new({
119             'ironmq_client' => $self, # Pass a reference to the parent object.
120             'name' => $get_queue_name,
121             'connection' => $connection,
122             });
123 0           $log->debugf('Created a new IO::Iron::IronMQ::Queue object (queue name=%s.', $get_queue_name);
124 0           $log->tracef('Exiting get_queue: %s', $queue);
125 0           return $queue;
126             }
127              
128              
129             sub get_queues {
130 0     0 1   my $self = shift;
131 0           my %params = validate(
132             @_, {
133             'prefix' => { type => SCALAR, optional => 1, },
134             }
135             );
136 0           $log->tracef('Entering get_queues()');
137              
138 0           my $previous = q{};
139 0           my @queue_names;
140 0           while( my @names = $self->list_queues( 'per_page' => 30, 'previous' => $previous ) ) {
141 0           push @queue_names, @names;
142 0           $previous = $names[-1];
143             }
144 0           $log->debugf('Got a list of %d queue names.', scalar @queue_names);
145              
146 0           my @queues;
147 0           foreach my $queue_name (@queue_names) {
148 0           push @queues, $self->get_queue('name' => $queue_name);
149             }
150              
151 0           $log->tracef('Exiting get_queues: %s', \@queues);
152 0           return @queues;
153             }
154              
155              
156             sub create_and_get_queue {
157 0     0 1   my $self = shift;
158             my %params = validate(
159             @_, {
160             'name' => { type => SCALAR, callbacks => {
161 0     0     'RFC 3986 reserved character check' => sub { return ! IO::Iron::Common::contains_rfc_3986_res_chars(shift) },
162             }}, # queue name.
163 0           'message_timeout' => { type => SCALAR, optional => 1, },
164             'message_expiration' => { type => SCALAR, optional => 1, },
165             'type' => { type => SCALAR, optional => 1,
166             regex => qr/^(?:multicast|unicast|pull)$/msx, ## no critic (Variables::ProhibitPunctuationVars)
167             },
168             'push' => { type => HASHREF, optional => 1, },
169             'dead_letter' => { type => HASHREF, optional => 1, },
170             }
171             );
172 0           $log->tracef('Entering create_queue(%s)', \%params);
173 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
174              
175 0           my $connection = $self->{'connection'};
176 0           my %item_body;
177 0 0         $item_body{'message_timeout'} = $params{'message_timeout'} if ($params{'message_timeout'});
178 0 0         $item_body{'message_expiration'} = $params{'message_expiration'} if ($params{'message_expiration'});
179 0 0         $item_body{'type'} = $params{'type'} if ($params{'type'});
180 0 0         $item_body{'push'} = $params{'push'} if ($params{'push'});
181 0 0         $item_body{'dead_letter'} = $params{'dead_letter'} if ($params{'dead_letter'});
182             my ($http_status_code, $response_message) = $connection->perform_iron_action(
183             IO::Iron::IronMQ::Api::IRONMQ_CREATE_QUEUE(),
184             {
185 0           '{Queue Name}' => $params{'name'},
186             'body' => \%item_body,
187             }
188             );
189 0           $self->{'last_http_status_code'} = $http_status_code;
190             my $queue = IO::Iron::IronMQ::Queue->new({
191             'ironmq_client' => $self, # Pass a reference to the parent object.
192             'connection' => $connection,
193 0           'name' => $params{'name'},
194             });
195 0           $log->debugf('Created a new IO::Iron::IronMQ::Queue object (queue name=%s).', $queue->name());
196 0           $log->tracef('Exiting create_queue: %s', $queue);
197 0           return $queue;
198             }
199              
200              
201             sub create_queue {
202 0     0 1   my $self = shift;
203             my %params = validate(
204             @_, {
205             'name' => { type => SCALAR, callbacks => {
206 0     0     'RFC 3986 reserved character check' => sub { return ! IO::Iron::Common::contains_rfc_3986_res_chars(shift) },
207             }}, # queue name.
208 0           'message_timeout' => { type => SCALAR, optional => 1, },
209             'message_expiration' => { type => SCALAR, optional => 1, },
210             'type' => { type => SCALAR, optional => 1,
211             regex => qr/^(?:multicast|unicast|pull)$/msx, ## no critic (Variables::ProhibitPunctuationVars)
212             },
213             'push' => { type => HASHREF, optional => 1, },
214             'dead_letter' => { type => HASHREF, optional => 1, },
215             }
216             );
217 0           $log->tracef('Entering create_queue(%s)', \%params);
218 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
219              
220 0           my $connection = $self->{'connection'};
221 0           my %item_body;
222 0 0         $item_body{'message_timeout'} = $params{'message_timeout'} if ($params{'message_timeout'});
223 0 0         $item_body{'message_expiration'} = $params{'message_expiration'} if ($params{'message_expiration'});
224 0 0         $item_body{'type'} = $params{'type'} if ($params{'type'});
225 0 0         $item_body{'push'} = $params{'push'} if ($params{'push'});
226 0 0         $item_body{'dead_letter'} = $params{'dead_letter'} if ($params{'dead_letter'});
227             my ($http_status_code, $response_message) = $connection->perform_iron_action(
228             IO::Iron::IronMQ::Api::IRONMQ_CREATE_QUEUE(),
229             {
230 0           '{Queue Name}' => $params{'name'},
231             'body' => \%item_body,
232             }
233             );
234 0           $self->{'last_http_status_code'} = $http_status_code;
235 0           $log->tracef('Exiting create_queue: %s', undef);
236 0           return;
237             }
238              
239              
240             sub get_queue_info {
241 0     0 1   my $self = shift;
242 0           my %params = validate(
243             @_, {
244             'name' => { type => SCALAR, }, # queue name.
245             }
246             );
247 0           $log->tracef('Entering get_queue_info(%s)', \%params);
248 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
249              
250 0           my $connection = $self->{'connection'};
251             my ($http_status_code, $response_message) = $connection->perform_iron_action(
252             IO::Iron::IronMQ::Api::IRONMQ_GET_QUEUE_INFO(),
253 0           { '{Queue Name}' => $params{'name'}, }
254             );
255 0           $self->{'last_http_status_code'} = $http_status_code;
256 0           my $info = $response_message;
257             # {"id":"51be[...]","name":"Log_Test_Queue","size":0,"total_messages":3,"project_id":"51bd[...]"}
258 0           $log->debugf('Returned info about queue %s.', $params{'name'});
259 0           $log->tracef('Exiting get_queue_info: %s', $info);
260 0           return $info;
261             }
262              
263              
264             sub update_queue {
265 0     0 1   my $self = shift;
266             my %params = validate(
267             @_, {
268             'name' => { type => SCALAR, callbacks => {
269 0     0     'RFC 3986 reserved character check' => sub { return ! IO::Iron::Common::contains_rfc_3986_res_chars(shift) },
270             }}, # queue name.
271 0           'message_timeout' => { type => SCALAR, optional => 1, },
272             'message_expiration' => { type => SCALAR, optional => 1, },
273             'push' => { type => HASHREF, optional => 1, },
274             'dead_letter' => { type => HASHREF, optional => 1, },
275             }
276             );
277 0           $log->tracef('Entering update_queue(%s)', \%params);
278 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
279              
280 0           my $connection = $self->{'connection'};
281 0           my %item_body;
282 0 0         $item_body{'message_timeout'} = $params{'message_timeout'} if ($params{'message_timeout'});
283 0 0         $item_body{'message_expiration'} = $params{'message_expiration'} if ($params{'message_expiration'});
284 0 0         $item_body{'push'} = $params{'push'} if ($params{'push'});
285 0 0         $item_body{'dead_letter'} = $params{'dead_letter'} if ($params{'dead_letter'});
286             my ($http_status_code, $response_message) = $connection->perform_iron_action(
287             IO::Iron::IronMQ::Api::IRONMQ_UPDATE_QUEUE(),
288             {
289 0           '{Queue Name}' => $params{'name'},
290             'body' => \%item_body,
291             }
292             );
293 0           $self->{'last_http_status_code'} = $http_status_code;
294 0           $log->tracef('Exiting update_queue: %s', undef);
295 0           return;
296             }
297              
298              
299             sub delete_queue {
300 0     0 1   my $self = shift;
301             my %params = validate(
302             @_, {
303             'name' => { type => SCALAR, callbacks => {
304 0     0     'RFC 3986 reserved character check' => sub { return ! IO::Iron::Common::contains_rfc_3986_res_chars(shift) },
305 0     0     'Is longer than zero characters' => sub { return length shift },
306             }}, # queue name.
307             }
308 0           );
309 0           $log->tracef('Entering delete_queue(%s)', \%params);
310              
311 0           my $connection = $self->{'connection'};
312             my ($http_status_code, $response_message) = $connection->perform_iron_action(
313             IO::Iron::IronMQ::Api::IRONMQ_DELETE_QUEUE(),
314             {
315 0           '{Queue Name}' => $params{'name'},
316             }
317             );
318 0           $self->{'last_http_status_code'} = $http_status_code;
319 0           $log->debugf('Deleted queue (queue name=%s).', $params{'name'});
320 0           $log->tracef('Exiting delete_queue: %d', undef);
321 0           return;
322             }
323              
324              
325             sub list_queues {
326 0     0 1   my $self = shift;
327 0           my %params = validate(
328             @_, {
329             'per_page' => { type => SCALAR, optional => 0,
330             regex => qr/^[[:digit:]]{1,}$/msx, ## no critic (Variables::ProhibitPunctuationVars)
331             },
332             'previous' => { type => SCALAR, optional => 0, }, # Can be empty string.
333             'prefix' => { type => SCALAR, optional => 1, },
334             }
335             );
336 0           $log->tracef('Entering list_queues()');
337              
338 0           my @queues;
339 0           my $connection = $self->{'connection'};
340 0           my %query_params;
341 0 0         $query_params{'{per_page}'} = $params{'per_page'} if $params{'per_page'};
342 0 0         $query_params{'{previous}'} = $params{'previous'} if $params{'previous'};
343 0 0         $query_params{'{prefix}'} = $params{'prefix'} if $params{'prefix'};
344 0           my ($http_status_code, $response_message) = $connection->perform_iron_action(
345             IO::Iron::IronMQ::Api::IRONMQ_LIST_QUEUES(),
346             {
347             %query_params
348             } );
349 0           $self->{'last_http_status_code'} = $http_status_code;
350 0           foreach my $queue_info (@{$response_message->{'queues'}}) {
  0            
351 0           my $queue_name = $queue_info->{'name'};
352 0           push @queues, $queue_name;
353             }
354              
355 0           $log->tracef('Exiting list_queues: %s', \@queues);
356 0           return @queues;
357             }
358              
359              
360             sub add_subscribers {
361 0     0 1   my $self = shift;
362 0           my %params = validate(
363             @_, {
364             'name' => { type => SCALAR, }, # queue name.
365             'subscribers' => { type => ARRAYREF, optional => 1 }, # array of subscriber hashes containing a required "url" field and an optional "headers" map for custom headers.
366             }
367             );
368 0           $log->tracef('Entering add_subscribers(%s)', \%params);
369 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
370              
371 0           my $connection = $self->{'connection'};
372 0           my %item_body;
373 0 0         $item_body{'subscribers'} = $params{'subscribers'} if ($params{'subscribers'});
374             my ($http_status_code, $response_message) = $connection->perform_iron_action(
375             IO::Iron::IronMQ::Api::IRONMQ_ADD_SUBSCRIBERS_TO_A_MESSAGE_QUEUE(),
376             {
377 0           '{Queue Name}' => $params{'name'},
378             'body' => \%item_body,
379             }
380             );
381 0           $self->{'last_http_status_code'} = $http_status_code;
382              
383 0           $log->tracef('Exiting add_subscribers: %d', 1);
384 0           return 1;
385             }
386              
387              
388             # TODO Inform bug in documentation: does not return the queue info, returns: "{msg => 'Updated'}".
389              
390             sub delete_subscribers {
391 0     0 1   my $self = shift;
392 0           my %params = validate(
393             @_, {
394             'name' => { type => SCALAR, }, # queue name.
395             'subscribers' => { type => ARRAYREF, optional => 1 }, # array of subscriber hashes containing a required "url" field and an optional "headers" map for custom headers.
396             }
397             );
398 0           $log->tracef('Entering delete_subscribers(%s)', \%params);
399 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
400              
401 0           my $connection = $self->{'connection'};
402 0           my %item_body;
403 0 0         $item_body{'subscribers'} = $params{'subscribers'} if ($params{'subscribers'});
404             my ($http_status_code, $response_message) = $connection->perform_iron_action(
405             IO::Iron::IronMQ::Api::IRONMQ_DELETE_SUBSCRIBERS_FROM_A_MESSAGE_QUEUE(),
406             {
407 0           '{Queue Name}' => $params{'name'},
408             'body' => \%item_body,
409             }
410             );
411 0           $self->{'last_http_status_code'} = $http_status_code;
412              
413 0           $log->debugf('Deleted subscribers (queue name=%s).', $params{'name'});
414 0           $log->tracef('Exiting delete_subscribers: %d', 1);
415 0           return 1;
416             }
417              
418              
419             sub add_alerts {
420 0     0 1   my $self = shift;
421             my %params = validate(
422             @_, {
423             'name' => { type => SCALAR, }, # queue name.
424             'alerts' => { type => ARRAYREF,
425             callbacks => {
426             'Assert item content' => sub {
427 0     0     foreach my $alert (@{$_[0]}) {
  0            
428             # TODO New function: Carp::Assert::More::assert_allowed(). Allowed keys in hash.
429 0           assert_exists($alert, [ 'type', 'queue', 'trigger' ], 'Hash alert contains keys \'type\', \'queue\' and \'trigger\'.');
430             }
431 0           return 1;
432             }
433             }
434             }, # An array of alert hashes containing required "type", "queue", "trigger", and optional "direction", "snooze" fields. Maximum number of alerts is 5.
435             }
436 0           );
437 0           $log->tracef('Entering add_alerts(%s)', \%params);
438 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
439              
440 0           my $connection = $self->{'connection'};
441 0           my %item_body;
442 0           $item_body{'alerts'} = $params{'alerts'};
443             my ($http_status_code, $response_message) = $connection->perform_iron_action(
444             IO::Iron::IronMQ::Api::IRONMQ_ADD_ALERTS_TO_A_QUEUE(),
445             {
446 0           '{Queue Name}' => $params{'name'},
447             'body' => \%item_body,
448             }
449             );
450 0           $self->{'last_http_status_code'} = $http_status_code;
451              
452 0           $log->tracef('Exiting add_alerts: %d', 1);
453 0           return 1;
454             }
455              
456              
457             sub replace_alerts {
458 0     0 1   my $self = shift;
459             my %params = validate(
460             @_, {
461             'name' => { type => SCALAR, }, # queue name.
462             'alerts' => { type => ARRAYREF,
463             callbacks => {
464             'Assert item content' => sub {
465 0     0     foreach my $alert (@{$_[0]}) {
  0            
466 0           assert_exists($alert, [ 'type', 'queue', 'trigger' ], 'Hash alert contains keys \'type\', \'queue\' and \'trigger\'.');
467             }
468 0           return 1;
469             }
470             }
471             }, # An array of alert hashes containing required "type", "queue", "trigger", and optional "direction", "snooze" fields. Maximum number of alerts is 5.
472             }
473 0           );
474 0           $log->tracef('Entering replace_alerts(%s)', \%params);
475 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
476              
477 0           my $connection = $self->{'connection'};
478 0           my %item_body;
479 0           $item_body{'alerts'} = $params{'alerts'};
480             my ($http_status_code, $response_message) = $connection->perform_iron_action(
481             IO::Iron::IronMQ::Api::IRONMQ_REPLACE_ALERTS_ON_A_QUEUE(),
482             {
483 0           '{Queue Name}' => $params{'name'},
484             'body' => \%item_body,
485             }
486             );
487 0           $self->{'last_http_status_code'} = $http_status_code;
488              
489 0           $log->tracef('Exiting replace_alerts: %d', 1);
490 0           return 1;
491             }
492              
493              
494             sub delete_alerts {
495 0     0 1   my $self = shift;
496             my %params = validate(
497             @_, {
498             'name' => { type => SCALAR, }, # queue name.
499             'alerts' => { type => ARRAYREF, optional => 1,
500             callbacks => {
501             'Either parameter \'alerts\' or \'id\'' => sub {
502 0 0   0     return exists$_[1]->{'id'} ? 0 : 1;
503             },
504             'Assert item content' => sub {
505 0     0     foreach my $alert (@{$_[0]}) {
  0            
506 0           assert_exists($alert, [ 'id' ], 'Hash alert contains key \'id\'.');
507             }
508 0           return 1;
509             }
510             },
511             }, # An array of alerts hashes containing "id" field.
512             'id' => { type => SCALAR, optional => 1,
513             callbacks => {
514             'Either parameter \'alerts\' or \'id\'' => sub {
515 0 0   0     return exists$_[1]->{'alerts'} ? 0 : 1;
516             },
517             },
518             }, # alert id.
519             # TODO New function: Params::Validate::validate(), mutually exclusive parameters.
520             }
521 0           );
522 0           $log->tracef('Entering delete_alerts(%s)', \%params);
523 0           assert_nonblank( $params{'name'}, 'Parameter \'name\' is a non blank string');
524 0   0       assert($params{'alerts'} || $params{'id'}, 'Have either parameter \'alerts\' or \'id\'.');
525              
526 0           my $connection = $self->{'connection'};
527 0 0         if($params{'alerts'}) {
528 0           my %item_body;
529 0           $item_body{'alerts'} = $params{'alerts'};
530             my ($http_status_code, $response_message) = $connection->perform_iron_action(
531             IO::Iron::IronMQ::Api::IRONMQ_REMOVE_ALERTS_FROM_A_QUEUE(),
532             {
533 0           '{Queue Name}' => $params{'name'},
534             'body' => \%item_body,
535             }
536             );
537 0           $self->{'last_http_status_code'} = $http_status_code;
538             }
539             else {
540             my ($http_status_code, $response_message) = $connection->perform_iron_action(
541             IO::Iron::IronMQ::Api::IRONMQ_REMOVE_ALERTS_FROM_A_QUEUE_BY_ID(),
542             {
543             '{Queue Name}' => $params{'name'},
544 0           '{Alert ID}' => $params{'id'},
545             }
546             );
547 0           $self->{'last_http_status_code'} = $http_status_code;
548             }
549              
550 0           $log->debugf('Deleted alerts (queue name=%s).', $params{'name'});
551 0           $log->tracef('Exiting delete_alerts: %d', 1);
552 0           return 1;
553             }
554              
555             1;
556              
557             __END__