public
Description: The RESTful way to develop Adobe Flex and AIR applications.
Home | Edit | New

Working with RestfulX Models

Basic Operations

Displaying model instances in a List


    <mx:List id="projectsList"
      width="100%" height="100%"
      dataProvider="{Rx.models.index(Project)}"/>

Creating a model instance


    private function createProject():void {
      var project:Project = new Project();

      // set properties
      project.name = nameTextInput.text;
      project.notes = notesTextArea.text;
      project.startDate = startDateDateField.selectedDate;
      project.endDate = endDateDateField.selectedDate;
      project.completed = completedCheckBox.selected;

      project.user = User(userComboBox.selectedItem);

      // create
      project.create();
    }

Updating a model instance


    private function updateProject():void {
      // set properties
      _project.name = nameTextInput.text;
      _project.notes = notesTextArea.text;
      _project.startDate = startDateDateField.selectedDate;
      _project.endDate = endDateDateField.selectedDate;
      _project.completed = completedCheckBox.selected;

      _project.user = User(userComboBox.selectedItem);

      // update
      _project.update();
    }

Deleting a model instance


    private function destroyProject():void {
      _project.destroy();
    }

Want a more feature complete example? Check out Pomodo On Rails

Example Configurations

Configuration Flex/AIR : Remote AR-like DS AIR : SQLite Flex/AIR : CouchDB Flex : GAE
Primitive Attributes1 + + + +
Primary Relationships2 (BelongsTo, HasOne, HasMany) + + + +
Polymorphic Relationships3 + + + +
Tree-Like Structures/Self-Referential Relationships4 + + + +
Custom Key Relationships5 + + + +
Single Table Inheritance6 + + + +
Many-to-Many Relationships7 + + + +
Conditional Relationships8 (HasOne, HasMany, Many-to-Many) + + + +
Nested Relationships9 + +
Custom Methods10 + +
Model Path Prefixes11 + +
Nested Models12 + +

Nested Relationships, Custom Methods, etc are not supported with SQLite because they are not relevant. They are specific to performance optimization when dealing with remote data sources.

If working with SQLite or CouchDB only you don’t need any servers running and by implication neither ActiveRecord, nor DataMapper, etc are necessary. RestfulX framework persists all of these configurations by itself. Only AS3 examples below are relevant when working with SQLite or CouchDB. If you are using CouchDB make sure to have your CouchDB database running of course.

Code Samples

1 Primitive Attributes

AS3


package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="simple_properties")]
  [Bindable]
  public class SimpleProperty extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var amount:int;

    public var price:Number;

    public var quantity:Number;

    public var available:Boolean;

    public var deliveredOn:Date;

    [DateTime]
    public var soldOn:Date;
    
    [DateTime]
    public var createdAt:Date;

    public function SimpleProperty() {
      super(LABEL);
    }
  }
}

ActiveRecord Model


class SimpleProperty < ActiveRecord::Base
end

ActiveRecord Migration


class CreateSimpleProperties < ActiveRecord::Migration
  def self.up
    create_table :simple_properties do |t|
      t.string :name
      t.integer :amount
      t.float :price
      t.decimal :quantity
      t.boolean :available
      t.date :delivered_on
      t.time :sold_on

      t.timestamps
    end
  end

  def self.down
    drop_table :simple_properties
  end
end

2 Primary Relationships (BelongsTo, HasOne, HasMany)

AS3


package restfulx.test.models {
  import org.restfulx.models.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="projects")]
  [Bindable]
  public class Project extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [HasOne]
    public var contractor:Contractor;
    
    [HasMany]
    public var tasks:ModelsCollection;
    
    public function Project() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="contractors")]
  [Bindable]
  public class Contractor extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var address:String;

    public var city:String;

    [BelongsTo]
    public var project:Project;

    public function Contractor() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="tasks")]
  [Bindable]
  public class Task extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [BelongsTo]
    public var project:Project;

    public function Task() {
      super(LABEL);
    }
  }
}

ActiveRecord Model


class Project < ActiveRecord::Base
  has_one :contractor
  has_many :tasks
end

class Contractor < ActiveRecord::Base
  belongs_to :project
end

class Task < ActiveRecord::Base
  belongs_to :project
end

ActiveRecord Migration


class CreateProjects < ActiveRecord::Migration
  def self.up
    create_table :projects do |t|
      t.string :name

      t.timestamps
    end
  end

  def self.down
    drop_table :projects
  end
end

class CreateContractors < ActiveRecord::Migration
  def self.up
    create_table :contractors do |t|
      t.string :name
      t.string :address
      t.string :city
      t.references :project

      t.timestamps
    end
  end

  def self.down
    drop_table :contractors
  end
