Object Plugins
This document discusses how to extend the various ``details'' pages in CMap (map_details, feature_details, etc) to add custom data drawn from external resources.
As has been discussed in the ``attributes'' document, CMap stores almost no biological information about the various objects (map sets, species, features, etc.). The ``attributes'' system does allow the curator to define any number of arbitrary name/value pairs of data (e.g., a ``GenBank ID'' for a feature or an ``NCBI Taxon ID'' for a species), and these attributes can be used by the cross-reference system to create novel and powerful dbxrefs. However, storing attributes such as these in all likelihood duplicates information stored elsewhere, which is almost never a Good Thing (tm).
As a solution, CMap includes a way to execute user-defined code that can manipulate the objects used in the construction of a ``details'' page (e.g., map sets have the ``map_set_info'' page, features have the ``feature'' page). As with most of the other user-configurable items in CMap, the user will define this code in the database-specific configuration files. The code must be written in Perl (the language in which CMap is written).
There are two methods for defining the custom code, ``in-line'' in the config file or in a separate module. The following example illustrates both of these methods.
<object_plugin> map_set_info <<EOF sub { my $map_set = shift; $map_set->{'foo'} = 'bar'; push @{ $map_set->{'xrefs'} }, { xref_name => 'Google', xref_url => "http://www.google.com/?q=". $map_set->{'map_set_short_name'} };
push @{ $map_set->{'attributes'} }, { attribute_name => 'Favorite Color', attribute_value => 'Blue. No, red! Ahhhhh!', }; } EOF feature Gramene::Marker::Marker2CMap::new </object_plugin>
As is shown above, the user has the ability to directly manipulate the given object (in this case, the map set object ``$map_set''). In all cases, the ``object'' will be a Perl hash reference data structure. This object is used by the template for the ``details'' page. This hashref will always be the first (and only) argument to the plugin code. Your code should alter the hashref to add new cross-references (``xrefs''), attributes, or even new fields.
The above example also shows adding a new field ``foo'' with the value ``bar.'' If you wish this value to be displayed on the ``map_set_info'' page, you would have to alter the ``map_set_info.tmpl'' template. It's probably much easier to simply add to hashref's ``attributes'' or ``xrefs'' as all the templates are designed to show these and will have to be altered to display other fields (and your changes could be lost the next time you update CMap).
The ``attributes'' and ``xrefs'' fields of the hashref are themselves arrayrefs of hashrefs. If that sounds scary, it's really not. In short, the thing you add to it (using ``push'') should be a hashref with the following fields:
attribute attribute_name : The type of attribute, e.g., the phrase "GenBank ID" attribute_value: The data, e.g., the actual GenBank ID
xref xref_name: The text that user will click on for the link xref_url : The location where the user will go after clicking
As long as you define the ``attributes'' and ``xrefs'' with these fields and copy the above syntax for ``push''ing them onto the appropriate fields (and all hashrefs will have these), your code should work just fine.
Following is more discussion on defining the plugin code.
Here is an example of the ``Gramene::Marker::Marker2CMap'' module:
package Gramene::Marker::Marker2CMap;
use strict; use Gramene::Marker::DB;
sub new { my $feature = shift; my $mdb = Gramene::Marker::DB->new;
my @markers; for my $name ( $feature->{'feature_name'}, ( map { $_->{'alias'} } @{ $feature->{'aliases'} } ) ) { my $found = $mdb->marker_search( marker_name => $name ) or next; push @markers, @$found; }
my $xref_url = '/db/markers/marker_view?marker_id='; for my $m ( @markers ) { push @{ $feature->{'xrefs'} }, { xref_name => "View "$m->{'name'}" in Marker DB", xref_url => $xref_url . $m->{'marker_id'}, }; } } 1;
Notice that it's simply a standard Perl package, starting with the proper ``package'' declaration and returning a ``true'' value at the end, ``1;'' in this case. The ``Gramene::Marker::DB'' module is used to search for the CMap feature in the marker name, looking with the feature's primary name and any aliases. For each marker (if any) that are found, a new cross-reference is created using the a relative URL (``/db/markers...'') and defining an informative link text and proper URL.
CMap could also define new attributes for the feature based upon the field names and data from the markers found in the marker database, thereby allowing the CMap user full access to the marker information without leaving the CMap interface and without duplicating the marker data as CMap attributes.
The following are the names to use for each object (they are the same as the ``handler'' for the object). For each hashref, you can always add (``push'') attributes and xrefs. If you wish to have the specific ``details'' page display a visually structured dump of the object's data structure, you can use this snippet (here for a ``feature''):
<object_plugin> feature <<EOF sub { use Data::Dumper; my $obj = shift; push @{ $obj->{'attributes'} }, { attribute_name => 'Dump', attribute_value => '<pre>'.Dumper($obj).'</pre>' }; } EOF </object_plugin>
This will cause a new attribute called ``Dump'' to be created with a value of the return of the standard Perl module Data::Dumper's ``Dumper'' method in ``PRE''-formatted HTML tags.
For each object type, following are the plugin names to use (in parentheses) and the hashref's fields you can use:
species_common_name species_full_name is_relational_map map_set_acc map_set_id map_set_name map_type map_type_acc map_units object_id published_on map_set_short_name species_acc species_id
map_acc map_name map_set_acc map_set_name map_type_acc map_units object_id species_acc species_common_name start map_start stop map_stop
aliases attributes feature_acc feature_id feature_name feature_type feature_type_acc is_landmark map_acc map_id map_name map_set_acc map_set_id map_set_name map_type_acc map_units object_id species_acc species_id species_common_name feature_start feature_stop
color feature_type feature_type_acc shape
color display_order is_relational_map map_type map_type_acc map_units shape width
evidence_type evidence_type_acc line_color rank
species_common_name display_order species_full_name species_acc species_id
To decrease the amount of redundant configuration information in the config files, an include statement is allowed. This imports the contents of another file into the config file and is read as though it were a part of that config file. An example of the syntax follows.
<<include common_types.cfg>> <<include object_plugins.cfg>>
In the above example, a file containing the common feature, map and evidence type configurations are stored in a file that can be used by multiple config files. Likewise for object plugins.
It is important to note that while additional feature, map and evidence types can be added in the individual config files, including an object plugin file AND defining object plugins in the file will cause CMap to break. This is because the types are self contained XML entities whereas the all plugins must be defined in the <object_plugin> object.
Also, be aware that it is posible to create infinite loops by including files that include each other.
Ken Youens-Clark <kclark@cshl.edu>.