Ace::Object(3pm)

✨ Ace::Object(3pm) β€” Manipulate Ace Data Objects ✨

bestie, we are so in the ACEDB object zone right now πŸ’…

πŸŽ€ NAME πŸŽ€

Ace::Object β€” Manipulate Ace Data Objects.

that's the name, that's the whole vibe, no cap.


🌸 SYNOPSIS 🌸

okay so here's the entire mood in code form ✨

# open database connection and get an object
use Ace;
$db = Ace->connect(-host => 'beta.crbm.cnrs-mop.fr',
                   -port => 20000100);
$sequence  = $db->fetch(Sequence => 'D12345');

# Inspect the object
$r    = $sequence->at('Visible.Overlap_Right');
@row  = $sequence->row;
@col  = $sequence->col;
@tags = $sequence->tags;

# Explore object substructure
@more_tags = $sequence->at('Visible')->tags;
@col       = $sequence->at("Visible.$more_tags[1]")->col;

# Follow a pointer into database
$r     = $sequence->at('Visible.Overlap_Right')->fetch;
$next  = $r->at('Visible.Overlap_left')->fetch;

# Classy way to do the same thing
$r     = $sequence->Overlap_right;
$next  = $sequence->Overlap_left;

# Pretty-print object
print $sequence->asString;
print $sequence->asTabs;
print $sequence->asHTML;

# Update object
$sequence->replace('Visible.Overlap_Right',$r,'M55555');
$sequence->add('Visible.Homology','GR91198');
$sequence->delete('Source.Clone','MBR122');
$sequence->commit();

# Rollback changes
$sequence->rollback()

# Get errors
print $sequence->error;

πŸ’– DESCRIPTION πŸ’–

Ace::Object is the base class for objects returned from ACEDB databases. Currently there is only one type of Ace::Object, but this may change in the future to support more interesting object-specific behaviors. lowkey mysterious, highkey useful πŸ¦‹

Using the Ace::Object interface, you can explore the internal structure of an Ace::Object, retrieve its content, and convert it into various types of text representation. You can also fetch a representation of any object as a GIF image. πŸ“Έ

If you have write access to the databases, add new data to an object, replace existing data, or kill it entirely. yes bestie, you can absolutely delete the whole thing πŸ’€ You can also create a new object de novo and write it into the database. main character creation energy ✨

For information on connecting to ACEDB databases and querying them, see Ace.


πŸ‘‘ ACEDB::OBJECT METHODS πŸ‘‘

The structure of an Ace::Object is very similar to that of an Acedb object. It is a tree structure like this one (an Author object):

        Thierry-Mieg J->Full_name ->Jean Thierry-Mieg
                         |
                        Laboratory->FF
                         |
                        Address->Mail->CRBM duCNRS
                         |        |     |
                         |        |    BP 5051
                         |        |     |
                         |        |    34033 Montpellier
                         |        |     |
                         |        |    FRANCE
                         |        |
                         |       E_mail->mieg@kaa.cnrs-mop.fr
                         |        |
                         |       Phone ->33-67-613324
                         |        |
                         |       Fax   ->33-67-521559
                         |
                        Paper->The C. elegans sequencing project
                                |
                               Genome Project Database
                                |
                               Genome Sequencing
                                |
                                How to get ACEDB for your Sun
                                |
                               ACEDB is Hungry

Each object in the tree has two pointers, a "right" pointer to the node on its right, and a "down" pointer to the node beneath it. Right pointers are used to store hierarchical relationships, such as Address->Mail->E_mail, while down pointers are used to store lists, such as the multiple papers written by the Author.

Each node in the tree has a type and a name. Types include integers, strings, text, floating point numbers, as well as specialized biological types, such as "dna" and "peptide." Another fundamental type is "tag," which is a text identifier used to label portions of the tree. Examples of tags include "Paper" and "Laboratory" in the example above. iykyk πŸ’…

In addition to these built-in types, there are constructed types known as classes. These types are specified by the data model. In the above example, "Thierry-Mieg J" is an object of the "Author" class, and "Genome Project Database" is an object of the "Paper" class. An interesting feature of objects is that you can follow them into the database, retrieving further information. For example, after retrieving the "Genome Project Database" Paper from the Author object, you could fetch more information about it, either by following its right pointer, or by using one of the specialized navigation routines described below. obsessed with this tbh 🫢


🌟 new() method 🌟

$object = new Ace::Object($class,$name,$database);
$object = new Ace::Object(-class=>$class,
                          -name=>$name,
                          -db=>database);

