E4Xで取得する方がAMFで取得するより5倍早い
Railsからの応答をE4Xで受信した場合とAMFで受信した場合を比較した。使ったのはidとnameからなる1000行のMySQLデータ。Flexからリクエストしてから応答までの時間を測定したところ、AMFの方が5倍も遅くなって困惑した。環境はFlex SDK 3.0, Ruby 1.8.6, Rails 2.1, MySQL 5.0.45で全て同じローカルマシン内。
Rails側
RubyAMFをインストールしたRailsプロジェクト内にUserモデルとUsersControllerを作成。UserモデルはMySQLのusersテーブル(id, name)とシンクロ済み(テストデータはgeneratedata.comで作成)。UsersControllerに以下のようにindexを記述。
class UsersController < ApplicationController def index @users = User.find(:all) respond_to do |format| format.xml {render :xml => @users} # For HTTPService format.amf {render :amf => @users} # For RemoteObject end end end
AMFの結果をArrayCollectionで簡単に受け取れるようrubyamf_config.rbを一部修正。ただしこの設定はRemoteObjectに対して有効で、NetConnectionではArrayCollectionが使えないため無効。
require ‘app/configuration’ module RubyAMF module Configuration ClassMappings.assume_types = true ClassMappings.use_array_collection = true end end
Flex側 (HTTPService - E4X)
load()とonResult()までの時間を10回測った。測定時間はTextAreaに、受信内容はDataGridにそれぞれ表示。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:HBox width="100%" height="100%"> <mx:DataGrid id="dg" width="50%" height="100%"> <mx:columns> <mx:DataGridColumn dataField="id"/> <mx:DataGridColumn dataField="name"/> </mx:columns> </mx:DataGrid> <mx:TextArea id="loadText" width="50%" height="100%"/> </mx:HBox> <mx:Button label="Load" width="100" height="30" click="load(event)" /> <mx:Script> <![CDATA[ import mx.rpc.http.HTTPService; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import flash.utils.getTimer; private var _start:uint; private var _str:String = ""; private function load(e:Event):void{ var req:HTTPService = new HTTPService(); req.url = "http://127.0.0.1:3000/users.xml"; req.resultFormat = HTTPService.RESULT_FORMAT_E4X; req.send(); req.addEventListener(ResultEvent.RESULT,onResult); req.addEventListener(FaultEvent.FAULT,onFault); _start = getTimer(); } private function onResult(e:ResultEvent):void{ dg.dataProvider = e.target.lastResult.user; _str += (getTimer() - _start).toString() + " msec\n"; loadText.text = _str; } private function onFault(e:FaultEvent):void{ loadText.text = e.fault.faultDetail; } ]]> </mx:Script> </mx:Application>
Flex側 (RemoteObject - AMF)
MXMLの骨格はHTTPServiceを使う場合と同じ。スクリプト部分とservices-config.xmlの設定は以下の通り。
スクリプト部分
<mx:Script> <![CDATA[ import mx.rpc.remoting.RemoteObject; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import flash.utils.getTimer; private var _start:uint; private var _str:String = ""; private function load(e:Event):void{ var ro:RemoteObject = new RemoteObject(); ro.source = "UsersController"; ro.destination = "rubyamf"; ro.index(); ro.addEventListener(ResultEvent.RESULT,onResult); ro.addEventListener(FaultEvent.FAULT,onFault); _start = getTimer(); } private function onResult(e:ResultEvent):void{ dg.dataProvider = e.result; _str += (getTimer() - _start).toString() + " msec\n"; loadText.text = _str; } private function onFault(e:FaultEvent):void{ loadText.text = e.fault.faultString; } ]]> </mx:Script>
services-config.xml (コンパイラオプションに追加)
<?xml version="1.0" encoding="UTF-8"?> <services-config> <services> <service id="remoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage"> <destination id="rubyamf"> <channels> <channel ref="ch-rubyamf" /> </channels> <properties> <source>*</source> </properties> </destination> </service> </services> <channels> <channel-definition id="ch-rubyamf" class="mx.messaging.channels.AMFChannel"> <endpoint uri="http://127.0.0.1:3000/rubyamf/gateway" class="flex.messaging.endpoints.AMFEndpoint" /> </channel-definition> </channels> </services-config>
結果
HTTPService - E4X
load()からonResult()までの時間測定は右テキストエリア内(msec)。10回測定でだいたい0.5-0.6秒。ちなみにRESULT_FORMAT_OBJECT(ActionScript Object)で受信すると0.7-0.8秒ぐらいだった。
RemoteObject - AMF
HTTPServiceの場合と同じ測定で一回あたり約3秒。
AMF、通信設定が面倒なくせにすげー遅い。Ajax and Flex Data Loading Benchmarks | James Ward - RIA Cowboyの結果と違いすぎる。どっか間違ってる?
[Flex][Ruby]Ruby唐辛子
ソースコード HTML化 コンバーター「唐辛子」という非常に便利なアプリケーションがあるんですが、残念ながらRubyに対応していません。そこでRubyコード変換機能を追加したものをリリースしました。僕が使いたいというのが最大の理由なので必要最小限のプログラミング言語(Flex, ActionScript, JavaScript, Ruby, HTML, and XML)のみ変換可能です。その他の言語については本家をご利用ください。
Rubyコードはこんな感じに表示されます。
# POST /projects # POST /projects.xml def create @project = Project.new(params[:project]) respond_to do |format| if @project.save flash[:notice] = ‘Project was successfully created.’ format.html { redirect_to(@project) } format.xml { render :xml => @project, :status => :created, :location => @project } else format.html { render :action => "new" } format.xml { render :xml => @project.errors, :status => :unprocessable_entity } end end end
参考:
八角研究所 : Flex2でソースコードをブログに貼り付けるツールを作る(1) - ブログに美しいソースコードを貼り付けよう
八角研究所 : Flex2でソースコードをブログに貼り付けるツールを作る(2) - ソースコードをブログに貼り付けるツールを拡張しよう
Flex + Ruby on Rails + MySQL 連携メモ
Tags: Flex, MySQL, Rails, Ruby
データベースの情報をRailsでXMLファイルに整形しFlexに読み込ませる。Model-View-Controller(MVC)パターンで言うとMCをRailsに、VをFlexに担当させる。今までデータベースへのアクセスはPHPスクリプトを書いて行っていたが、Railsを用いるとその面倒くさいテンプレート書きが必要なくなるので非常に快適。用いたのはFlexSDK 3.0, Ruby 1.8.6, Rails 1.2.6, MySQL 5.0.5。Railsは最新の2.xを用いても特に問題ないと思う。以下の例ではデータベースに格納されたの本の詳細(title, price, description)をRailsで管理、FlexのGridDataに読み込ませている。
Rails側
Railsで適当なプロジェクト(ここではflex_connect)を作成。コマンドラインより
rails flex_connect
データベースを作成。MySQLコマンドラインより
mysql> create database flex_connect_development;
Railsプロジェクト内config/database.ymlを設定(今回はdevelopmentのみ使用)。
development: adapter: mysql database: flex_connect_development username: root password: [YOURPASSWORD] socket: /tmp/mysql.sock
次にモデルを作成(ここではBook)。Railsプロジェクト内に移動してコマンドラインより
ruby script/generate model Book
Railsプロジェクト内、db/migrate/xxx_create_books.rb(xxxの部分はrailsのバージョンによって異なる)を以下のように修正。
class CreateBooks < ActiveRecord::Migration def self.up create_table :books do |t| t.column :title, :string t.column :price, :float t.column :description, :text end end def self.down drop_table :books end end
コマンドラインより
rake migrate
これによりDB: flex_connect_developmentにテーブルbooks(title, price, description)が作成される(MySQLを直接触らなくてよい!)。テーブル内に適当にテストデータを入力しておく(title=”Flex3″, price=”54.99″, description=”Adobe developer library”, etc.)。Railsから入力してやるのが簡単だがここでは割愛。
次にコントローラーを作成。コマンドラインより
ruby script/generate controller Book
Railsプロジェクト内、app/controllers/book_controller.rbに以下のメソッドを定義。
class BookController < ApplicationController def make_xml @books = Book.find(:all) render :xml => @books.to_xml end end
これで”http://localhost:3000/book/make_xml”にアクセスするとデータベース情報がXML形式で出力される。例えばこんな感じ。
<?xml version="1.0" encoding="UTF-8"?> <books> <book> <description>Ruby on Rails made easy.</description> <id type="integer">3</id> <price type="float">34.99</price> <title>Rails Solutions</title> </book> <book> <description>The Adobe developer library guide for rich interner application developers</description> <id type="integer">4</id> <price type="float">44.99</price> <title>Flex 3 Cookbook</title> </book> <book> <description>Friends of ED Adobe lerning library.</description> <id type="integer">5</id> <price type="float">54.99</price> <title>The Essential Guide To Flex 3</title> </book> </books>
Flex側
“http://localhost:3000/book/make_xml”の内容をDataGridに表示させるソースは以下の通り。上記RailsのXMLファイルを出力するURLをロードしてDataGridに格納している。詳細は割愛。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init(event)"> <mx:DataGrid dataProvider="{_bookList}"> <mx:columns> <mx:DataGridColumn dataField="title" /> <mx:DataGridColumn dataField="price" /> <mx:DataGridColumn dataField="description" /> </mx:columns> </mx:DataGrid> <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.rpc.http.HTTPService; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import mx.controls.Alert; import flash.net.URLRequestMethod; [Bindable] private var _bookList:ArrayCollection private function init(e:Event):void{ var req:HTTPService = new HTTPService(); req.url = "http://localhost:3000/book/make_xml"; req.method = URLRequestMethod.POST; req.send(); req.addEventListener(ResultEvent.RESULT,onResult); req.addEventListener(FaultEvent.FAULT,onFault); } private function onResult(e:ResultEvent):void{ _bookList = e.result.books.book; e.target.removeEventListener(ResultEvent.RESULT,onResult); e.target.removeEventListener(FaultEvent.FAULT,onFault); } private function onFault(e:FaultEvent):void{ Alert.show("Could not connect the database.","ERROR"); e.target.removeEventListener(ResultEvent.RESULT,onResult); e.target.removeEventListener(FaultEvent.FAULT,onFault); } ]]> </mx:Script> </mx:Application>
まとめ
今回はRails側にMVCパターンのMCを担当させた。同様にしてRails側のコントローラーに適切なメソッド(データの追加・削除 etc.)を追加することで専用のURLを簡単に作成できるので、Flex側は必要なURLにアクセスするだけでよくなった。
[Books] Ruby on Rails - up and running
結論: Railsのバージョンの違いは致命的。この本は扱っているRailsのバージョンが1.1と古いので買うべからず。買うならRails 2.xと明記した最近の本を。
O’reillyでRuby on Railsを扱っている本がこれしかなかったので盲目的に買ったが失敗。扱ってるRailsのバージョンが古すぎて(1.x)、現行のRails 2.xだと四苦八苦することが多い。本自体は150ページと割と薄めで、一つのRailsアプリケーションを題材に進行していくため、(頭に入るかどうかは別として)よみすすめやすくはある。ただしRuby自体の教本ではないので(それほど親切に説明はない)Rubyのリファレンスは必要。僕は平行してThe Ruby Programming Languageを読んだ。
Railsのバージョンアップは初学者にはかなりの障壁になるようなので、もし手持ちの本がRails 1.xを扱っているようであれば、Railsのバージョンを落として学ぶか(仕組みを覚えてから2.xに移行)、2.xを扱う本を買った方が効率がよいと思う。以下Rails 1.x > 2.xの違いについて参考にしたページ。
- Rails 2.0移行でつまずくポイント
- Rails 2.0 を使うときに注意すること その 1 - プログラミングのこととか
- InfoQ: Rails 2.0 - 初心者がすべきことは?
- よしたんのぶろぐ scaffold ? paginate ? なんじゃそれ?
追記 2008/08/08
レーダーチャートによる評価レビューはこちら






