File Coverage

blib/lib/Astro/SIMBAD/Client.pm
Criterion Covered Total %
statement 227 378 60.0
branch 80 226 35.4
condition 17 39 43.5
subroutine 39 54 72.2
pod 15 15 100.0
total 378 712 53.0


'.
line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Astro::SIMBAD::Client - Fetch astronomical data from SIMBAD 4.
4              
5             =head1 SYNOPSIS
6              
7             use Astro::SIMBAD::Client;
8             my $simbad = Astro::SIMBAD::Client->new ();
9             print $simbad->query (id => 'Arcturus');
10              
11             =head1 NOTICE
12              
13             As of release 0.027_01 the SOAP interface is deprecated. The University
14             of Strasbourg has announced at
15             L that this
16             interface will not be maintained after April 1 2014, and that
17             B.
18              
19             Because the SOAP interface is still sort of functional (except for
20             VO-format queries) as of June 4 2014, I have revised the transition plan
21             announced with the release of 0.027_01 on October 28 2014.
22              
23             What I have done as of version 0.031_01 is to add attribute
24             C. This was false by default. If this attribute is
25             true, the C method and friends, instead of issuing a SOAP
26             request to the SIMBAD server, will instead construct an equivalent
27             script query, and issue that. The deprecation warning will not be issued
28             if C is true, since the SOAP interface is not
29             being used.
30              
31             As of March 22 2021, SOAP queries started returning 404. Because of
32             this, I have made the default of C true. Well,
33             actually I have made it the Boolean inverse of environment variable
34             L. This is
35             mostly for my benefit, so I can see if SOAP has come back.
36              
37             If SOAP still has not come back after six months, SOAP queries will
38             become fatal, as will setting C to a false value.
39              
40             Eventually the SOAP code will be removed. In the meantime all tests are
41             skipped unless C is true, and are marked
42             TODO. Support of SOAP by this module will be on a best-effort basis;
43             that is, if I can make it work without a huge amount of work I will --
44             otherwise SOAP will become unsupported.
45              
46             =head1 DESCRIPTION
47              
48             This package implements several query interfaces to version 4 of the
49             SIMBAD on-line astronomical database, as documented at
50             L. B
51             with SIMBAD version 3.> Its primary purpose is to obtain SIMBAD data,
52             though some rudimentary parsing functionality also exists.
53              
54             There are three ways to access these data.
55              
56             - URL queries are essentially page scrapers, but their use is
57             documented, and output is available as HTML, text, or VOTable. URL
58             queries are implemented by the url_query() method.
59              
60             - Scripts may be submitted using the script() or script_file() methods.
61             The former takes as its argument the text of the script, the latter
62             takes a file name.
63              
64             - Queries may be made using the web services (SOAP) interface. The
65             query() method implements this, and queryObjectByBib,
66             queryObjectByCoord, and queryObjectById have been provided as
67             convenience methods. As of version 0.027_01, SOAP queries are
68             deprecated. See the L section above for the deprecation
69             schedule.
70              
71             Astro::SIMBAD::Client is object-oriented, with the object supplying not
72             only the URL scheme and SIMBAD server name, but the default format and
73             output type for URL and web service queries.
74              
75             A simple command line client application is also provided, as are
76             various examples in the F directory.
77              
78             =head2 Methods
79              
80             The following methods should be considered public:
81              
82             =over 4
83              
84             =cut
85              
86             package Astro::SIMBAD::Client;
87              
88             # We require Perl 5.008 because of MailTools, used by SOAP::Lite.
89             # Otherwise it would be 5.006 because of 'our'.
90              
91 5     5   980 use 5.008;
  5         17  
92              
93 5     5   25 use strict;
  5         9  
  5         92  
94 5     5   25 use warnings;
  5         8  
  5         137  
95              
96 5     5   28 use Carp;
  5         8  
  5         448  
97 5     5   3949 use LWP::UserAgent;
  5         279177  
  5         200  
98 5     5   39 use LWP::Protocol;
  5         11  
  5         120  
99 5     5   2658 use HTTP::Request::Common qw{POST};
  5         11964  
  5         366  
100 5     5   37 use Scalar::Util 1.01 qw{looks_like_number};
  5         145  
  5         237  
101 5     5   39 use URI::Escape ();
  5         8  
  5         286  
102             # use XML::DoubleEncodedEntities;
103             # use Astro::SIMBAD::Client::WSQueryInterfaceService;
104              
105 5         11 use constant HAVE_DOUBLE_ENCODED => do {
106 5         12 local $@ = undef;
107 5         12 eval { ## no critic (RequireCheckingReturnValueOfEval)
108 5         1008 require XML::DoubleEncodedEntities;
109 0         0 1;
110             };
111 5     5   33 };
  5         11  
112              
113 5     5   31 use constant ARRAY_REF => ref [];
  5         10  
  5         312  
114 5     5   30 use constant CODE_REF => ref sub {};
  5         11  
  5         562  
115              
116             my $have_time_hires;
117             BEGIN {
118 5     5   17 $have_time_hires = eval {
119 5         2716 require Time::HiRes;
120 5         7287 Time::HiRes->import (qw{time sleep});
121 5         1006 1;
122             };
123              
124             *_escape_uri = URI::Escape->can( 'uri_escape_utf8' )
125             || URI::Escape->can( 'uri_escape' )
126 5   50     850 || sub { return $_[0] };
127             }
128              
129             our $VERSION = '0.047';
130              
131             our @CARP_NOT = qw{Astro::SIMBAD::Client::WSQueryInterfaceService};
132              
133             # TODO replace this with s///r if we ever get to the point where we
134             # require Perl 5.13.2 or greater.
135             sub _strip_returns {
136 10     10   30 my ( $data ) = @_;
137 10         122 $data =~ s/ \n //smxg;
138 10         915 return $data;
139             }
140              
141 5     5   37 use constant FORMAT_TXT_SIMPLE_BASIC => _strip_returns( <<'EOD' );
  5         11  
  5         25  
142             ---\n
143             name: %IDLIST(NAME|1)\n
144             type: %OTYPE\n
145             long: %OTYPELIST\n
146             ra: %COO(d;A)\n
147             dec: %COO(d;D)\n
148             plx: %PLX(V)\n
149             pmra: %PM(A)\n
150             pmdec: %PM(D)\n
151             radial: %RV(V)\n
152             redshift: %RV(Z)\n
153             spec: %SP(S)\n
154             bmag: %FLUXLIST(B)[%flux(F)]\n
155             vmag: %FLUXLIST(V)[%flux(F)]\n
156             ident: %IDLIST[%*,]
157             EOD
158              
159 5     5   44 use constant FORMAT_TXT_YAML_BASIC => _strip_returns( <<'EOD' );
  5         11  
  5         16  
