NAME
Webservice::InterMine::Cookbook::Recipe7 - Extending Webservice::InterMine
SYNOPSIS
package WriteOutYaml;
use Moose::Role;
use YAML::Syck qw(Dump);
requires qw(results);
sub results_to_yaml {
my $self = shift;
my %args = @_;
my $results = $self->results(%args);
return Dump($results);
}
1;
# Later, in a nearby script
use Webservice::InterMine ('www.flymine.org');
my $query = Webservice::InterMine->new_query(with => ['WriteOutYaml']);
# Specifying a name and a description is purely optional
$query->name('Tutorial 7 Query');
$query->description('All genes involved in biosynthetic processes');
$query->add_view(qw/
Gene.name
Gene.primaryIdentifier
Gene.goAnnotation.ontologyTerm.name
/);
$query->add_constraint(
path => 'Gene.goAnnotation.ontologyTerm.name',
op => 'CONTAINS',
value => 'biosynthetic process',
);
print $query->results_to_yaml(as => 'hashrefs');
DESCRIPTION
Since scripted queries represent an attempt to automate commonly repeated workflows, it is sensible to provide the opportunity to further eliminate any repetitive code you may end up writing. You may find that your personal use of queries and their results follows a predictable pattern, and you end up writing your code in an almost cookie-cutter style. When that happens, it is time to refactor out the commonalities into reusable chunks of code, ie. as modules.
Webservice::InterMine is designed to incorporate any additions you may want to write in as simply as possible. Being object orientated, it is possible to subclass and reimplement the entire Webservice::InterMine suite. However, a simpler approach is to use roles(1). The Webservice::InterMine modules are written using the Moose MOP system, and you can specify additional roles to be composed into the query or result iterator objects from the constructors.
In the example above we imagine that a user frequently uses YAML(2) to serialise the results data to files. Rather than repeating the same chunk of code in each script, that functionality has been packaged up in a 'role', and then passed to the query constructor:
my $query = Webservice::InterMine->new_query(with => [$role]);
# note that we can pass a list of roles
my $query = Webservice::InterMine->new_query(with => [$role1, $role2]);
Now the query has all the extra functionality that the role provides, so here the query knows automatically how to serialise itself to a file in the YAML format.
$query->dump_yaml_to_file($file, as => $format);
Note that the result format is passed along to the results method, so we have exactly the same behaviour here as the default results method - this is simply a wrapper for the frequently repeated chunk of code.
This can also be done with ResultIterators:
package HTMLTableRow;
use Moose::Role;
requires qw(arrayref);
sub html_row {
my $self = shift;
my $row = $self->arrayref;
return unless (defined $row);
my $output = '<tr>';
for (@$row) {
$output .= "<td>$_</td>";
}
$output .= '</tr>';
return $output;
}
1;
# Later, in a nearby script:
my $ri = $query->results_iterator(with => ['HTMLTableRow']);
die $ri->status_line unless $ri->is_success;
print '<table>';
while (my $row = $ri->html_row) {
print $row;
print '</table>';
This shows how we can provide a completely new method to ResultIterators to make the production of HTML tables from query results trivial. It also illustrates another advantage of having low level access to the iterator itself, as it allows you to define in great detail how you want your results returned to you.
Combining these two kinds of roles can produce some radically new behaviour:
package HTMLTable;
use Moose::Role;
requires qw(results_iterator views);
sub results_as_html_table {
my $self = shift;
my $ri = $self->results_iterator(with => ['HTMLTableRow']);
die $ri->status_line unless $ri->is_success;
my $table_string = '<table>';
$table_string .=
"<tr>".
join('', map {"<td>$_</td>"} $self->view).
"</tr>";
while (my $row = $ri->html_row) {
$table_string .= $row;
}
$table_string .= '</table>';
return $table_string;
}
1;
# Later, in a nearby script
my $query = Webservice::InterMine->new_query(with => ['HTMLTable']);
# ... define the query here
print $query->results_as_html_table;
CONCLUSION
The Perl API can be dynamically extended using Moose::Roles, with two of the main objects, the query, and the result iterator, allowing roles to be composed onto them when constructed. This can increase code reuse, and thus maintainability and flexibility.
FURTHER READING
see examples of roles that can be applied to queries in Webservice::InterMine/Query/Roles/Extra/
FOOTNOTES
(1) Roles are a key feature of the Moose Object Orientated Framework (Moose). Essentially they are composable units of behaviour that make up a class or object, similar to Ruby's mixins, scala's traits, or Java's interfaces (except they have code too). For a discussion of roles in Perl see: http://www.modernperlbooks.com/mt/2009/04/the-why-of-perl-roles.html and http://perlbuzz.com/2010/07/why-roles-in-perl-are-awesome.html.
AUTHOR
Alex Kalderimis <dev@intermine.org>
BUGS
Please report any bugs or feature requests to dev@intermine.org
.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Webservice::InterMine
You can also look for information at:
InterMine
Documentation
COPYRIGHT AND LICENSE
Copyright 2006 - 2010 FlyMine, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.