Here is an picture of the result. A department has employees and an employee belongs to a department
First step is to create the RoR application
rails hr_app
change directory to the new application
cd hr_app
We need to edit the database configuration file
hr_app\config\database.yml
Install RubyAMF
ruby script/plugin install http://rubyamf.googlecode.com/svn/tags/current/rubyamf
Generate the department and employee table
ruby script/generate rubyamf_scaffold department
ruby script/generate rubyamf_scaffold employee
change department table configuration in hr_app/db/migrate folder
class CreateDepartments < ActiveRecord::Migration
def self.up
create_table :departments do |t|
t.string :name
t.string :location
t.timestamps
end
end
def self.down
drop_table :departments
end
end
change employee table also located in the hr_app/db/migrate folder
class CreateEmployees < ActiveRecord::Migration
def self.up
create_table :employees do |t|
t.integer :department_id
t.string :first_name
t.string :last_name
t.string :job
t.timestamps
end
end
def self.down
drop_table :employees
end
end
Let's create the department and employee table
rake db:migrate
We can add one to many associations in RoR
Edit hr_app/app/models/department.rb file where we will add has_many
class Department < ActiveRecord::Base
has_many :employees
end
Edit hr_app/app/models/employee.rb file where we will add belongs_to
class Employee < ActiveRecord::Base
belongs_to :department
end
The RoR configuration is ready we only have to configure RubyAMF
We can generate the class mappings and copy this to the rubyamf_config.rb configuration
ruby script/generate rubyamf_mappings
The generator detects the associations between department and employees
Copy the generated block of text into config/rubyamf_config.rb:
edit hr_app/config/rubyamf_config.rb
require 'app/configuration'
module RubyAMF
module Configuration
ClassMappings.ignore_fields = ['created_at','updated_at']
ClassMappings.translate_case = true
ClassMappings.assume_types = false
ParameterMappings.scaffolding = false
ClassMappings.register(
:actionscript => 'Department',
:ruby => 'Department',
:type => 'active_record',
:associations => ["employees"],
:attributes => ["id", "name", "location", "created_at", "updated_at"])
ClassMappings.register(
:actionscript => 'Employee',
:ruby => 'Employee',
:type => 'active_record',
:associations => ["department"],
:attributes => ["id", "first_name", "last_name", "job", "created_at", "updated_at", "department_id"])
ClassMappings.force_active_record_ids = true
ClassMappings.use_ruby_date_time = false
ClassMappings.use_array_collection = true
ClassMappings.check_for_associations = true
ParameterMappings.always_add_to_params = true
end
end
Now we can start the RoR application
ruby script/server
Create a new Flex application.
First we will create the employee and department actionscript class.
The employee field in the Department class is an arraycollection
package vo
{
import mx.collections.ArrayCollection;
[RemoteClass(alias="Department")]
[Bindable]
public class Department
{
public var id:int;
public var name:String;
public var location:String;
public var createdAt:Date;
public var updatedAt:Date;
public var employees:ArrayCollection;
public function Department()
{
}
}
}
In the Employee class we add the department field of type Department
package vo
{
[RemoteClass(alias="Employee")]
[Bindable]
public class Employee
{
public var id:int;
public var firstName:String;
public var lastName:String;
public var job:String;
public var departmentId:int;
public var createdAt:Date;
public var updatedAt:Date;
public var department:Department;
public function Employee()
{
}
}
}
Here is the mxml code
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="loadAll(); loadAll2();">
<mx:Script>
<![CDATA[
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import vo.Department;
import vo.Employee;
import mx.collections.ArrayCollection;
[Bindable]
private var departments:ArrayCollection = new ArrayCollection();
[Bindable]
private var employees:ArrayCollection = new ArrayCollection();
private function loadAll():void {
var token:AsyncToken = AsyncToken(departmentService.find_all());
token.kind = "fill";
}
private function loadAll2():void {
var token:AsyncToken = AsyncToken(employeeService.find_all());
token.kind = "fill";
}
private function createDept():void {
var dept:Department = new Department();
dept.name = "Headquarters";
dept.location = "Putten";
dept.employees = new ArrayCollection;
var token:AsyncToken = AsyncToken(departmentService.save(dept));
token.kind = "create";
}
private function createDeptWithEmp():void {
var dept:Department = new Department();
dept.name = "Headquarters";
dept.location = "Putten";
dept.employees = new ArrayCollection;
var emp:Employee = new Employee;
emp.firstName = "pipo";
emp.lastName = "pipo";
emp.job = "clown"
dept.employees.addItem(emp);
var token:AsyncToken = AsyncToken(departmentService.save(dept));
token.kind = "create";
}
private function destroy():void {
var token:AsyncToken = AsyncToken(departmentService.destroy(dg.selectedItem.id));
token.kind = "delete";
}
private function faultHandler(event:FaultEvent):void {
Alert.show(event.fault.faultString + " : " + event.fault.faultCode + " : " + event.fault.faultDetail , "Error in LoginCommand");
}
private function resultHandler(event:ResultEvent):void {
if ( event.token.kind == "fill" ) {
departments = event.result as ArrayCollection;
} else {
loadAll();
loadAll2();
}
}
private function resultHandler2(event:ResultEvent):void {
if ( event.token.kind == "fill" ) {
employees = event.result as ArrayCollection;
} else {
loadAll2();
}
}
]]>
</mx:Script>
<mx:RemoteObject id="departmentService" destination="rubyamf"
endpoint="http://localhost:3000/rubyamf_gateway/"
source="DepartmentsController"
showBusyCursor="true"
result="resultHandler(event)"
fault="faultHandler(event)"
/>
<mx:RemoteObject id="employeeService" destination="rubyamf"
endpoint="http://localhost:3000/rubyamf_gateway/"
source="EmployeesController"
showBusyCursor="true"
result="resultHandler2(event)"
fault="faultHandler(event)" />
<mx:HBox>
<mx:VBox>
<mx:Label text="Departments with employees"/>
<mx:HBox>
<mx:Button label="Create Department" click="createDept()"/>
<mx:Button label="Create Department with Employee" click="createDeptWithEmp()"/>
<mx:Button label="Remove Department" click="destroy()"/>
</mx:HBox>
<mx:DataGrid id="dg" dataProvider="{departments}">
<mx:columns>
<mx:DataGridColumn dataField="id" headerText="Key"/>
<mx:DataGridColumn dataField="name" headerText="Name"/>
<mx:DataGridColumn dataField="location" headerText="Location"/>
</mx:columns>
</mx:DataGrid>
<mx:DataGrid id="dg_2" dataProvider="{dg.selectedItem.employees}">
<mx:columns>
<mx:DataGridColumn dataField="id" headerText="Key"/>
<mx:DataGridColumn dataField="firstName" headerText="First Name"/>
<mx:DataGridColumn dataField="lastName" headerText="Last Name"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
<mx:VBox>
<mx:Label text="Employees"/>
<mx:DataGrid id="dg2" dataProvider="{employees}" variableRowHeight="true">
<mx:columns>
<mx:DataGridColumn dataField="id" headerText="Key"/>
<mx:DataGridColumn dataField="firstName" headerText="First Name"/>
<mx:DataGridColumn dataField="lastName" headerText="Last Name"/>
<mx:DataGridColumn dataField="department">
<mx:itemRenderer>
<mx:Component>
<mx:VBox>
<mx:Text text="{data.department.name}"/>
<mx:Text text="{data.department.location}"/>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:HBox>
</mx:Application>
That's all. you can easily create and retrieve departments with it's employees with RoR and RubyAMF. RoR and RubyAMF do all the hard work.
Hello Edwin,
ReplyDeletegreat post!
Some things where new for me, for example the command ruby script/generate rubyamf_mappings.
Thanks for that!