end

class CreateTasks < ActiveRecord::Migration
  def self.up
    create_table :tasks do |t|
      t.string :name
      t.references :project

      t.timestamps
    end
  end

  def self.down
    drop_table :tasks
  end
end

3 Polymorphic Relationships

AS3


package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="locations")]
  [Bindable]
  public class Location extends RxModel {
    public static const LABEL:String = "lineOne";

    public var lineOne:String;

    public var lineTwo:String;

    public var city:String;
    
    [BelongsTo(polymorphic="true", dependsOn="Employee, Customer")]
    public var owner:Object;

    public function Location() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="employees")]
  [Bindable]
  public class Employee extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [HasOne]
    public var location:Location;

    public function Employee() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="customers")]
  [Bindable]
  public class Customer extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [HasOne]
    public var location:Location;

    public function Customer() {
      super(LABEL);
    }
  }
}

ActiveRecord Model


class Location < ActiveRecord::Base
  belongs_to :owner, :polymorphic => true
end

class Customer < ActiveRecord::Base
  has_one :location, :as => :owner
end

class Employee < ActiveRecord::Base
  has_one :location, :as => :owner
end

ActiveRecord Migration


class CreateLocations < ActiveRecord::Migration
  def self.up
    create_table :locations do |t|
      t.string :line_one
      t.string :line_two
      t.string :city
      t.references :owner, :polymorphic => true

      t.timestamps
    end
  end

  def self.down
    drop_table :locations
  end
end

class CreateEmployees < ActiveRecord::Migration
  def self.up
    create_table :employees do |t|
      t.string :name

      t.timestamps
    end
  end

  def self.down
    drop_table :employees
  end
end

class CreateCustomers < ActiveRecord::Migration
  def self.up
    create_table :customers do |t|
      t.string :name

      t.timestamps
    end
  end

  def self.down
    drop_table :customers
  end
end


4 Tree-Like Structures/Self-Referential Relationships

AS3


package restfulx.test.models {
  import org.restfulx.models.RxTreeModel;
  
  [Resource(name="categories")]
  [Bindable]
  public class Category extends RxTreeModel {
    public static const LABEL:String = "name";

    public var name:String;
    
    [BelongsTo]
    public var parent:Category;

    public function Category() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="facebook_users")]
  [Bindable]
  public class FacebookUser extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;
    
    [BelongsTo(referAs="friends")]
    public var friend:FacebookUser;
    
    [HasMany(type="FacebookUser")]
    public var friends:ModelsCollection;

    public function FacebookUser() {
      super(LABEL);
    }
  }
}

ActiveRecord Model


class Category < ActiveRecord::Base
  acts_as_tree
end

class FacebookUser < ActiveRecord::Base
  belongs_to :friend, :class_name => 'FacebookUser'
  has_many :friends, :class_name => 'FacebookUser'
end

ActiveRecord Migration


class CreateCategories < ActiveRecord::Migration
  def self.up
    create_table :categories do |t|
      t.string :name
      t.integer :parent_id

      t.timestamps
    end
  end

  def self.down
    drop_table :categories
  end
end

class CreateFacebookUsers < ActiveRecord::Migration
  def self.up
    create_table :facebook_users do |t|
      t.string :name
      t.integer :friend_id

      t.timestamps
    end
  end

  def self.down
    drop_table :facebook_users
  end
end

5 Custom Key Relationships

AS3


package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="actors")]
  [Bindable]
  public class Actor extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [BelongsTo]
    public var movie:Movie;

    [BelongsTo]
    public var actionMovie:Movie;

    [BelongsTo]
    public var documentary:Movie;

    public function Actor() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="movies")]
  [Bindable]
  public class Movie extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [HasOne]
    public var leadActor:Actor;
    
    [HasMany]
    public var actors:ModelsCollection;
    
    public function Movie() {
      super(LABEL);
    }
  }
}

ActiveRecord Model


class Actor < ActiveRecord::Base
  belongs_to :movie
  belongs_to :action_movie, :class_name => 'Movie'
  belongs_to :documentary, :class_name => 'Movie'
end

class Movie < ActiveRecord::Base
  has_one :lead_actor, :class_name => 'Actor', :order => 'name asc'
  has_many :actors
end

ActiveRecord Migration


class CreateActors < ActiveRecord::Migration
  def self.up
    create_table :actors do |t|
      t.string :name
      t.references :movie
      t.references :action_movie
      t.references :documentary

      t.timestamps
    end
  end

  def self.down
    drop_table :actors
  end
end