You can create a new Ace::Object from scratch by calling the new() routine with the object's class, its identifier and a handle to the database to create it in. ngl, the object won't actually be created in the database until you add() one or more tags to it and commit() it (see below) πŸ’– If you do not provide a database handle, the object will be created in memory only. in-memory baby object, slay ✨

Arguments can be passed positionally, or as named parameters, as shown above.

This routine is usually used internally. See also add_row(), add_tree(), delete() and replace() for ways to manipulate this object.


πŸŽ€ name() method πŸŽ€

$name = $object->name();

Return the name of the Ace::Object. This happens automatically whenever you use the object in a context that requires a string or a number. the way it just knows how to stringify itself 😭 For example:

$object = $db->fetch(Author,"Thierry-Mieg J");
print "$object did not write 'Pride and Prejudice.'\n";

πŸ¦‹ class() method πŸ¦‹

$class = $object->class();

Return the class of the object. The return value may be one of "float," "int," "date," "tag," "txt," "dna," "peptide," and "scalar." (The last is used internally by Perl to represent objects created programmatically prior to committing them to the database.) scalar is literally the Perl internals showing through πŸ’… The class may also be a user-constructed type such as Sequence, Clone or Author. These user-constructed types usually have an initial capital letter.


πŸ’œ db() method πŸ’œ

$db = $object->db();

Return the database that the object is associated with.


⭐ isClass() method ⭐

$bool = $object->isClass();

Returns true if the object is a class (can be fetched from the database).


🌈 isTag() method 🌈

$bool = $object->isTag();

Returns true if the object is a tag.


πŸ“ tags() method πŸ“

@tags = $object->tags();

Return all the top-level tags in the object as a list. In the Author example above, the returned list would be ('Full_name','Laboratory','Address','Paper').

You can fetch tags more deeply nested in the structure by navigating inwards using the methods listed below. deep dive bestie 🧚


πŸ’« right() and down() methods πŸ’«

$subtree = $object->right;
$subtree = $object->right($position);
$subtree = $object->down;
$subtree = $object->down($position);

right() and down() provide a low-level way of traversing the tree structure by following the tree's right and down pointers. Called without any arguments, these two methods will move one step. Called with a numeric argument >= 0 they will move the indicated number of steps (zero indicates no movement).

$full_name = $object->right->right;
$full_name = $object->right(2);

$city = $object->right->down->down->right->right->down->down;
$city = $object->right->down(2)->right(2)->down(2);

If $object contains the "Thierry-Mieg J" Author object, then the first series of accesses shown above retrieves the string "Jean Thierry-Mieg" and the second retrieves "34033 Montpellier." If the right or bottom pointers are NULL, these methods will return undef. watch out for the undef jumpscare 😭

In addition to being somewhat awkard, you will probably never need to use these methods. A simpler way to retrieve the same information would be to use the at() method described in the next section. fr fr, just use at()

The right() and down() methods always walk through the tree of the current object. They do not follow object pointers into the database. Use fetch() (or the deprecated pick() or follow() methods) instead.


πŸ¦‹ at() method πŸ¦‹

$subtree    = $object->at($tag_path);
@values     = $object->at($tag_path);

at() is a simple way to fetch the portion of the tree that you are interested in. It takes a single argument, a simple tag or a path. A simple tag, such as "Full_name", must correspond to a tag in the column immediately to the right of the root of the tree. A path such as "Address.Mail" is a dot-delimited path to the subtree. Some examples are given below.

($full_name)   = $object->at('Full_name');
@address_lines = $object->at('Address.Mail');

The second line above is equivalent to:

@address = $object->at('Address')->at('Mail');

Called without a tag name, at() just dereferences the object, returning whatever is to the right of it, the same as $object->right

If a path component already has a dot in it, you may escape the dot with a backslash, as in:

$s=$db->fetch('Sequence','M4');
@homologies = $s->at('Homol.DNA_homol.yk192f7\.3';

This also demonstrates that path components don't necessarily have to be tags, although in practice they usually are. the backslash escape is giving survivor mode πŸ’…

at() returns slightly different results depending on the context in which it is called. In a list context, it returns the column of values to the right of the tag. However, in a scalar context, it returns the subtree rooted at the tag. To appreciate the difference, consider these two cases:

$name1   = $object->at('Full_name');
($name2) = $object->at('Full_name');

After these two statements run, $name1 will be the tag object named "Full_name", and $name2 will be the text object "Jean Thierry-Mieg", The relationship between the two is that $name1->right leads to $name2. This is a powerful and useful construct, but it can be a trap for the unwary. do not let the scalar context catch you slipping If this behavior drives you crazy, use this construct:

$name1   = $object->at('Full_name')->at();

For finer control over navigation, path components can include optional indexes to indicate navigation to the right of the current path component. Here is the syntax:

$object->at('tag1[index1].tag2[index2].tag3[index3]...');

Indexes are zero-based. An index of [0] indicates no movement relative to the current component, and is the same as not using an index at all. An index of [1] navigates one step to the right, [2] moves two steps to the right, and so on. Using the Thierry-Mieg object as an example again, here are the results of various indexes:

$object = $db->fetch(Author,"Thierry-Mieg J");
$a = $object->at('Address[0]')   --> "Address"
$a = $object->at('Address[1]')   --> "Mail"
$a = $object->at('Address[2]')   --> "CRBM duCNRS"

Okay so here's where it gets spicy 🌢️ β€” in an array context, that last index in the path does something very interesting. It returns the entire column of data K steps to the right of the path, where K is the index. That's what powers the whole "tag[2]" syntax thing, and bestie it is so useful in some situations. Like, check this β€” here's a little fragment that gives you the Thierry-Mieg object's entire address without you having to name every single intervening "Mail", "E_Mail" and "Phone" tag explicitly. No cap, it's a lifesaver 🫢

@address = $object->at('Address[2]');
--> ('CRBM duCNRS','BP 5051','34033 Montpellier','FRANCE',
     'mieg@kaa.cnrs-mop.fr,'33-67-613324','33-67-521559')

Same energy: "tag[3]" hands you the column of data three hops to the right of the tag. And "tag[1]" is literally identical to plain "tag" (no index at all), returning the column immediately to the right. ✨

And "tag[0]"? In an array context there's no special behavior β€” it just always returns the subtree rooted at the indicated tag. That's it, that's the whole vibe.

Internal indices like "Homol[2].BLASTN" also don't get any special array-context treatment. They're always treated as if you called them in a scalar context. Periodt. πŸ’…

Also go look at col() and get() β€” they're your friends here.


🌸 get() method 🌸

$subtree    = $object->get($tag);
@values     = $object->get($tag);
@values     = $object->get($tag, $position);
@values     = $object->get($tag => $subtag, $position);

So get() does a breadth-first search through the object (columns first, then rows, iykyk) looking for the tag you named, and it returns the column of the portion of the subtree that tag points to. Watch this β€” the code fragment returns the value of the "Fax" tag:

($fax_no) = $object->get('Fax');
     --> "33-67-521559"

The list-versus-scalar context semantics are the exact same as in at(). So if you want the scalar value the indicated tag points to, either use list context like the example above κ’° and honestly slay κ’± or dereference it like:

 $fax_no = $object->get('Fax');
     --> "Fax"
 $fax_no = $object->get('Fax')->at;
     --> "33-67-521559"

There's an optional second argument to get(), $position, and it lets you navigate the tree relative to the retrieved subtree. Just like the at() navigational indexes, $position has to be a number greater than or equal to zero. πŸ™Œ In a scalar context, $position moves rightward through the tree. In an array context, $position implements that "tag[2]" behavior we were just talking about.

For example:

 $fax_no = $object->get('Fax',0);
      --> "Fax"

 $fax_no = $object->get('Fax',1);
      --> "33-67-521559"

 $fax_no = $object->get('Fax',2);
      --> undef  # nothing beyond the fax number

 @address = $object->get('Address',2);
      --> ('CRBM duCNRS','BP 5051','34033 Montpellier','FRANCE',
           'mieg@kaa.cnrs-mop.fr,'33-67-613324','33-67-521559')

Super important tho!! 🚨 get() only traverses tags. It will not traverse nodes that aren't tags β€” so, strings, integers, objects? nope, it walks right past them. This is in keeping with the behavior of the Ace query language "show" command. It's giving ✨consistency✨.

Ngl, this restriction can lead to confusing results. 😭 Consider the following object:
 Clone: B0280  Position    Map            Sequence-III  Ends   Left   3569
                                                               Right  3585
                           Pmap           ctg377        -1040  -1024
               Positive    Positive_locus nhr-10
               Sequence    B0280
               Location    RW
               FingerPrint Gel_Number     0
                           Canonical_for  T20H1
                                          K10E5
                           Bands          1354          18

If you try to fetch the left and right positions of the clone this way, it's gonna fail, because the search for the "Left" and "Right" tags can't traverse "Sequence-III" β€” which is an object, not a tag:

  my $left = $clone->get('Left');    # will NOT work
  my $right = $clone->get('Right');  # neither will this one

You have to explicitly step over the non-tag node to make the query work. This syntax will work:

  my $left = $clone->get('Map',1)->get('Left');   # works
  my $left = $clone->get('Map',1)->get('Right');  # works

Or, bestie, you might prefer the tag[2] syntax here:

  my($left,$right) = $clone->get('Map',1)->at('Ends[2]');

There's also a form of get() β€” not used super often, but it exists β€” that lets you stack subtags:

    $locus = $object->get('Positive'=>'Positive_locus');

Only one subtag is allowed. πŸŽ€ And you can follow that with a position if you want to offset from the subtag:

    $locus = $object->get('Positive'=>'Positive_locus',1);

πŸ’€ search() method πŸ’€

This is a deprecated synonym for get(). Like, it still works, but it's giving 2009. Use get().


πŸ‘‘ Autogenerated Access Methods πŸ‘‘

 $scalar = $object->Name_of_tag;
 $scalar = $object->Name_of_tag($position);
 @array  = $object->Name_of_tag;
 @array  = $object->Name_of_tag($position);
 @array  = $object->Name_of_tag($subtag=>$position);
 @array  = $object->Name_of_tag(-fill=>$tag);

This module will autogenerate data access methods for you as needed. πŸͺ„ So if you refer to a method named "Fax" (which doesn't match any of the built-in methods), the code goes and calls the get() method to find a tag named "Fax" and returns its contents. Magic, honestly.

But β€” and this is the plot twist β€” unlike get(), this method will always step into objects. Which means:

   $map = $clone->Map;

returns the Sequence_Map object pointed to by the Clone's Map tag, and not just a pointer to a portion of the Clone tree. So autogenerated methods are functionally equivalent to:

   $map = $clone->get('Map')->fetch;

The scalar context semantics are also slightly different. In a scalar context, the autogenerated function will always move one step to the right. ⭐

The list context semantics? Identical to get(). So if you want to dereference every member of a multivalued tag, you gotta do it yourself, manually:

  @papers = $author->Paper;
  foreach (@papers) {
    my $paper = $_->fetch;
    print  $paper->asString;
  }

You can hand it an optional positional index to rapidly navigate through the tree, or to get that tag[2] behavior. In these next examples, the first two give you the object's Fax number, and the third gives you all data two hops to the right of Address:

     $object   = $db->fetch(Author => 'Thierry-Mieg J');
     ($fax_no) = $object->Fax;
     $fax_no   = $object->Fax(1);
     @address  = $object->Address(2);

You can also position at a subtag, using this syntax:

     $representative = $object->Laboratory('Representative');

And both named tags and positions can be combined, like:

     $lab_address = $object->Laboratory(Address=>2);

Now β€” if you provide a -fill=>$tag argument, the object fetch will automatically fill the specified subtree, which greatly improves performance. πŸ”₯ Example:

      $lab_address = $object->Laboratory(-filled=>'Address');
** NOTE: In a scalar context, if the node to the right of the tag is ** an object, the method will perform an implicit dereference of the ** object. For example, in the case of:
    $lab = $author->Laboratory;
**NOTE: The object returned is the dereferenced Laboratory object, not a node in the Author object. You can control this by giving the autogenerated method a numeric offset, such as Laboratory(0) or Laboratory(1). For backwards compatibility, Laboratory('@') is equivalent to Laboratory(1).

Heads up 🫢 β€” the semantics of the autogenerated methods changed subtly between version 1.57 (the last stable release) and version 1.62. In the earlier versions, calling an autogenerated method in a scalar context returned the subtree rooted at the tag. In the current version, an implicit right() and dereference is performed instead. So if you're on old code and confused, that's why. 😭


πŸ¦‹ fetch() method πŸ¦‹

$new_object = $object->fetch;
$new_object = $object->fetch($tag);

Follow the object into the database and get a new object back. This is the best way to follow object references, fr fr. For example:

    $laboratory = $object->at('Laboratory')->fetch;
    print $laboratory->asString;

Because that previous example is such a frequent idiom, the optional $tag argument lets you smoosh the two operations into one:

    $laboratory = $object->fetch('Laboratory');

🌈 follow() method 🌈

@papers        = $object->follow('Paper');
@filled_papers = $object->follow(-tag=>'Paper',-filled=>1);
@filled_papers = $object->follow(-tag=>'Paper',-filled=>'Author');

follow() follows a tag into the database, dereferences the column to its right, and returns the objects that come out of that operation. ✨

Beware!! ⚠️ If you follow a tag that points to an object β€” like the Author "Paper" tag β€” you'll get a list of all the Paper objects. But if you follow a tag that points to a scalar, like "Full_name", you'll get an empty string. 🫠

In a scalar context, this method returns the number of objects that would have been followed.

The full named-argument form of this call accepts -tag (mandatory) and -filled (optional). The former points to the tag to follow. The latter accepts a boolean argument, or the name of a subtag. A numeric true argument returns completely "filled" objects β€” which increases network and memory usage, but possibly boosts performance if you've got high database access latency. 🐌➑️⚑ Alternatively, you can give the name of a tag to follow, in which case just the named portion of the subtree in the followed objects gets filled (v.g.)

For backward compatibility: if follow() is called with no arguments at all, it acts like fetch().


πŸ“ pick() method πŸ“

Deprecated method. It has the same semantics as fetch(), and fetch() is what you should be using instead. kindly retire it πŸ’…


🧚 col() method 🧚

@column = $object->col;
@column = $object->col($position);

col() flattens a portion of the tree by returning the column one hop to the right of the current subtree. You can toss in an additional positional index to navigate the tree with "tag[2]" behavior. This next example returns the author's mailing address:

  @mailing_address = $object->at('Address.Mail')->col();

And this one returns the author's entire address β€” mail, e-mail and phone:

  @address = $object->at('Address')->col(2);

It's equivalent to any of these calls:

  $object->at('Address[2]');
  $object->get('Address',2);
  $object->Address(2);

Use whatever syntax is most comfortable for you, bestie β€” they all serve the same cunt 🌸

In a scalar context, col() returns the number of items in the column.


🌸 row() method 🌸

@row=$object->row();
@row=$object->row($position);

okay so row() hands you back the row of data sittin' to the right of the object. the first member of that list is the object itself, always. so like, if you've got the "Thierry-Mieg J" object, this lil example right here:

@row = $object->Address->row();

gives you the list ('Address','Mail','CRBM duCNRS'). slay πŸ’…

you can also hand it an optional position if you wanna scoot rightward one or more spots before it grabs the row. so this fragment returns ('Mail','CRBM duCNRS'):

@row = $object->Address->row(1);

and in scalar context, row() returns the number of items in the row. no cap.


✨ asString() method ✨

$object->asString;

asString() gives you a pretty-printed ASCII representation of the object tree. it's giving ✨aesthetic tree diagram✨.

πŸŽ€ asTable() method πŸŽ€

$object->asTable;

asTable() returns the object as a tab-delimited text table.

πŸ¦‹ asAce() method πŸ¦‹

$object->asAce;

asAce() returns the object as a tab-delimited text table but in ".ace" format.

πŸ’– asHTML() method πŸ’–

$object->asHTML;
$object->asHTML(\&tree_traversal_code);

asHTML() returns an HTML 3 table representing the object β€” perfect for dropping straight into a Web browser page, fr fr.

the callback routine, if you pass one, gets a chance to modify the object representation before it gets incorporated into the table. like, turning it into an HREF link, iconic behavior honestly.

so the callback takes a single argument containing the object, and it must return a string-valued result. OR it can return a list as its result, in which case the first member of the list is the string representation of the object, and the second member is a boolean indicating whether to prune the table at this level. for example, you can prune large repetitive lists. so useful bestie 😭

here's a complete example:

sub process_cell {
  my $obj = shift;
  return "$obj" unless $obj->isObject || $obj->isTag;

  my @col = $obj->col;
  my $cnt = scalar(@col);
  return ("$obj -- $cnt members",1);  # prune
         if $cnt > 10                 # if subtree to big

  # tags are bold
  return "<B>$obj</B>" if $obj->isTag;

  # objects are blue
  return qq{<FONT COLOR="blue">$obj</FONT>} if $obj->isObject;
}

$object->asHTML(\&process_cell);

look at her go 🫢

🌈 asXML() method 🌈

$result = $object->asXML;

asXML() returns a well-formed XML representation of the object. heads up tho: the particular representation is still under discussion, so this feature is primarily for demonstration. it's giving beta energy πŸ‘‘.

πŸ’… asGIF() method πŸ’…

($gif,$boxes) = $object->asGIF();
($gif,$boxes) = $object->asGIF(-clicks=>[[$x1,$y1],[$x2,$y2]...]
                               -dimensions=> [$width,$height],
                               -coords    => [$top,$bottom],
                               -display   => $display_type,
                               -view      => $view_type,
                               -getcoords => $true_or_false
                               );

asGIF() returns the object as a GIF image. the contents of the GIF will be whatever xace would ordinarily display in graphics mode, and it'll vary for different object classes. so like, it depends on the vibe of the class πŸ’«

you can optionally hand asGIF a -clicks argument to simulate the action of a user clicking on the image. the click coordinates should be formatted as an array reference that contains a series of two-element subarrays, each corresponding to the X and Y coordinates of a single mouse click. and ngl, there is currently no way to pass information about middle or right mouse clicks, dragging operations, or keystrokes. that's just the rules πŸ˜”

you may also specify a -dimensions to control the width and height of the returned GIF. but since there is no way of obtaining the preferred size of the image in advance, this is not usually useful. so, yk, lowkey pointless but it's there.

the optional -display argument lets you specify an alternate display for the object. for example, Clones can be displayed either with the PMAP display or with the TREE display. if not specified, the default display is used.

the optional -view argument lets you specify an alternative view for MAP objects only. if not specified, you'll get the default view.

the -coords argument lets you provide the top and bottom of the display for MAP objects only. these coordinates are in the map's native coordinate system (cM, bp). by default, AceDB will show most (but not necessarily all) of the map according to xace's display rules. annnd if you call this method with the -getcoords argument and a true value, it will return a two-element array containing the coordinates of the top and bottom of the map. obsessed πŸ”₯

so asGIF() returns a two-element array:

  • the first element is the GIF data
  • the second element is an array reference that indicates special areas of the image called "boxes."

boxes are rectangular areas that surround buttons, and certain displayed objects. using the contents of the boxes array, you can turn the GIF image into a client-side image map. unfortunately β€” and this is the sad part bestie 😭 β€” not everything that is clickable is represented as a box. you still have to pass clicks on unknown image areas back to the server for processing. no shortcuts, periodt.

each box in the array is a hash reference containing the following keys:

'coordinates'  => [$left,$top,$right,$bottom]
'class'        => object class or "BUTTON"
'name'         => object name, if any
'comment'      => a text comment of some sort

coordinates points to an array of points indicating the top-left and bottom-right corners of the rectangle. class indicates the class of the object this rectangle surrounds. it may be a database object, or the special word "BUTTON" for one of the display action buttons. name indicates the name of the object or the button. comment is some piece of information about the object in question. you can display it in the status bar of the browser or in a popup window if your browser provides that facility πŸ’–

🧚 asDNA() and asPeptide() methods 🧚

$dna = $object->asDNA();
$peptide = $object->asPeptide();

if you're dealing with a sequence object of some sort, these methods will return strings corresponding to the DNA or peptide sequence in FASTA format. biology girlies rise 🧬✨


πŸ“ add_row() method πŸ“

$result_code = $object->add_row($tag=>$value);
$result_code = $object->add_row($tag=>[list,of,values]);
$result_code = $object->add(-path=>$tag,
                            -value=>$value);

add_row() updates the tree by adding data to the indicated tag path. the example given below adds the value "555-1212" to a new Address entry named "Pager". you may call add_row() a second time to add a new value under this tag, creating multi-valued entries. stacking them up πŸ’…

$object->add_row('Address.Pager'=>'555-1212');

you can also provide a list of values to add an entire row of data. for example:

$sequence->add_row('Assembly_tags'=>['Finished Left',38949,38952,'AC3']);

actually, the array reference is not entirely necessary, and if you prefer you can use this more concise notation:

$sequence->add_row('Assembly_tags','Finished Left',38949,38952,'AC3');
⚠️ real talk: no check is done against the database model for the correct data type or tag path. and the update isn't actually performed until you call commit(), at which time a result code indicates whether the database update was successful. don't forget to commit, bestie, or literally nothing happens 😭

you may create objects that reference other objects this way:

$lab = new Ace::Object('Laboratory','LM',$db);
$lab->add_row('Full_name','The Laboratory of Medicine');
$lab->add_row('City','Cincinatti');
$lab->add_row('Country','USA');

$author = new Ace::Object('Author','Smith J',$db);
$author->add_row('Full_name','Joseph M. Smith');
$author->add_row('Laboratory',$lab);

$lab->commit();
$author->commit();

the result code indicates whether the addition was syntactically correct. add_row() will fail if you attempt to add a duplicate entry (that is, one with exactly the same tag and value). in this case, use replace() instead. currently there is no checking for an attempt to add multiple values to a single-valued (UNIQUE) tag. the error will be detected and reported at commit() time however. so like, surprise! πŸ™ˆ

the add() method is an alias for add_row().

see also the Ace->new() method. ✨


🌸 add_tree() 🌸

$result_code = $object->add_tree($tag=>$ace_object);
$result_code = $object->add_tree(-tag=>$tag,-tree=>$ace_object);

the add_tree() method will insert an entire Ace subtree into the object to the right of the indicated tag. this can be used to build up complex Ace objects, or to copy portions of objects from one database to another. the first argument is a tag path, and the second is the tree that you wish to insert. as with add_row(), the database will only be updated when you call commit().

⚠️ when inserting a subtree, you must be careful to remember that everything to the *right* of the node that you are pointing at will be inserted; not the node itself. this trips everybody up, i'm not even joking πŸ’€

for example, given this Sequence object:

Sequence AC3
  DB_info     Database    EMBL
  Assembly_tags   Finished Left   1   4   AC3
                  Clone left end      1   4   AC3
                  Clone right end     5512    5515    K07C5
                                      38949   38952   AC3
                  Finished Right      38949   38952   AC3

if we use at('Assembly_tags') to fetch the subtree rooted on the "Assembly_tags" tag, it is the tree to the right of this tag, beginning with "Finished Left", that will be inserted.

here's an example of copying the "Assembly_tags" subtree from one database object to another:

$remote = Ace->connect(-port=>200005)  || die "can't connect";
$ac3 = $remote->fetch(Sequence=>'AC3') || die "can't get AC7";
my $assembly = $ac3->at('Assembly_tags');

$local = Ace->connect(-path=>'~acedb') || die "can't connect";
$AC3copy = Ace::Object->new(Sequence=>'AC3copy',$local);
$AC3copy->add_tree('Assembly_tags'=>$tags);
$AC3copy->commit || warn $AC3copy->error;

notice that this syntax will not work the way you think it should:

$AC3copy->add_tree('Assembly_tags'=>$ac3->at('Assembly_tags'));

this is because calling at() in an array context returns the column to the right of the tag, not the tag itself. sneaky little thing, that one 😭

here's an example of building up a complex structure from scratch using a combination of add() and add_tree():

$newObj = Ace::Object->new(Sequence=>'A555',$local);
my $assembly = Ace::Object->new(tag=>'Assembly_tags');
$assembly->add('Finished Left'=>[10,20,'ABC']);
$assembly->add('Clone right end'=>[1000,2000,'DEF']);
$assembly->add('Clone right end'=>[8000,9876,'FRED']);
$assembly->add('Finished Right'=>[1000,3000,'ETHEL']);
$newObj->add_tree('Assembly_tags'=>$assembly);
$newObj->commit || warn $newObj->error;

and that's how you build a whole structure out of thin air, main character energy 🫢

πŸ’… delete() method

$result_code = $object->delete($tag_path,$value);
$result_code = $object->delete(-path=>$tag_path,
                               -value=>$value);

Okay so bestie, delete() does exactly what it says on the tin: it removes the tag and value you point at from the object. Here's the glow-up version β€” this lil example deletes the address line 'FRANCE' from the Author's mailing address πŸ—‘οΈβœ¨

$object->delete('Address.Mail','FRANCE');
🚨 MAJOR PSA: nothing actually gets deleted from the database until you call commit(). Like, it's giving "added to cart" energy, not "purchased." πŸ’€

The delete() result code tells you whether the deletion was successful. Currently it is always true, since the database model is not checked. Not a vibe, just the facts. πŸ™ˆ


πŸŽ€ replace() method

$result_code = $object->replace($tag_path,$oldvalue,$newvalue);
$result_code = $object->replace(-path=>$tag_path,
                                -old=>$oldvalue,
                                -new=>$newvalue);

replace() swaps out the indicated tag and value for a shiny new value. This example changes the address line 'FRANCE' to 'LANGUEDOC' in the Author's mailing address:

$object->delete('Address.Mail','FRANCE','LANGUEDOC');
⚠️ Same deal as before, gorgeous: no actual database changes happen until you call commit(). Patience is a virtue, periodt.

The delete() result code indicates whether the replace was successful. Currently it's true if the old value was identified. πŸ•΅οΈβ™€οΈ


πŸ’Ύ commit() method

$result_code = $object->commit;

This is the moment of truth, bestie β€” commit() pushes all your add(), replace() and delete() operations into the database. It can also be used to write a completely brand new object into the database. Main character energy for real. πŸ‘‘

The result code tells you whether the object was successfully written. If something went wrong, you can find further details in the Ace->error() error string. πŸ”


πŸŒ€ rollback() method

$object->rollback;

The undo button, the "never mind", the ctrl+Z of your dreams ✨ β€” rollback() discards all adds, deletions and replacements, returning the object to the state it was in prior to the last commit().

Lowkey how it works: rollback() deletes the object from Perl memory and fetches the object anew from AceDB. So if someone changed the object in the database while you were working with it, you'll see this version, or the one you originally fetched. It's giving ✨mystery box✨.

And heads up: if you're creating an entirely new object, you must add at least one tag in order to enter the object into the database. No tag, no entry, no exceptions. πŸšͺ


πŸ’€ kill() method

$result_code = $object->kill;

Oop β€” this one's the nuclear option. kill() removes the object from the database immediately and completely. It does not wait for a commit(), and it does not respond to a rollback(). It's not asking permission, it's not taking questions. 🫣

If it's successful, you'll be left with an empty object that contains just the class and object names.

USE WITH CARE! Genuinely. No takebacks. 😭

If it fails β€” which commonly happens when the database is not open for writing β€” this method will return undef. A description of the problem can be found by calling the error() method. πŸ“


πŸ“… date_style() method

$object->date_style('ace');

A lil convenience method that sets the date format for all objects returned by the database. It is exactly equivalent to:

$object->db->date_style('ace');
πŸ‘€ Quick note: the text representation of the date will change for all objects returned from this database, not just the current one. So like, it's a group chat announcement, not a DM. πŸ“’

🌳 isRoot() method

print "Top level object" if $object->isRoot;

This method will return true if the object is a "top level" object β€” that is, the root of an object tree rather than a subtree. It's the CEO of the family tree, basically. πŸ‘‘πŸŒ²


🧬 model() method

$model = $object->model;

This method will return the object's model as an Ace::Model object, or undef if the object does not have a model. See Ace::Model for details. Cute, documented, done. πŸŽ€


⏰ timestamp() method

$stamp = $object->timestamp;

The timestamp() method retrieves the modification time and date from the object. It works both with top level objects and with subtrees. 🌈

⚠️ Timestamp handling must be turned on in the database, or timestamp() will return undef. It's giving "feature flag off, sorry bestie." πŸ™ˆ

The returned timestamp is actually a UserSession object, which can be printed and explored like any other object. However (ngl, this is a little tragic πŸ’€) there is currently no useful information in UserSession other than its name.


πŸ’¬ comment() method

$comment = $object->comment;

This returns the comment attached to an object or object subtree, if any. Comments are Comment objects and they have the interesting property that a single comment can refer to multiple objects. Iconic, honestly β€” one comment, many lives. 🌸

If there is no comment attached to the current subtree, this method will return undef.

Currently you cannot create a new comment in AcePerl or edit an old one. So it's read-only vibes only. πŸ“–


🚨 error() method

$error = $object->error;

Returns the error from the previous operation, if any. Same rules as in Ace::error(): this string will only have meaning if the previous operation returned a result code indicating an error. Otherwise it's just noise, bestie. 🫢


🏭 factory() method

WARNING - THIS IS DEFUNCT AND NO LONGER WORKS. USE THE Ace->class() METHOD INSTEAD
$package = $object->factory;

Okay so storytime: when a root Ace object instantiates its tree of tags and values, it creates a hierarchical structure of Ace::Object objects. πŸ“š The factory() method determines what class to bless these subsidiary objects into. By default, they are Ace::Object objects, but you can override this method in a child class in order to create more specialized Ace::Object classes. The method should return a string corresponding to the package to bless the object into. It receives the current Ace::Object as its first argument.

...but again, πŸ’€ it's defunct. Use Ace->class().


πŸ› debug() method

$object->debug(1);

Change the debugging mode, bestie. A zero turns off debugging messages. Integer values produce debug messages on standard error. Higher integers produce progressively more verbose messages. πŸ“’

And the tea: this actually is just a front end to Ace->debug(), so the debugging level is global. Set it once, it's everywhere. 🌍✨


🌟 SEE ALSO

  • Ace
  • Ace::Model
  • Ace::Object
  • Ace::Local
  • Ace::Sequence,Ace::Sequence::Multi

πŸ‘‘ AUTHOR

Lincoln Stein <lstein@cshl.org> with extensive help from Jean Thierry-Mieg <mieg@kaa.crbm.cnrs-mop.fr> πŸ’–

Copyright (c) 1997-1998, Lincoln D. Stein

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See DISCLAIMER.txt for disclaimers of warranty. πŸ“œ


And that's a wrap on Ace::Object(3pm) β€” this page is from perl v5.42.3, dated 2026-08-19. Slay, you read the whole thing, I'm so proud of you πŸ₯ΉπŸ’…βœ¨

πŸ“¦ where this page comes from