Archive for the ‘airdb’ Category

You are currently browsing the archives for the airdb category.


AirDB Join Table Attributes

In some cases, it becomes necessary to have additional attributes associated with a many-many relationship.

This is typically true of “has_and_belongs_to_many” associations which end up mirroring some kind of “membership” between the joined models. For example, a library system might track borrowing of books by patrons, with extra attributes for return dates and accrued fines.

The Django guys have a handy example about musicians and bands with extra attributes such as the membership role and date of joining. In my case, I needed to track sharing status of Photos across Photosets.

AirDB now has support for such join table attributes. Its a bit of a hack, in the interests of time, code size and abstraction concessions. The new things, which make it all possible include:

  1. An optional argument to Migrator.joinTable()
  2. DB.execute(sql) to allow tweaking an existing join table.
  3. Associator methods: setAttrfindAllByAttr, countByAttr, getAttrVal.

Here are some actual code excerpts (in the Photoset model) Read the rest of this entry »

AirDB Updates

A summary of the several improvements committed to AirDB in the last five weeks. 

  1. Support for has-many and belongs-to associations
  2. Support for multiple associations for a given Modeler class.
  3. More smarts and existence checks for automatic schema migration
  4. Call-chaining for Associations e.g. post.author.comments.count
  5. Miscellaneous fixes, improvements and optimizations

Highlights of the major new additions since I wrote about the initial design

The Migrator handles schema versioning, checking and automatic migrations.

public class Migrator implements IMigratable
{
  public function Migrator(klass:Class, options:Object, directives:Array) {
     // setup table structures, prototype fields, existing schema
     DB.migrate(this);
  }
  // Supported migration directives
 
  // create a table after processing the provided function block
  public function createTable(block:Function):void { ..}
 
  // specify a column, if it does not exist, it is created.
  public function column(name:String, dataType:uint, options:Object = null):void {..}
 
  // make a join table to handle many-many associations with specified class
  public function joinTable(klass:Class):void { .. }
 
  // map a foreign key for belongs-to association
  public function belongsTo(klass:Class):void { .. }
}

The Associator handles associations which are specified via class meta-data. The improvements under the hood make it easier to make associations between Modeler object instances. For example

// Use the record ID of the target to specify associations
// Post has-many comments
post.comments.push(3);
 
// Chain the target for further calls
// Post belongs-to author and Author has-many comments
 
// Obtain field value of associated target
var nm:String = post.author.name;
 
// Make further associations on associated target
post.author.comments.push(new Comment({title: 'hello there'}));

Join queries and foreign-key lookups are now a breeze with AirDB.

AirDB available at github

I received a request today for a look at the code in response to my post on Designing an ActionScript ORM for AIR.  So, I took a few hours to take the ActionScript ORM code, add a working example and put it out on github. 

AirDB is now available here: 

http://github.com/dkeskar/airdb/tree/master

Suggestions, improvements and contributions are welcome and appreciated.

Designing an ActionScript ORM

How do we get the best of DataMapper and ActiveRecord within the constraints of ActionScript?

First off, the design goals and desires:

  1. Easily create persistent objects that map to database storage.
  2. Flexible methods to abstract data manipulation and queries.
  3. Model DB associations through object properties and relations.
  4. Support run-time schema extensibility (migrations)

ActionScript is dynamic, object-oriented, and does have support for limited introspection. It also imposes several restrictions such as strong typing, Java-style single class inheritance and the concomitant Class vs. Interface distinction.

Items 1) and 2) are straightforward using object inheritance.

import com.memamsa.datamapper.*;
dynamic public class Post extends Modeler {
   // Post objects are now DB models.
}

Being a data model automatically allows Post objects to do database operations. There is one weakness, if you are a purist. Since ActionScript does not allow static methods of a class to be inherited, some methods (such as find and create) which make sense as class level static methods end up having to be defined as object methods.

For associations, I use reflection using a combination of class meta-data and the describeType functionality within the Flex framework. The declaration itself is straightforward.

[Association(name="comments", className="Comment", type="has_many")];
dynamic public class Post extends Modeler {
}

Which means, you can now add, access, and update comments associated with the Post objects like so:

var post:Post = new Post();
// add a new comment to the post
post.comments.push(new Comment({author: 'cooldude', text: '1st'}));
// how many comments do we have?
post.comments.list().length;
// find comments matching criteria
post.comments.find({conditions: 'author like %cool%', order: 'created_at ASC'});

I really like Datamapper style declaration of fields and properties within the class definition. ActiveRecord does better with schema versioning and the ability to migrate selectively without loss of data and with  optional data transforms. The ability to extend the schema during development, or as part of application updates is invaluable. This is how we handle it in our ActionScript ORM.

import com.memamsa.datamapper.*;
dynamic public class Post extends Modeler {
  // describe the migrations
  // The Migrator takes this class references, options and an array of
  // migration functions.
  private static const migrations:Migrator = new Migrator(
    Post,
    {id: true},
    [
      function(my:Migrator):void {
        my.createTable(function():void {
          my.column('title', DB.Field.VarChar, {limit: 128});
          my.column('author', DB.Field.VarChar, {limit: 40});
          my.columnTimestamps();
        }
      }
    ]);
    //.. other class methods and properties...
}

When necessary, additional migration functions can be added to the array of migrations. The Migrator tracks schema versions on a per-table basis to make sure changes are automatically reflected in the schema.

Finally, we utilize the ECMAScript prototype mechanism to set schema properties on each of the Modeler sub-classes, to allow usage such as this:

var p:Post = new Post();
p.load({id: 2});
p['title'] = 'New Title';
p.save();

One more thing, add the following compiler setting.

  • -keep-as3-metadata+=Association

And we now have a usable ORM for ActionScript.