class CreateMovies < ActiveRecord::Migration
  def self.up
    create_table :movies do |t|
      t.string :name

      t.timestamps
    end
  end

  def self.down
    drop_table :movies
  end
end

6 Single Table Inheritance

AS3


package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="accounts")]
  [Bindable]
  public class Account extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public function Account() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="payable_accounts")]
  [Bindable]
  public class PayableAccount extends Account {
    public static const LABEL:String = "name";

    public function PayableAccount() {
      super();
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="receivable_accounts")]
  [Bindable]
  public class ReceivableAccount extends Account {
    public static const LABEL:String = "name";

    public function ReceivableAccount() {
      super();
    }
  }
}

ActiveRecord Model


class Account < ActiveRecord::Base
end

class PayableAccount < Account
end

class ReceivableAccount < Account
end

ActiveRecord Migration


class CreateAccounts < ActiveRecord::Migration
  def self.up
    create_table :accounts do |t|
      t.string :name
      t.string :type

      t.timestamps
    end
  end

  def self.down
    drop_table :accounts
  end
end

7 Many-to-Many Relationships

AS3


package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="clients")]
  [Bindable]
  public class Client extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var city:String;

    [HasMany]
    public var billableWeeks:ModelsCollection;
    
    [HasMany(through="billableWeeks")]
    public var timesheets:ModelsCollection;
    
    [HasMany(type="Timesheet", through="billableWeeks")]
    public var incompleteTimesheets:ModelsCollection;
    
    public function Client() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="timesheets")]
  [Bindable]
  public class Timesheet extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var total:Number;

    [HasMany]
    public var billableWeeks:ModelsCollection;
    
    [HasMany(through="billableWeeks")]
    public var clients:ModelsCollection;
    
    public function Timesheet() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="billable_weeks")]
  [Bindable]
  public class BillableWeek extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var total:Number;

    [BelongsTo]
    public var client:Client;

    [BelongsTo]
    public var timesheet:Timesheet;

    public function BillableWeek() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="authors")]
  [Bindable]
  public class Author extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [BelongsTo]
    public var book:Book;

    public function Author() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="books")]
  [Bindable]
  public class Book extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [BelongsTo]
    public var store:Store;

    [HasMany]
    public var authors:ModelsCollection;
    
    public function Book() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="stores")]
  [Bindable]
  public class Store extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [HasMany]
    public var books:ModelsCollection;
    
    [HasMany(through="books")]
    public var authors:ModelsCollection;
    
    [HasMany(type="Author", through="books")]
    public var randomAuthors:ModelsCollection;
    
    public function Store() {
      super(LABEL);
    }
  }
}

ActiveRecord Model


class Client < ActiveRecord::Base
  has_many :billable_weeks
  has_many :timesheets, :through => :billable_weeks
  has_many :incomplete_timesheets, :through => :billable_weeks, :source => :timesheet
end

class Timesheet < ActiveRecord::Base
  has_many :billable_weeks
  has_many :clients, :through => :billable_weeks
end

class BillableWeek < ActiveRecord::Base
  belongs_to :client
  belongs_to :timesheet
end

class Author < ActiveRecord::Base
  belongs_to :book
end

class Book < ActiveRecord::Base
  belongs_to :store
  has_many :authors
end

class Store < ActiveRecord::Base
  has_many :books
  has_many :authors, :through => :books
end

ActiveRecord Migration


class CreateClients < ActiveRecord::Migration
  def self.up
    create_table :clients do |t|
      t.string :name
      t.string :city

      t.timestamps
    end
  end

  def self.down
    drop_table :clients
  end
end

class CreateTimesheets < ActiveRecord::Migration
  def self.up
    create_table :timesheets do |t|
      t.string :name
      t.decimal :total

      t.timestamps
    end
  end

  def self.down
    drop_table :timesheets
  end
end

class CreateBillableWeeks < ActiveRecord::Migration
  def self.up
    create_table :billable_weeks do |t|
      t.string :name
      t.decimal :total
      t.references :client
      t.references :timesheet

      t.timestamps
    end
  end

  def self.down
    drop_table :billable_weeks
  end
end

class CreateAuthors < ActiveRecord::Migration
  def self.up
    create_table :authors do |t|
      t.string :name
      t.references :book

      t.timestamps
    end
  end

  def self.down
    drop_table :authors
  end
end

class CreateBooks < ActiveRecord::Migration
  def self.up
    create_table :books do |t|
      t.string :name
      t.references :store

      t.timestamps
    end
  end

  def self.down
    drop_table :books
  end
end

class CreateStores < ActiveRecord::Migration
  def self.up
    create_table :stores do |t|
      t.string :name

      t.timestamps
    end
  end

  def self.down
    drop_table :stores
  end
