package ACHserver; # # This is a SAS Component # use strict; use LWP::UserAgent; use YAML; =head1 Annotation Clearinghouse Server Helper Object =head2 Description This module is used to call the Annotation Clearinghouse Server, which is a special-purpose server for assertion data from the Sapling database. Each Annotation Clearinghouse Server function corresponds to a method of this object. This package deliberately uses no internal SEED packages or scripts, only common PERL modules. The fields in this object are as follows. =over 4 =item server_url The URL used to request data from the subsystem server. =item ua The user agent for communication with the server. =item singleton Indicates whether or not results are to be returned in singleton mode. In singleton mode, if the return document is a hash reference with only one entry, the entry value is returned rather than the hash. =back =cut =head3 new my $ach = ACHserver->new(%options); Construct a new ACHserver object. The following options are supported. =over 4 =item url URL for the ACH server. This option may be used to redirect requests to a test version of the server, or to an older server script. =item singleton If TRUE, results from methods will be returned in singleton mode. In singleton mode, if a single result comes back, it will come back as a scalar rather than as a hash value accessible via an incoming ID. =back =cut sub new { # Get the parameters. my ($class, %options) = @_; # Get the options. my $url = $options{url} || "http://servers.nmpdr.org/ach/server.cgi"; my $singleton = $options{singleton} || 0; # Create the fields of the object. my $server_url = $url; my $ua = LWP::UserAgent->new(); # Create the SAPserver object. my $retVal = { server_url => $server_url, ua => $ua, singleton => $singleton, }; # Bless and return it. bless $retVal, $class; return $retVal; } =head2 Public Methods All L are also methods of this object. =head3 AUTOLOAD my $result = $ach->method(%args); Call a function on the server. Any method call on this object (other than the constructor) is translated into a request against the server. This enables us to add new server functions without requiring an update to this module. The parameters are specified as a hash, and the result is a scalar or object reference. If an error occurred, we will throw an exception. =cut # This variable will contain the method name. our $AUTOLOAD; sub AUTOLOAD { # Get the parameters. We do some fancy dancing to allow the user to pass # in a hash, a list, or a hash reference. my $self = shift @_; my $args = $_[0]; if (defined $args) { if (scalar @_ gt 1) { # Here we have multiple arguments. We check the first one for a # leading hyphen. if ($args =~ /^-/) { # This means we have hash-form parameters. my %args = @_; $args = \%args; } else { # This means we have list-form parameters. my @args = @_; $args = \@args; } } else { # Here we have a single argument. If it's a scalar, we convert it # to a singleton list. if (! ref $args) { $args = [$args]; } } } # Declare the return variable. my $retVal; # Get the method name. my $function = $AUTOLOAD; # Strip off the stuff before the method name. $function =~ s/.+:://; # Compute the argument document. my $argString = YAML::Dump($args); # Get our user agent. my $ua = $self->{ua}; # Request the function from the server. my $response = $ua->post($self->{server_url}, [function => $function, args => $argString, source => __PACKAGE__ ]); # Get the response content. my $content = $response->content; if (! $response->is_success) { die "Server error " . $response->status_line . "\n$content"; } else { $retVal = YAML::Load($content); # Figure out what we got back. my $returnType = ref $retVal; if ($returnType) { if ($returnType eq 'ErrorDocument') { # Here an error occurred, so we throw an exception using the # error message. die $retVal->{message}; } elsif ($self->{singleton} && $returnType eq 'HASH' && scalar(keys %$retVal) <= 1) { # Here we're in singleton mode and we got a single result, # so we dereference a bit to make it easier for the user # to access it. ($retVal) = values %$retVal; } } } # Return the result. return $retVal; } =head3 DESTROY $ss->DESTROY(); This method has no function. It's purpose is to keep the destructor from being caught by the autoload processing. =cut sub DESTROY { } 1;