160             ---\n
161             name: '%IDLIST(NAME|1)'\n
162             type: '%OTYPE'\n
163             long: '%OTYPELIST'\n
164             ra: %COO(d;A)\n
165             dec: %COO(d;D)\n
166             plx: %PLX(V)\n
167             pm:\n
168             - %PM(A)\n
169             - %PM(D)\n
170             radial: %RV(V)\n
171             redshift: %RV(Z)\n
172             spec: %SP(S)\n
173             bmag: %FLUXLIST(B)[%flux(F)]\n
174             vmag: %FLUXLIST(V)[%flux(F)]\n
175             ident:\n%IDLIST[ - '%*'\n]
176             EOD
177              
178             # Documentation errors/omissions:
179             # %PLX:
180             # P = something. Yields '2' for Arcturus
181             # %SP: is really %sptype
182             # B = bibcode? Yields '~' for Arcturus
183             # N = don't know -- yields 'S' for Arcturus
184             # Q = quality? Yields 'C' for Arcturus
185             # S = spectral type
186              
187 5         26934 use constant FORMAT_VO_BASIC => join ',', qw{
188             id(NAME|1) otype ra(d) dec(d) plx_value pmra pmdec rv_value z_value
189 5     5   37 sp_type flux(B) flux(V)};
  5         14  
190             # Note that idlist was documented at one point as being the
191             # VOTable equivalent of %IDLIST. But it is no longer documented,
192             # and never returned anything but '?IDLIST
193              
194             my %static = (
195             autoload => 1,
196             debug => 0,
197             delay => 3,
198             emulate_soap_queries => ! $ENV{ASTRO_SIMBAD_CLIENT_USE_SOAP},
199             format => {
200             txt => FORMAT_TXT_YAML_BASIC,
201             vo => FORMAT_VO_BASIC,
202             script => '',
203             },
204             parser => {
205             txt => '',
206             vo => '',
207             script => '',
208             },
209             post => 1,
210             # lc(...) per https://tools.ietf.org/html/rfc3986#section-3.1
211             scheme => lc( $ENV{ASTRO_SIMBAD_CLIENT_SCHEME} || 'http' ),
212             ## server => 'simbad.u-strasbg.fr',
213             server => $ENV{ASTRO_SIMBAD_CLIENT_SERVER} || 'simbad.u-strasbg.fr',
214             type => 'txt',
215             url_args => {},
216             verbatim => 0,
217             );
218              
219             if ( my $msg = _is_scheme_valid(
220             $static{scheme},
221             q,
222             ) ) {
223             carp $msg;
224             $static{scheme} = 'http';
225             }
226              
227             =item $simbad = Astro::SIMBAD::Client->new ();
228              
229             This method instantiates an Astro::SIMBAD::Client object. Any arguments will be
230             passed to the set() method once the object is instantiated.
231              
232             =cut
233              
234             # The set() method does the unpacking. CAVEAT: do _NOT_ modify the
235             # contents of @_, as this will be seen by the caller. Modifying @_
236             # itself is fine.
237             sub new { ## no critic (RequireArgUnpacking)
238 3     3 1 664 my $class = shift;
239 3 50       13 $class = ref $class if ref $class;
240 3         10 my $self = bless {}, $class;
241 3         27 $self->set (%static, @_);
242 3         15 return $self;
243             }
244              
245             =item $string = $simbad->agent ();
246              
247             This method retrieves the user agent string used to identify this
248             package in queries to SIMBAD. This string will be the default string for
249             LWP::UserAgent, with this package name and version number appended in
250             parentheses. This method is exposed for the curious.
251              
252             =cut
253              
254             {
255             my $agent_string;
256             sub agent {
257 9   66 9 1 86 return ($agent_string ||= join (' ', LWP::UserAgent->_agent,
258             __PACKAGE__ . '/' . $VERSION));
259             }
260             }
261              
262             =item @attribs = $simbad->attributes ();
263              
264             This method retrieves the names of all public attributes, in
265             alphabetical order. It can be called as a static method, or
266             even as a subroutine.
267              
268             =cut
269              
270             sub attributes {
271 0 0   0 1 0 return wantarray ? sort keys %static : [sort keys %static]
272             }
273              
274             =item $value = $simbad->get ($attrib);
275              
276             This method retrieves the current value of the named
277             L. It can be called as a static method to
278             retrieve the default value.
279              
280             =cut
281              
282             sub get {
283 69     69 1 498 my $self = shift;
284 0         0 croak "Error - First argument must be an @{[__PACKAGE__]} object"
285 69 50       142 unless eval {$self->isa(__PACKAGE__)};
  69         368  
286 69 100       365 $self = \%static unless ref $self;
287 69         200 my $name = shift;
288             croak "Error - Attribute '$name' is unknown"
289 69 50       291 unless exists $static{$name};
290 69         581 return $self->{$name};
291             }
292              
293             =item $result = Parse_TXT_Simple ($data);
294              
295             This subroutine (B method) parses the given text data under the
296             assumption that it was generated using FORMAT_TXT_SIMPLE_BASIC or
297             something similar. The data is expected to be formatted as follows:
298              
299             A line consisting of exactly '---' separates objects.
300              
301             Data appear on lines that look like
302              
303             name: data
304              
305             and are parsed into a hash keyed by the given name. If the line ends
306             with a comma, it is assumed to contain multiple items, and the data
307             portion of the line is split on the commas; the resultant hash value
308             is a list reference.
309              
310             The user would normally not call this directly, but specify it as the
311             parser for 'txt'-type queries:
312              
313             $simbad->set (parser => {txt => 'Parse_TXT_Simple'});
314              
315             =cut
316              
317             sub Parse_TXT_Simple {
318 6     6 1 21 my $text = shift;
319 6         27 my $obj = {};
320 6         19 my @data;
321 6         267 foreach (split '\s*\n', $text) {
322 90 50       243 next unless $_;
323 90 100       243 if (m/^-+$/) {
324 6         24 $obj = {};
325 6         23 push @data, $obj;
326             } else {
327 84         395 my ($name, $val) = split ':\s*', $_;
328 84 100       476 $val =~ s/,$// and $val = [split ',', $val];
329 84         379 $obj->{$name} = $val;
330             }
331             }
332 6         48 return @data;
333             }
334              
335             =item $result = Parse_VO_Table ($data);
336              
337             This subroutine (B method) parses the given VOTable data,
338             returning a list of anonymous hashes describing the data. The $data
339             value is split on '
340             VOTables back (rather than a parse error) if that is what the input
341             contains.
342              
343             This is B a full-grown VOTable parser capable of handling
344             the full spec (see L).
345             It is oriented toward returning ETABLEDATAE contents, and the
346             metadata that can reasonably be associated with those contents.
347              
348             B that as of version 0.026_01, the requisite modules
349             to support VO format are B required. If you need VO format you will
350             need to install L or L
351              
352             The return is a list of anonymous hashes, one per ETABLEE. Each
353             hash contains two keys:
354              
355             {data} is the data contained in the table, and
356             {meta} is the metadata for the table.
357              
358             The {meta} element for the table is a reference to a list of data
359             gathered from the ETABLEE tag. Element zero is the tag name
360             ('TABLE'), and element 1 is a reference to a hash containing the
361             attributes of the ETABLEE tag. Subsequent elements if any
362             represent metadata tags in the order encountered in the parse.
363              
364             The {data} contains an anonymous list, each element of which is a row of
365             data from the ETABLEDATAE element of the ETABLEE, in the
366             order encountered by the parse. Each row is a reference to a list of
367             anonymous hashes, which represent the individual data of the row, in the
368             order encountered by the parse. The data hashes contain two keys:
369              
370             {value} is the value of the datum with undef for '~', and
371             {meta} is a reference to the metadata for the datum.
372              
373             The {meta} element for a datum is a reference to the metadata tag that
374             describes that datum. This will be an anonymous list, of which element 0
375             is the tag ('FIELD'), element 1 is a reference to a hash containing that
376             tag's attributes, and subsequent elements will be the contents of the
377             tag (typically including a reference to the list representing the
378             EDESCRIPTIONE tag for that FIELD).
379              
380             All values are returned as provided by the XML parser; no further
381             decoding is done. Specifically, the datatype and arraysize attributes
382             are ignored.
383              
384             This parser is based on XML::Parser.
385              
386             The user would normally not call this directly, but specify it as the
387             parser for 'vo'-type queries:
388              
389             $simbad->set (parser => {vo => 'Parse_VO_Table'});
390              
391             =cut
392              
393             { # Begin local symbol block.
394              
395             my $xml_parser;
396              
397             # TODO get rid of XML::Parser::Lite when you get rid of SOAP
398             foreach (qw{XML::Parser XML::Parser::Lite}) {
399             eval { _load_module( $_ ); 1 } or next;
400             $xml_parser = $_;
401             last;
402             }
403              
404             sub Parse_VO_Table {
405 0     0 1 0 my $data = shift;
406              
407 0 0       0 defined $xml_parser
408             or croak 'Error - No XML parser available. Need XML::Parser or XML::Parser::Lite';
409              
410 0         0 my $root;
411             my @tree;
412 0         0 my @table;
413 0         0 my @to_strip;
414              
415             # Arguments:
416             # Init ($class)
417             # Start ($class, $tag, $attr => $value ...)
418             # Char ($class, $text)
419             # End ($class, $tag)
420             # Final ($class)
421              
422             my $psr = $xml_parser->new (
423             Handlers => {
424             Init => sub {
425 0     0   0 $root = [];
426 0         0 @tree = ($root);
427 0         0 @table = ();
428             },
429             Start => sub {
430 0     0   0 shift;
431 0         0 my $tag = shift;
432 0         0 my $item = [$tag, {@_}];
433 0         0 push @{$tree[-1]}, $item;
  0         0  
434 0         0 push @tree, $item;
435             },
436             Char => sub {
437 0     0   0 push @{$tree[-1]}, $_[1];
  0         0  
438             },
439             End => sub {
440 0     0   0 my $tag = $_[1];
441 0 0       0 die < 1;
442             Error - Unmatched end tag
443             eod
444 0 0       0 die <
445             Error - End tag does not match start tag <$tree[-1][0]>
446             eod
447              
448             # From here to the end of the subroutine is devoted to detecting
449             # the
tag and extracting the data of the table into what 450             # is hopefully a more usable format. Any relationship of tables 451             # to resources is lost. 452               453 0         0 my $element = pop @tree; 454 0 0       0 if ($element->[0] eq 'TABLE') { 455 0         0 my (@meta, @data, @descr); 456 0         0 foreach (@$element) { 457 0 0       0 next unless ARRAY_REF eq ref $_; 458 0 0       0 if ($_->[0] eq 'FIELD') {     0           459 0         0 push @meta, $_; 460 0         0 push @descr, $_; 461             } elsif ($_->[0] eq 'DATA') { 462 0         0 foreach (@$_) { 463 0 0       0 next unless ARRAY_REF eq ref $_; 464 0 0       0 next unless $_->[0] eq 'TABLEDATA'; 465 0         0 foreach (@$_) { 466 0 0       0 next unless ARRAY_REF eq ref $_; 467 0 0       0 next unless $_->[0] eq 'TR'; 468 0         0 my @row; 469 0         0 foreach (@$_) { 470 0 0       0 next unless ARRAY_REF eq ref $_; 471 0 0       0 next unless $_->[0] eq 'TD'; 472 0         0 my @inf = grep {!ref $_} @$_;   0         0   473 0         0 shift @inf; 474 0         0 push @row, join ' ', @inf; 475             } 476 0         0 push @data, \@row; 477             } 478             } 479             } else { 480 0         0 push @descr, $_; 481             } 482             } 483 0         0 foreach (@data) { 484 0         0 my $inx = 0; 485 0         0 @$_ = map { { 486 0 0 0     0 value => (defined $_ && $_ eq '~') 487             ? undef : $_, 488             meta => $meta[$inx++], 489             } } @$_; 490             } 491 0         0 push @to_strip, @descr; 492 0         0 push @table, { 493             data => \@data, 494             meta => [$element->[0], 495             $element->[1], @descr], 496             }; 497             } 498             }, 499             Final => sub { 500 0 0   0   0 die < 1; 501             Error - Missing end tags. 502             eod 503               504             ## _strip_empty ($root); 505             ## @$root; 506             # If the previous two lines were uncommented and the following two 507             # commented, the parser would return the parse tree for the 508             # VOTable. 509 0         0 _strip_empty (\@to_strip); 510 0         0 @table; 511             }, 512 0         0 }); 513 0 0       0 return map {$_ ? $psr->parse ($_) : ()} split '(?=<\?xml)', $data   0         0   514             } 515               516             } # End of local symbol block. 517               518             # _strip_empty (\@tree) 519             # 520             # splices out anything in the tree that is not a reference and 521             # does not match m/\S/. 522               523             sub _strip_empty { 524 0     0   0 my $ref = shift; 525 0         0 my $inx = @$ref; 526 0         0 while (--$inx >= 0) { 527 0         0 my $val = $ref->[$inx]; 528 0         0 my $typ = ref $val; 529 0 0       0 if ( ARRAY_REF eq $typ ) {     0           530 0         0 _strip_empty ($val); 531             } elsif (!$typ) { 532 0 0       0 splice @$ref, $inx, 1 unless $val =~ m/\S/ms; 533             } 534             } 535 0         0 return; 536             } 537               538             =item $result = $simbad->query ($query => @args); 539               540             This method is B, and will cease to work in April 2014. 541             Please choose a method that does not use SOAP. See the L 542             above for details. 543               544             This method issues a web services (SOAP) query to the SIMBAD database. 545             The $query specifies a SIMBAD query method, and the @args are the 546             arguments for that method. Valid $query values and the corresponding 547             SIMBAD methods and arguments are: 548               549             bib => queryObjectByBib ($bibcode, $format, $type) 550             coo => queryObjectByCoord ($coord, $radius, $format, $type) 551             id => queryObjectById ($id, $format, $type) 552               553             where: 554               555             $bibcode is a SIMBAD bibliographic code 556             $coord is a set of coordinates 557             $radius is an angular radius around the coordinates 558             $type is the type of data to be returned 559             $format is a format appropriate to the data type. 560               561             The $type defaults to the value of the L attribute, and 562             the $format defaults to the value of the L attribute 563             for the given $type. 564               565             The return value depends on a number of factors: 566               567             If the query found nothing, you get undef in scalar context, and an 568             empty list in list context. 569               570             If a L is defined for the given type, the returned 571             data will be fed to the parser, and the output of the parser will be 572             returned. This is assumed to be a list, so a reference to the list 573             will be used in scalar context. Parser exceptions are not trapped, 574             so the caller will need to be prepared to deal with malformed data. 575               576             Otherwise, the result of the query is returned as-is. 577               578             B that this functionality makes use of the 579             L module. As of version 0.026_01 of 580             C, L is not a prerequisite 581             of this module. If you wish to use the C method, you will have 582             to install L separately. This can be done after 583             C is installed. 584               585             =cut 586               587             { # Begin local symbol block 588               589             my %query_args = ( 590             id => { 591             type => 2, 592             format => 1, 593             method => 'queryObjectById', 594             }, 595             bib => { 596             type => 2, 597             format => 1, 598             method => 'queryObjectByBib', 599             }, 600             coo => { 601             type => 3, 602             format => 2, 603             method => 'queryObjectByCoord', 604             }, 605             ); 606               607             my %transform = ( 608             txt => sub { 609             local $_ = $_[0]; 610             s/\n//gm; 611             return $_ 612             }, 613             vo => sub { 614             local $_ = ref $_[0] ? join (',', @{$_[0]}) : $_[0]; 615             if ( defined $_ ) { 616             s/\s+/,/gms; 617             s/^,+//; 618             s/,+$//; 619             s/,+/,/g; 620             } 621             return $_ 622             }, 623             ); 624               625             my %make_script = ( 626             txt => sub { 627             my ( $self, $query, @args ) = @_; 628             return <<"EOD"; 629             format object "@{[ $transform{txt}->( $self->get( 'format' )->{txt} ) ]}" 630             query $query @args 631             EOD 632             }, 633             vo => sub { 634             my ( $self, $query, @args ) = @_; 635             return <<"EOD"; 636             votable myvo { 637             @{[ $transform{vo}->( $self->get( 'format' )->{vo} ) ]} 638             } 639             votable open myvo 640             query $query @args 641             votable close myvo 642             EOD 643             }, 644             ); 645               646             sub query { 647 2     2 1 11 my ( $self, $query, @args ) = @_; 648 2 50       13 if ( $self->get( 'emulate_soap_queries' ) ) { 649 2         11 my $type = $self->get( 'type' ); 650             my $code = $make_script{$type} || sub { 651 0     0   0 my ( undef, $query, @args ) = @_; # Invocant unused 652 0         0 return "query $query @args\n"; 653 2   50     15 }; 654 2         13 return $self->_script( 655             parser => $type, 656             script => $code->( $self, $query, @args ), 657             verbatim => 0, 658             ); 659             } 660 0         0 $self->_deprecation_notice( method => 'query', 'a non-SOAP method' ); 661 0 0       0 eval { _load_module( 'SOAP::Lite' ); 1 }   0         0     0         0   662             or croak 'Error - query() requires SOAP::Lite'; 663 0 0       0 eval { _load_module(   0         0   664 0         0 'Astro::SIMBAD::Client::WSQueryInterfaceService' ); 1 } 665             or croak "Programming Error - Can not load Astro::SIMBAD::Client::WSQueryInterfaceService: $@"; 666             croak "Error - Illegal query type '$query'" 667 0 0       0 unless $query_args{$query}; 668 0         0 my $method = $query_args{$query}{method}; 669 0 0       0 croak "Programming error - Illegal query $query method $method" 670             unless Astro::SIMBAD::Client::WSQueryInterfaceService->can ($method); 671 0         0 my $debug = $self->get ('debug'); 672 0         0 my $parser; 673 0 0       0 if (defined (my $type = $query_args{$query}{type})) { 674 0   0     0 $args[$type] ||= $self->get ('type'); 675 0 0       0 if (defined (my $format = $query_args{$query}{format})) { 676 0   0     0 $args[$format] ||= $self->get ('format')->{$args[$type]}; 677             $args[$format] = $transform{$args[$type]}->($args[$format]) 678 0 0       0 if $transform{$args[$type]}; 679 0 0       0 warn "$args[$type] format: $args[$format]\n" if $debug; 680 0 0       0 $args[$format] = undef unless $args[$format]; 681             } 682 0         0 $parser = $self->_get_parser ($args[$type]); 683             } 684 0 0       0 SOAP::Lite->import (+trace => $debug ? 'all' : '-all'); 685 0         0 $self->_delay (); 686             ## $debug and SOAP::Trace->import ('all'); 687 0         0 my $rslt = Astro::SIMBAD::Client::WSQueryInterfaceService->$method( 688             $self, @args); 689 0 0       0 return unless defined $rslt; 690 0 0       0 HAVE_DOUBLE_ENCODED 691             and $rslt = XML::DoubleEncodedEntities::decode ($rslt); 692 0 0       0 return wantarray ? ($parser->($rslt)) : [$parser->($rslt)]     0           693             if $parser; 694 0         0 return $rslt; 695             } 696               697             } # End local symbol block. 698               699             =item $value = $simbad->queryObjectByBib ($bibcode, $format, $type); 700               701             This method is B, and will cease to work on December 31 702             2018. Please choose a method that does not use SOAP. See the 703             L above for details. 704               705             This method is just a convenience wrapper for 706               707             $value = $simbad->query (bib => $bibcode, $format, $type); 708               709             See the query() documentation for more information. 710               711             =cut 712               713             sub queryObjectByBib { 714 0     0 1 0 my $self = shift; 715 0         0 return $self->query (bib => @_); 716             } 717               718             =item $value = $simbad->queryObjectByCoord ($coord, $radius, $format, $type); 719               720             This method is B, and will cease to work on December 31 721             2018. Please choose a method that does not use SOAP. See the 722             L above for details. 723               724             This method is just a convenience wrapper for 725               726             $value = $simbad->query (coo => $coord, $radius, $format, $type); 727               728             See the query() documentation for more information. 729               730             =cut 731               732             sub queryObjectByCoord { 733 0     0 1 0 my $self = shift; 734 0         0 return $self->query (coo => @_); 735             } 736               737             =item $value = $simbad->queryObjectById ($id, $format, $type); 738               739             This method is B, and will cease to work on December 31 740             2018. Please choose a method that does not use SOAP. See the 741             L above for details. 742               743             This method is just a convenience wrapper for 744               745             $value = $simbad->query (id => $id, $format, $type); 746               747             See the query() documentation for more information. 748               749             =cut 750               751             sub queryObjectById { 752 0     0 1 0 my $self = shift; 753 0         0 return $self->query (id => @_); 754             } 755               756             =item $release = $simbad->release (); 757               758             This method returns the current SIMBAD4 release, as scraped from the 759             top-level web page. This will look something like 'SIMBAD4 1.045 - 760             27-Jul-2007' 761               762             If called in list context, it returns ($major, $minor, $point, $patch, 763             $date). The returned information corresponding to the scalar example 764             above is: 765               766             $major => 4 767             $minor => 1 768             $point => 45 769             $patch => '' 770             $date => '27-Jul-2007' 771               772             The $patch will usually be empty, but occasionally you get something 773             like release '1.019a', in which case $patch would be 'a'. 774               775             Please note that this method is B based on a published interface, 776             but is simply a web page scraper, and subject to all the problems such 777             software is heir to. What the algorithm attempts to do is to find (and 778             parse, if called in list context) the contents of the next EtdE 779             after 'Release:' (case-insensitive). 780               781             =cut 782               783             sub release { 784 3     3 1 8 my $self = shift; 785 3         14 my $rslt = $self->_retrieve( 'simbad/' ); 786 3 50       166 my ($rls) = $rslt->content =~ 787             m{Release:.*?.*?(.*?)}sxi 788             or croak "Error - Release information not found"; 789 3         664 $rls =~ s{<.*?>}{}g; 790 3         24 $rls =~ s/^\s+//; 791 3         29 $rls =~ s/\s+$//; 792 3 100       90 wantarray or return $rls; 793 1         7 $rls =~ s/\s+-\s+/ /; 794 1 50       12 my ($major, $minor, $date) = split '\s+', $rls 795             or croak "Error - Release '$rls' is ill-formed"; 796 1         5 $major =~s/^\D+//; 797 1         3 $major += 0; 798 1         7 ($minor, my $point) = split '\.', $minor, 2; 799 1         3 $minor += 0; 800 1 50       9 ($point, my $patch) = $point =~ m/^(\d+)(.*)/ 801             or croak "Error - Release '$rls' is ill-formed: bad point"; 802 1 50       4 defined $patch or $patch = ''; 803 1         3 $point += 0; 804 1         22 return ($major, $minor, $point, $patch, $date); 805             } 806               807             =item $value = $simbad->script ($script); 808               809             This method submits the given script to SIMBAD4. The $script variable 810             contains the text if the script; if you want to submit a script file 811             by name, use the script_file() method. 812               813             If the L attribute is false, the front matter of the 814             result (up to and including the '::data:::::' line) is stripped. If 815             there is no '::data:::::' line, the entire script output is raised as an 816             exception. 817               818             If a 'script' L was specified, the output of the script 819             (after stripping front matter if that was specified) is passed to it. 820             The parser is presumed to return a list, so if script() was called in 821             scalar context you get a reference to that list back. 822               823             If no 'script' L is specified, the output of the script 824             (after stripping front matter if that was specified) is simply returned 825             to the caller. 826               827             =cut 828               829             sub script { 830 2     2 1 9 my ( $self, $script ) = @_; 831 2         13 return $self->_script( 832             parser => 'script', 833             script => $script, 834             verbatim => $self->get( 'verbatim' ), 835             ); 836             } 837               838             { 839             my %dflt = ( 840             parser => sub { return 'script' }, 841             script => sub { 842             confess 'Programming error - script argument required'; 843             }, 844             verbatim => sub { 845             my ( $self ) = @_; 846             return $self->get( 'verbatim' ); 847             }, 848             ); 849               850             sub _script { 851 4     4   29 my ( $self, %arg ) = @_; 852               853 4         28 foreach my $key ( keys %dflt ) { 854             defined $arg{$key} 855 12 50       42 or $arg{$key} = $dflt{$key}->( $self ); 856             } 857               858 4         25 my $debug = $self->get( 'debug' ); 859               860 4 50       27 $debug 861             and warn "Debug - script\n$arg{script} "; 862               863             my $resp = $self->_retrieve( 'simbad/sim-script', { 864             submit => 'submit+script', 865             script => $arg{script}, 866             }, 867 4         35 ); 868               869 4 50       233 my $rslt = $resp->content 870             or return; 871               872 4 50       149 unless ( $arg{verbatim} ) { 873 4 50       139 $rslt =~ s/.*?::data:+\s*//sm or croak $rslt; 874             } 875               876             $debug 877 4 50       36 and warn "Debug - result:\n$rslt "; 878               879 4 50       48 HAVE_DOUBLE_ENCODED 880             and $rslt = XML::DoubleEncodedEntities::decode ($rslt); 881 4 50       43 if ( my $parser = $self->_get_parser( $arg{parser} ) ) { 882 4 50       21 $debug 883             and warn "Debug - Parser $arg{parser}"; 884             ## $rslt =~ s/.*?::data:+.?$//sm or croak $rslt; 885 4         26 my @rslt = $parser->($rslt); 886             $debug 887 4 50       21 and eval { ## no critic (RequireCheckingReturnValueOfEval) 888 0         0 require YAML; 889 0         0 warn "Debug - Parsed to:\n", YAML::Dump( \@rslt ), ' '; 890             }; 891 4 50       108 return wantarray ? @rslt : \@rslt; 892             } else { 893 0 0       0 $debug 894             and warn "Debug - No parser for $arg{parser}"; 895 0         0 return $rslt; 896             } 897             } 898             } 899               900             =item $value = $simbad->script_file ($filename); 901               902             This method submits the given script file to SIMBAD, returning the 903             result of the script. Unlike script(), the argument is the name of the 904             file containing the script, not the text of the script. However, if a 905             parser for 'script' has been specified, it will be applied to the 906             output. 907               908             =cut 909               910             sub script_file { 911 2     2 1 7 my ( $self, $file ) = @_; 912               913 2         8 my $url = $self->__build_url( 'simbad/sim-script' ); 914 2         32 my $rqst = POST $url, 915             Content_Type => 'form-data', 916             Content => [ 917             submit => 'submit file', 918             scriptFile => [$file, undef], 919             # May need to specify Content_Type => application/octet-stream. 920             ]; 921 2         22355 my $resp = $self->_retrieve( $rqst ); 922               923 2 50       99 my $rslt = $resp->content or return; 924 2 50       60 unless ($self->get ('verbatim')) { 925 2 50       56 $rslt =~ s/.*?::data:+\s*//sm or croak $rslt; 926             } 927 2 50       21 if (my $parser = $self->_get_parser ('script')) { 928             ## $rslt =~ s/.*?::data:+.?$//sm or croak $rslt; 929             ## $rslt =~ s/\s+//sm; 930 2         13 my @rslt = $parser->($rslt); 931 2 50       43 return wantarray ? @rslt : \@rslt; 932             } else { 933 0         0 return $rslt; 934             } 935               936             } 937               938             =item $simbad->set ($name => $value ...); 939               940             This method sets the value of the given L. More 941             than one name/value pair may be specified. If called as a static method, 942             it sets the default value of the attribute. 943               944             =cut 945               946             { # Begin local symbol block. 947               948             my $ckpn = sub { 949             (looks_like_number ($_[2]) && $_[2] >= 0) 950             or croak "Attribute '$_[1]' must be a non-negative number"; 951             +$_[2]; 952             }; 953               954             my %mutator = ( 955             format => \&_set_hash, 956             parser => \&_set_hash, 957             scheme => \&_set_scheme, 958             url_args => \&_set_hash, 959             ); 960               961             my %transform = ( 962             delay => ($have_time_hires ? 963             $ckpn : 964             sub {+sprintf '%d', $ckpn->(@_) + .5}), 965             format => sub { 966             ## my ( $self, $name, $val, $key ) = @_; 967             my ( $self, undef, $val ) = @_; # Name and key unused 968             if ($val !~ m/\W/ && (my $code = eval { 969             $self->_get_coderef ($val)})) { 970             $val = $code->(); 971             } 972             $val; 973             }, 974             parser => sub { 975             ## my ( $self, $name, $val, $key ) = @_; 976             my ( $self, undef, $val ) = @_; # Name and key unused 977             if (!ref $val) { 978             unless ($val =~ m/::/) { 979             my $pkg = $self->_parse_subroutine_name ($val); 980             $val = $pkg . '::' . $val; 981             } 982             $self->_get_coderef ($val); # Just to see if we can. 983             } elsif ( CODE_REF ne ref $val ) { 984             croak "Error - $_[1] value must be scalar or code reference"; 985             } 986             $val; 987             }, 988             ); 989               990             foreach my $key (keys %static) { 991             $transform{$key} ||= sub {$_[2]}; 992             $mutator{$key} ||= sub { 993             my $hash = ref $_[0] ? $_[0] : \%static; 994             $hash->{$_[1]} = $transform{$_[1]}->(@_) 995             }; 996             } 997               998             sub set { 999 15     15 1 70 my ($self, @args) = @_; 1000 0         0 croak "Error - First argument must be an @{[__PACKAGE__]} object" 1001 15 50       54 unless eval {$self->isa(__PACKAGE__)};   15         101   1002 15         74 while (@args) { 1003 48         86 my $name = shift @args; 1004             croak "Error - Attribute '$name' is unknown" 1005 48 50       121 unless exists $mutator{$name}; 1006 48         183 $mutator{$name}->($self, $name, shift @args); 1007             } 1008 14         54 return $self; 1009             } 1010               1011             sub _set_hash { 1012 13     13   40 my ($self, $name, $value) = @_; 1013 13 50       33 my $hash = ref $self ? $self : \%static; 1014 13 100       35 unless (ref $value) { 1015 4 50       32 $value = {$value =~ m/=/ ? 1016             split ('=', $value, 2) : ($value => undef)}; 1017             } 1018 13 50       37 $hash->{$name} = {} if $value->{clear}; 1019 13         25 delete $value->{clear}; 1020 13         55 foreach my $key (keys %$value) { 1021 22         43 my $val = $value->{$key}; 1022 22 50       55 if (!defined $val) {     100           1023 0         0 delete $hash->{$name}{$key}; 1024             } elsif ($val) { 1025             $hash->{$name}{$key} = 1026 10         54 $transform{$name}->($self, $name, $value->{$key}, $key); 1027             } else { 1028 12         32 $hash->{$name}{$key} = ''; 1029             } 1030             } 1031 13         45 return; 1032             } 1033               1034             sub _set_scheme { 1035 6     6   25 my ( $self, $name, $value ) = @_; 1036 6 100       22 if ( my $msg = _is_scheme_valid( $value ) ) { 1037 1         250 croak $msg; 1038             } 1039 5 50       77 my $hash = ref $self ? $self : \%static; 1040 5         25 $hash->{$name} = lc $value; 1041 5         18 return; 1042             } 1043               1044             } # End local symbol block. 1045               1046             =item $value = $simbad->url_query ($type => ...) 1047               1048             This method performs a query by URL, returning the results. The type 1049             is one of: 1050               1051             id = query by identifier, 1052             coo = query by coordinates, 1053             ref = query by references, 1054             sam = query by criteria. 1055               1056             The arguments depend on on the type, and are documented at 1057             L. They are 1058             specified as name => value. For example: 1059               1060             $simbad->url_query (id => 1061             Ident => 'Arcturus', 1062             NbIdent => 1 1063             ); 1064               1065             Note that in an id query you must specify 'Ident' explicitly. This is 1066             true in general, because it is not always possible to derive the first 1067             argument name from the query type, and consistency was chosen over 1068             brevity. 1069               1070             The output.format argument can be defaulted based on the object's type 1071             setting as follows: 1072               1073             txt becomes 'ASCII', 1074             vo becomes 'VOTable'. 1075               1076             Any other value is passed verbatim. 1077               1078             If the query succeeds, the results will be passed to the appropriate 1079             parser if any. The reverse of the above translation is done to determine 1080             the appropriate parser, so the 'vo' parser (if any) is called if 1081             output.format is 'VOTable', and the 'txt' parser (if any) is called if 1082             output.format is 'ASCII'. If output.format is 'HTML', you will need to 1083             explicitly set up a parser for that. 1084               1085             The type of HTTP interaction depends on the setting of the L 1086             attribute: if true a POST is done; otherwise all arguments are tacked 1087             onto the end of the URL and a GET is done. 1088               1089             =cut 1090               1091             { # Begin local symbol block. 1092               1093             my %type_map = ( # Map SOAP type parameter to URL output.format. 1094             txt => 'ASCII', 1095             vo => 'VOTable', 1096             ); 1097             my %type_unmap = reverse %type_map; 1098               1099             # Perl::Critic objects to the use of @_ (rather than values 1100             # unpacked from it) but the parity check lets me give a less 1101             # unfriendly error message. CAVEAT: do NOT modify the contents 1102             # of @_, since this will be seen by the caller. Modifying @_ 1103             # itself is fine. 1104             sub url_query { ## no critic (RequireArgUnpacking) 1105 0 0   0 1 0 @_ % 2 and croak < 1106             Error - url_query needs an even number of arguments after the query 1107             type. 1108             eod 1109 0         0 my ($self, $query, %args) = @_; 1110             ### my $debug = $self->get ('debug'); 1111 0         0 my $dflt = $self->get ('url_args'); 1112 0         0 foreach my $key (keys %$dflt) { 1113 0 0       0 exists ($args{$key}) or $args{$key} = $dflt->{$key}; 1114             } 1115 0 0       0 unless ($args{'output.format'}) { 1116 0         0 my $type = $self->get ('type'); 1117 0   0     0 $args{'output.format'} = $type_map{$type} || $type; 1118             } 1119 0         0 my $resp = $self->_retrieve( "simbad/sim-$query", \%args ); 1120               1121 0         0 my $rslt = $resp->content(); 1122               1123 0 0       0 HAVE_DOUBLE_ENCODED 1124             and $rslt = XML::DoubleEncodedEntities::decode( $rslt ); 1125               1126 0         0 my $parser; 1127 0 0       0 if (my $type = $type_unmap{$args{'output.format'}}) { 1128 0         0 $parser = $self->_get_parser ($type); 1129 0 0       0 return wantarray ? ($parser->($rslt)) : [$parser->($rslt)]     0           1130             if $parser; 1131             } 1132               1133 0         0 return $rslt; 1134             } 1135               1136             } # End local symbol block. 1137               1138             ######################################################################## 1139             # 1140             # Utility routines 1141             # 1142               1143             # __build_url 1144             # 1145             # Builds a URL based on the currently-set scheme and server, and 1146             # the fragment provided as an argument. If the fragment is an 1147             # HTTP::Request object it is simply returned. 1148               1149             sub __build_url { 1150 17     17   14115 my ( $self, $fragment ) = @_; 1151 17 100       70 defined $fragment 1152             or $fragment = ''; 1153 17 100       40 eval { $fragment->isa( 'HTTP::Request' ) }   17         195   1154             and return $fragment; 1155 15         66 $fragment =~ s< \A / ><>smx; # Defensive programming 1156 15         96 return sprintf '%s://%s/%s', $self->get( 'scheme' ), 1157             $self->get( 'server' ), $fragment; 1158             } 1159               1160             # _callers_caller(); 1161             # 1162             # Returns the name of the subroutine that called the caller. 1163             # Results undefined if not called from a subroutine nested at 1164             # least two deep. 1165               1166             sub _callers_caller { 1167 0     0   0 my $inx = 1; 1168 0         0 my $caller; 1169 0         0 foreach ( 1 .. 2 ) { 1170 0         0 do { 1171 0         0 $caller = ( caller $inx++ )[3] 1172             } while '(eval)' eq $caller; 1173             } 1174 0         0 return $caller; 1175             } 1176               1177             # $self->_delay 1178             # 1179             # Delays the desired amount of time before issuing the next 1180             # query. 1181               1182             { 1183             my %last; 1184             sub _delay { 1185 9     9   23 my $self = shift; 1186 9   100     59 my $last = $last{$self->{server}} ||= 0; 1187 9 100       109 if ((my $delay = $last + $self->{delay} - time) > 0) { 1188 6         16954968 sleep ($delay); 1189             } 1190 9         269 return ($last{$self->{server}} = time); 1191             } 1192             } 1193               1194             # $self->_deprecation_notice( $type, $name ); 1195             # 1196             # This method centralizes deprecation. Type is 'attribute' or 1197             # 'method'. Deprecation is driven of the %deprecate hash. Values 1198             # are: 1199             # false - no warning 1200             # 1 - warn on first use 1201             # 2 - warn on each use 1202             # 3 - die on each use. 1203             # 1204             # $self->_deprecation_in_progress( $type, $name ) 1205             # 1206             # This method returns true if the deprecation is in progress. In 1207             # practice this means the %deprecate value is defined. 1208             # This is currently unused and commented out 1209               1210             { 1211               1212             my %deprecate = ( 1213             method => { 1214             query => 2, 1215             }, 1216             ); 1217               1218             sub _deprecation_notice { 1219 0     0   0 my ( undef, $type, $name, $repl ) = @_; # Invocant unused 1220 0 0       0 $deprecate{$type} or return; 1221 0 0       0 $deprecate{$type}{$name} or return; 1222             my $msg = sprintf 'The %s %s is %s', $name, $type, 1223 0 0       0 $deprecate{$type}{$name} > 2 ? 'removed' : 'deprecated'; 1224 0 0       0 defined $repl 1225             and $msg .= "; use $repl instead"; 1226 0 0       0 $deprecate{$type}{$name} >= 3 1227             and croak( $msg ); 1228 0 0       0 warnings::enabled( 'deprecated' ) 1229             and carp( $msg ); 1230             $deprecate{$type}{$name} == 1 1231 0 0       0 and $deprecate{$type}{$name} = 0; 1232 0         0 return; 1233             } 1234               1235             =begin comment 1236               1237             sub _deprecation_in_progress { 1238             my ( undef, $type, $name ) = @_; # Invocant unused 1239             $deprecate{$type} or return; 1240             return defined $deprecate{$type}{$name}; 1241             } 1242               1243             =end comment 1244               1245             =cut 1246               1247             } 1248               1249             # $ref = $self->_get_coderef ($string) 1250             # 1251             # Translates the given string into a code reference, loading 1252             # modules if needed. If the string is not a fully-qualified 1253             # subroutine name, it is assumed to be in the namespace of 1254             # the first caller not in this namespace. Failed loads are 1255             # cached so that they will not be tried again. 1256               1257             { 1258               1259             sub _get_coderef { 1260 10     10   73 my $self = shift; 1261 10         38 my $parser = shift; 1262 10 50 33     80 if ($parser && !ref $parser) { 1263 10         60 my ($pkg, $code) = 1264             $self->_parse_subroutine_name ($parser); 1265 10 50 33     122 unless (($parser = $pkg->can ($code)) || !$self->get ('autoload')) { 1266 0         0 _load_module ($pkg); 1267 0         0 $parser = $pkg->can ($code); 1268             } 1269 10 50       60 $parser or croak "Error - ${pkg}::$code undefined"; 1270             } 1271 10         56 return $parser; 1272             } 1273               1274             } 1275               1276             # $parser = $self->_get_parser ($type) 1277               1278             # returns the code reference to the parser for the given type of 1279             # data, or false if none. An exception is thrown if the value 1280             # is a string which does not specify a defined subroutine. 1281               1282             sub _get_parser { 1283 6     6   32 my ($self, $type) = @_; 1284 6         37 return $self->_get_coderef ($self->get ('parser')->{$type}); 1285             } 1286               1287             # Return false if the argument is a URI scheme we know how to deal with; 1288             # otherwise return an error message. The optional second argument is a 1289             # template for the message, with a single '%s' that gets the actual 1290             # value of the scheme. 1291               1292             { 1293             my %supported; 1294               1295             BEGIN { 1296 5     5   32 %supported = map { $_ => 1 } qw{ http https };   10         5023   1297             } 1298               1299             sub _is_scheme_valid { 1300 12     12   36 my ( $scheme, $msg ) = @_; 1301 12   100     42 $scheme = lc( $scheme || '' ); 1302 12   100     54 $msg ||= q; 1303 12 100       64 $supported{$scheme} 1304             or return sprintf $msg, $scheme; 1305 11 100       49 LWP::Protocol::implementor( $scheme ) 1306             and return; 1307 1         292 $msg .= "; have you installed LWP::Protocol::$scheme?"; 1308 1         7 return sprintf $msg, $scheme; 1309             } 1310             } 1311               1312             # $rslt = _load_module($name) 1313             # 1314             # This subroutine loads the named module using 'require'. It 1315             # croaks if the load fails, or returns the result of the 1316             # 'require' if it succeeds. Results are cached, so subsequent 1317             # calls simply do what the first one did. 1318               1319             { # Local symbol block. Oh, for 5.10 and state variables. 1320             my %error; 1321             my %rslt; 1322             sub _load_module { 1323 10     10   31 my ($module) = @_; 1324 10 50       48 exists $error{$module} and croak $error{$module}; 1325 10 50       39 exists $rslt{$module} and return $rslt{$module}; 1326 10         713 $rslt{$module} = eval "require $module"; 1327 10 50       1703 $@ and croak ($error{$module} = $@); 1328 0         0 return $rslt{$module}; 1329             } 1330             } # End local symbol block. 1331               1332             # $ua = _get_user_agent (); 1333             # 1334             # This subroutine returns an LWP::UserAgent object with its agent 1335             # string set to the default, with our class name and version 1336             # appended in parentheses. 1337               1338             sub _get_user_agent { 1339 9     9   82 my $ua = LWP::UserAgent->new ( 1340             ); 1341             ## $ua->agent ($ua->_agent . ' (' . __PACKAGE__ . ' ' . $VERSION . 1342             ## ')'); 1343 9         6386 $ua->agent (&agent); 1344 9         743 return $ua; 1345             } 1346               1347             # ($package, $subroutine) = $self->_parse_subroutine_name ($name); 1348             # 1349             # This method parses the given name, and returns the package name 1350             # in which the subroutine is defined and the subroutine name. If 1351             # the $name is a bare subroutine name, the package is the calling 1352             # package unless that package contains no such subroutine but 1353             # $self->can($name) is true, in which case the package is 1354             # ref($self). 1355             # 1356             # If called in scalar context, the package is returned. 1357               1358             sub _parse_subroutine_name { 1359 13     13   45 my ($self, $parser) = @_; 1360 13         63 my @parts = split '::', $parser; 1361 13         45 my $code = pop @parts; 1362 13         50 my $pkg = join '::', @parts; 1363 13 100       48 unless ($pkg) { 1364 4         14 my %tried = (__PACKAGE__, 1); 1365 4         9 my $inx = 1; 1366 4         40 while ($pkg = (caller ($inx++))[0]) { 1367 30 100       135 next if $tried{$pkg}; 1368 12         47 $tried{$pkg} = 1; 1369 12 50       253 last if $pkg->can ($code); 1370             } 1371 4 50 33     61 $pkg = ref $self if !$pkg && $self->can ($code); 1372 4 50       14 defined $pkg or croak < 1373             Error - '$parser' yields undefined package name. 1374             eod 1375 4         21 @parts = split '::', $pkg; 1376             } 1377 13 100       92 return wantarray ? ($pkg, $code) : $pkg; 1378             } 1379               1380             # my $resp = $self->_retrieve( $fragment, \%args ); 1381             # 1382             # Build a URL from the contents of the 'scheme' and 'server' 1383             # attributes, and the given fragment, and retrieve the data from 1384             # that URL. The \%args argument is optional. 1385             # 1386             # The return is an HTTP::Response object. If the response is 1387             # indicates that the request is unsuccessful we croak with the URL 1388             # (if that can be retrieved) and the status line. 1389             # 1390             # The details depend on the arguments and the state of the 1391             # invocant as follows: 1392             # 1393             # If $url is an HTTP::Request object, it is executed and the 1394             # response returned. Otherwise 1395             # 1396             # If \%args is present and not empty, and the 'post' attribute is 1397             # true, an HTTP post() request is done to the URL, sending the 1398             # data. Otherwise 1399             # 1400             # If there are arguments they are appended to the URL, and an HTTP 1401             # get() is done to the URL. 1402               1403             sub _retrieve { 1404 9     9   39 my ($self, $fragment, $args) = @_; 1405 9         58 my $url = $self->__build_url( $fragment ); 1406 9   100     77 $args ||= {}; 1407 9         60 my $debug = $self->get ('debug'); 1408 9         57 my $ua = _get_user_agent (); 1409 9         73 $self->_delay (); 1410 9         31 my $resp; 1411 9 100 66     42 if (eval {$url->isa('HTTP::Request')}) {   9 100       326   1412 2 50       27 $debug 1413             and print 'Debug ', _callers_caller(), 'executing ', 1414             $url->as_string, "\n"; 1415 2         39 $resp = $ua->request ($url); 1416             } elsif ($self->get ('post') && %$args) { 1417 4 50       19 if ($debug) { 1418 0         0 print 'Debug ', _callers_caller(), " posting to $url\n"; 1419 0         0 foreach my $key (sort keys %$args) { 1420 0         0 print " $key => $args->{$key}\n"; 1421             } 1422             } 1423 4         56 $resp = $ua->post ($url, $args); 1424             } else { 1425 3         13 my $join = '?'; 1426 3         27 foreach my $key (sort keys %$args) { 1427             $url .= $join . _escape_uri( $key ) . '=' . _escape_uri ( 1428 0         0 $args->{$key} ); 1429 0         0 $join = '&'; 1430             } 1431             $debug 1432 3 50       12 and print 'Debug ', _callers_caller(), " getting from $url\n"; 1433 3         31 $resp = $ua->get( $url ); 1434             } 1435 9 50       1514763 $debug 1436             and print 'Debug - request: ', $resp->request()->as_string(), "\n"; 1437               1438 9 50       93 $resp->is_success() 1439             and return $resp; 1440               1441 0 0         my $rq = $resp->request() 1442             or croak $resp->status_line(); 1443 0           my $u = $rq->uri(); 1444 0           croak "$u: ", $resp->status_line(); 1445             } 1446               1447             1; 1448               1449             __END__