end

8 Conditional Relationships (HasOne, HasMany)

AS3


package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="projects")]
  [Bindable]
  public class Project extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    [HasOne]
    public var contractor:Contractor;
    
    [HasOne(conditions="name:2")]
    public var randomContractor:Contractor;
        
    [HasMany]
    public var tasks:ModelsCollection;
    
    public function Project() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="contractors")]
  [Bindable]
  public class Contractor extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var address:String;

    public var city:String;

    [BelongsTo(referAs="contractor, randomContractor")]
    public var project:Project;

    public function Contractor() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="clients")]
  [Bindable]
  public class Client extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var city:String;

    [HasMany]
    public var billableWeeks:ModelsCollection;
    
    [HasMany(through="billableWeeks")]
    public var timesheets:ModelsCollection;
    
    [HasMany(type="Timesheet", through="billableWeeks")]
    public var incompleteTimesheets:ModelsCollection;
    
    [HasMany(type="Timesheet", through="billableWeeks", conditions="name:2")]
    public var randomTimesheets:ModelsCollection;
    
    public function Client() {
      super(LABEL);
    }
  }
}

package restfulx.test.models {
  import org.restfulx.collections.ModelsCollection;
  import org.restfulx.models.RxModel;
  
  [Resource(name="facebook_users")]
  [Bindable]
  public class FacebookUser extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;
    
    [BelongsTo(referAs="friends, randomFriends")]
    public var friend:FacebookUser;
    
    [HasMany(type="FacebookUser")]
    public var friends:ModelsCollection;
    
    [HasMany(type="FacebookUser", conditions="name:4")]
    public var randomFriends:ModelsCollection;

    public function FacebookUser() {
      super(LABEL);
    }
  }
}

9 Nested Relationships

Serializing to FXML or JSON you can:


@projects.to_fxml(:include => [:contractor, :tasks])

@movie.to_fxml(:include => [:lead_actor, :actors])

@customer.to_fxml(:include => :location)

@store.to_fxml(:include => :authors)

10 Custom Methods

You can perform computations/delegate relevant logic to your server and then pass these methods as simple properties to Flex:

AS3


package restfulx.test.models {
  import org.restfulx.models.RxModel;
  
  [Resource(name="simple_properties")]
  [Bindable]
  public class SimpleProperty extends RxModel {
    public static const LABEL:String = "name";

    public var name:String;

    public var amount:int;

    public var price:Number;

    public var quantity:Number;

    public var available:Boolean;

    public var deliveredOn:Date;

    [DateTime]
    public var soldOn:Date;
    
    [DateTime]
    public var createdAt:Date;

    [Ignored]
    public var randomComputedProperty:String

    public function SimpleProperty() {
      super(LABEL);
    }
  }
}

ActiveRecord Model


class SimpleProperty < ActiveRecord::Base
  def random_computed_property
    "foobar"
  end
end

ActionController


@simple_properties.to_fxml(:methods => :random_computed_property)

Alternatively you can express that using default_methods helper.

ActiveRecord Model


class SimpleProperty < ActiveRecord::Base
  default_methods :random_computed_property

  def random_computed_property
    "foobar"
  end
end

Then in your ActionController, you can skip the methods array and call:

ActionController


@simple_properties.to_fxml

// or

@simple.properties.to_json

11 Model Path Prefixes

If you are working with a remote data service such as Rails, it is sometimes useful to prepend a string to the generated model URL. You can accomplish this using pathPrefix:

AS3


package restfulx.test.models {
  import org.restfulx.models.RxModel;

  [Bindable]
  [Resource(name="articles", pathPrefix="admin")]
  public class Article extends RxModel {
    public static const LABEL:String = "name";
    
    public var name:String;
    
    public function Article() {
      super(LABEL);
    }
  }
}

This will produce /admin/articles.fxml URL for POSTS and GETS and so on.

pathPrefix annotation is simply ignored in SQLite mode. You can safely leave it on if necessary for some other Service Providers, it just doesn’t have an effect with AIRServiceProvier.

12 Nested Models

Let’s say you want to index only a subset of models based on a model they belong to. If your service provider supports these kind of requests, you can pass a nestedBy array with instances of the models you want the requested nested by. nestedBy is supported on all REST/CRUD methods such as index, show, create, etc.

AS3


Rx.models.index(Task, {nestedBy: [currentProject]});

Where currentProject is a model instance of the project we are going to be differentiating by. This will produce the following URL /projects/123123123/tasks.fxml

Last edited by dima, Mon Aug 03 02:55:07 -0700 2009
Home | Edit | New
Versions: