Working with RestfulX Models

dima edited this page Dec 13, 2010 · 8 revisions

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


  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="contactors")] [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 :contactors 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.RubossTreeModel; [Resource(name="categories")] [Bindable] public class Category extends RubossTreeModel { 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