高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

如何在 Rails 裡使用 AMF

前言

常聽很多朋友會問:「flash要怎麼樣跟資料庫串接?」。

答案其實很簡單:「不行!目前flash沒辦法直接與資料庫串接」。

那到底別人是怎麼做的? 為什麼他們的Flash可以由後台管理而且更新資料? 其實運作流程是這樣:

Flash(*.swf) <–> Server端程式(ASP, ASP.NET, PHP..etc) <–> DB

SWF透過HTTP GET/POST的方式送給中間的程式,透過中間的程式,Flash才得以從資料庫中取出/寫入資料。

其中SWF與server端程式溝通的格式,有簡單的純文字組合,或是用JSON或XML來包裝資料,再餵給SWF,最後再呈現在Flash上。特別是XML對AS3還滿友善的,可以簡單的就取出指定節點的資料。

而今天要提的 AMF(Action Message Format),其實做的工作跟上面的JSON或XML差不多,只是它的格式是binary的。

AMF的實作,各家程式語言或Framework都有類似的實作品 像是PHP就有AMFPHP/Zend_Amf,Python有PyAMF,.NET有FluorineFx,Ruby的話則有RubyAMF

不過不管是哪家實作的AMF,流程上都差不多:

  1. SWF連上指定的Gateway。
  2. 呼叫/執行Gateway上掛載的service,並把所需的參數以AMF格式傳給它(如果有的話)。
  3. 執行結果回傳。

最近手邊有個案子正好是用Rails寫的,剛好有用上AMF,就趁這個機會寫一下心得,免得自己以後忘記(其實在Rails裡面,用respond_to直接render產出xml或json也還滿方便的..)

環境

  • Mac OX 10.6
  • Rails 2.3.5
  • Ruby 1.8.7
  • RubyAMF 1.6.5

安裝

就不多做Rails的介紹了,直接開個空白的Rails Project來做示範:

> rails rubyamf_demo
> cd rubyamf_demo

接下來,安裝RubyAMF。網址:https://github.com/victorcoder/rubyamf_plugin

個人比較建議直接用script/plugin來安裝,簡單方便。當然如果要自己下載打包檔再手動放進來也ok的。

> script/plugin install git://github.com/victorcoder/rubyamf_plugin.git

沒問題的話,這個動作就會幫你把RubyAMF安裝在vender資料夾裡了。

image

其實這個安裝的過程中,除了把相關檔案裝到vender裡之外,背後有幫忙做了一些事你可能需要知道的:

1. app/controllers/ 裡多了一個rubyamf_controller.rb

這是整個RubyAMF的對外窗口,gateway就是寫在這裡了。

2. config/ 裡多了一個 rubyamf_config.rb

RubyAMF的設定檔,打開這個檔案應該可以看到許多註解說明,可依情況及個人使用習慣做調整

3. config/initializers/mime_types.rb多加了一行

Mime::Type.register "application/x-amf", :amf

到時候可以像 render :text => "hello"一樣,直接用render :amf => "hello"來輸出

4. config/route.rb多了一行路徑設定

map.rubyamf_gateway 'rubyamf_gateway', :controller => 'rubyamf', :action => 'gateway'

gateway到時候的位置就是http://127.0.0.1:3000/rubyamf/gateway

5. public/ 資料夾多了一個 crossdomain.xml

常用AS在串接外部資料的人應該知道這是幹嘛的了,預設是全開

<allow-access-from domain="*" />

如果有需要調整可直接動手修改。接下來試著啟動server,看看能不能正常運作:

> script/server

沒問題的話,接著開瀏覽器看看http://127.0.0.1:3000/rubyamf/gateway/。如果你看到一個黑色的畫面,中間放著一個RubyAMF的logo:

image

恭喜你,目前這樣就算是把RubyAMF安裝起來了。接下來,就要開始準備寫service上去了

實作

Rails部份:

先建立一個model,待會我們會用來取出/寫入資料用的:

> script/generate model book author:string content:text

目前只放了author跟content兩個簡單的欄位(for demo purpose, Model部份沒有特別做驗證)。

> rake db:migrate

為了省去另建資料庫的麻煩,這裡直接使用預設的SQLite做為資料庫。table建立後,先塞一筆測試資料進去:

> script/console
>> Book.create(:author => "eddie", :content => "this is a RubyAMF demo")

再來新增一個controller,裡面放一個hello_world這個action:

> script/generate controller amf_test hello_world

在hello_world這個action裡,我們加一行:

1
2
3
def hello_world
  render :amf => "Hello AMF"
end

大部份網路上看到的範例都是用Flex當範例,不過我個人比較偏好Flash。以下我就用Flash當做範例示範(其實沒太大差別啦,純粹個人喜好)。

Flash部份:

檔案:amf_hello.fla

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var nc:NetConnection = new NetConnection();
nc.objectEncoding = ObjectEncoding.AMF3;
nc.connect('http://127.0.0.1:3000/rubyamf/gateway');

var responder:Responder = new Responder(onOK, onErr);
nc.call('AmfTestController.hello_world', responder);

function onOK(res:Object):void
{
    trace(res);
}

function onErr(res:Object):void
{
    trace("Error!");
}

其中比較需要注意的是nc.call那一段,直接呼叫ControllerName.ActionName就行了。

按下Ctrl+Enter之後,應該就可以看到”Hello AMF”的字樣了,代表你的SWF已經可以成功從AMF Gateway讀資料回來了。如果這裡有發生錯誤,可能檢查一下是不是有打錯字,或是server忘了啟動。

接下來,我們試著送資料給Gateway,讓它寫入資料庫之後再回傳目前資料庫裡的書總共有幾本。

這次我們先做Flash端的介面,我放了一個按鈕跟二個輸入框,instance name分別取名為add_btnauthor_txtcontent_txt

檔名:amf_addbook.fla

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
add_btn.addEventListener(MouseEvent.CLICK, click_handler);

function click_handler(evt:MouseEvent):void
{
  var nc:NetConnection = new NetConnection();
  nc.objectEncoding = ObjectEncoding.AMF3;
  nc.connect('http://127.0.0.1:3000/rubyamf/gateway');

  var amf_object:Object = {
    'author': author_txt.text,
    'content': content_txt.text
  };

  var responder:Responder = new Responder(onOK, onErr);
  nc.call('AmfTestController.add_book', responder, amf_object);
}

function onOK(res:Object):void
{
  trace("目前共有" +res +"本書");
}

function onErr(res:Object):void
{
  trace("fail");
}

其實只是把呼叫的部份放到click handler裡,並且在裡面做了一個object,並且在呼叫的時候:

1
nc.call('AmfTestController.add_book', responder, amf_object);

把它當做參數傳出去。

接下來,在Rails裡,我們要來加一個add_book這個action,準備接收資料:

1
2
3
4
5
6
7
8
def add_book
  if is_amf
    Book.create(:author => params[0][:author], :content => params[0][:content])
    render :amf => Book.count
  else
    render :text => "error"
  end
end

這裡可以用is_amf來檢查傳進來的是不是AMF,傳進來的參數可用 params[0] 取得。

接著執行Flash,沒問題的話,當每次按下Add Book按鈕時,它就會把author跟content資料寫入DB,並回傳目前總筆數。

以上為展示目的,都沒有加資料的驗證,所以就算空白資料也可以送出。

以上是一些個人小小的心得,供大家參考囉

相關網站:

原始檔下載(fla + rails project):

http://nayumi.myweb.hinet.net/downloads/sample.zip

update: RubyAMF專案已由原本的google code移至github

Comments