高見龍

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

Ruby on Rails 之門外漢心得

image

這不是討戰文,也不是一篇討論程式語言的誰優誰劣的文章,僅就我個人這半年來使用Ruby on Rails(以下簡稱RoR,不是那個那個RO線上遊戲)用在工作上的一些簡單的心得跟大家分享。

很多廠商喜歡做網路活動來促銷新商品、打知名度或是跟網友拉近距離。目前廠商們比較愛的網路活動,大概有上傳照片、影片、票選、轉寄、推薦..等,而隨著” 社會的進步”,客戶們也”得跟上時代”,都不免俗的要跟FB或plurk弄在一起,而我的工作,主要就是幫客戶處理這樣的需求。(我想這段我的同業應該知道我不是在講真心話!)

就我而言,我個人比較熟悉的程式語言算是ActionScript 2/3,大部份的工作也都是跟紅紅的Flash為伍。但後來的網路活動越弄越複雜,單純的Flash已經不見得夠用了,很多時候得跟server端交換資料。

從最早的ASP,後來換到PHP,其實都還不錯用,也沒什麼問題(不然PHP現在不會有這麼大的市佔率)。後來試著用PHP的Zend Framework,因為現在比較流行的web framework都有照著MVC(Model-View-Controller)的樣子來打造,寫起來程式碼跟頁面有更清楚的感覺。

2009年試著換到PythonDjango,它也是MVC的實作,感覺還不賴,有種程式寫起來可以打更少字的感覺。而且它所謂的batteries included,只要把一些設定打開,後台管理功能就直接做起來了,相當方便。

2010年起,我試著開始把案子用RoR來做,整個有種「我終於可以把時間花在吃喝玩樂寫AS上了」的感覺了。

以下是一些大家可能會有的疑問:

Q: 為什麼你要一直換?

A: 我是善變的人 :)

Q: 你是寫AS的,為什麼要自己寫server端的程式?

A: 情況允許的話當然我也想跟別人一起合作寫,不過網路活動很多都是急件,而且客戶通常不太會管你六日週末要不要休假。後來發現其實自己寫真的還挺快的,而要什麼資料或是要接什麼資料都可以自己加一下就搞定,免看人臉色。

Q: RoR是一種程式語言嗎?

A: 不是,它是一種web framework,它本身用的程式語言是Ruby。

Q: RoR要錢嗎?

A: 不用的,而且你有興趣的話,還可以取得完整的原始碼,印出來上廁所的時候閱讀 :)

Q: 為什麼用RoR來開發會快?

A: 以下的動作我先假設開發的工程師有一些RoR的開發經驗。

以一個發票登錄的程式來說,假設它需要使用者姓名、電話、地址、Email及發票號碼等幾個欄位,我的動作就是先建立一個Model來對應這些欄位(這動作 大概1~2分鐘左右可完成)。再來就是建立一個Controller來準備接收使用者submit出來的動作(大概1~2分鐘左右),而實際在 Controller裡寫的程式碼,也就是把Model給叫出來,把使用者post的資料餵進去,程式碼如下:

1
invoice = Invoice.new(params[:invoice])

最後再一個save的動作:

1
invoice.save

差不多就搞定了(這動作大概1分鐘左右可完成)

剛剛這些資料庫寫入的動作,再加上一些if..else..的判斷,大概10行程式碼有找。整個發票程式部份,不包含設計的話,大概5分鐘能寫完,也許如果再加個發票重複登錄判斷的驗證,最多應該10分鐘搞定。

那要怎麼把資料庫裡的東西撈出來? 通常只要一行:

1
invoice = Invoice.all

(大概3~10秒,依打字速度不同而有差異)

那要怎麼樣把它餵給Flash? RoR的ActiveRecord有支援直接輸出成XML或JSON的方法,簡單的可以直接用invoice.to_xml 或invoice.to_json 就行了(大概10秒),複雜一點的,也可以用xml builder來建個模板給它(看複雜程度,大概5分鐘內搞定)。

藉由ORM(Object-Relational Mapping)的包裝,把原本要用SQL操作的動作,都改用物件的方式來操作。而大部份的網路活動的寫入/查詢語法都挺單純,沒意外的話應該都不用自己寫 SELECT * FROM 或是 INSERT INTO之類的東西了,不只操作簡單,也可避開SQL Injection的麻煩。

另外RoR的另一大優點,就是它有相當多好用的外掛(plugin),而且幾乎都是免費可以取得的(包括原始碼)。

一樣以網路活動上傳照片這個來舉例,上傳照片之後,通常得自動再產生一些比較小尺寸的縮圖,可能還要裁切一下圖片。透過某個方便的外掛(paperclip),上面這些動作,真正要寫程式好像不到5行吧,搞定之後會有種「哇賽!怎麼就這樣就可以了?」的驚奇感。

再例如上傳照片後,資料一多,前端呈現的頁面通常就得有分頁的功能,而這功能透過某個方便的外掛(will_paginate),大概只要多加一行程式碼就可以搞定分頁的工作。

Q: 可是你知道,做網路的活動,客戶很愛改來改去…RoR改起來會不會很麻煩?

A: 網路活動的特性,就是變動性高。除了設計部份可能會被改來改去外,一會可能要你加個中獎名單,一會說這個頁面要放到另一個頁面之後…愛改來改去這件事,我想這個是許多同業的痛!

老實說,就以上面的例子來說,就算整個砍掉重練,也不見得花多少時間;如果是修改流程,說不定改一下route規則就可以技巧性的處理掉了,其實面對愛改來改去的狀況,RoR還是個不錯的應對工具。

Q: RoR效能好嗎?

A: 我沒比較過所有家的程式語言以及web framework,所以我沒辦法下這個結論。通常如果遇到效能差或是資料庫負載很吃重的地方,可以把它做cache或產出靜態頁面來提昇效能,而做 cache或是把cache給expire掉的動作,在RoR裡是很容易做的。

Q: 那,Ruby好寫嗎?

A: 我以AS程式碼來舉個例子:

1
2
3
4
if (my_age >= 30)
{
  trace("you're so old!");
}

改用ruby寫的話:

1
2
3
if my_age >= 30
  puts "you're so old!"
end

基本上少了小括號、大括號,也不用分號結尾,至少就可以少打好些個字,甚如果想要的話,if也可以放到後面:

1
puts "you're so old" if my_age >= 30

如何? 看起來更像一般的英文吧!

再例如如果我想要印出3天前的日期,有的程式語言可能要先new一個Date物件出來,再透過一些method可以取得日期。在Ruby裡,只要用3.days.ago就行了。3? 它不是數字嗎? 是的,你沒看錯,在Ruby裡什麼東西都是物件,包括數字也是,所以你可以對3這個物件呼叫方法,再來個例子:

1
2
3
3.times do
  puts 'hello'
end

猜得出來它在幹嘛嗎? 它就是把”hello”這個字串印3遍..

其它關於這個程式語言的細節,如果想了解更多,可參考Ruby的官網,有相當多的文件可以看。

Q: 在Flash裡可以用AMF的格式與server端溝通,RoR能做嗎?

A: 可以的,請參考http://blog.eddie.com.tw/2010/03/18/rubyamf

Q: 還有什麼好處?

A: 前面提到,由於大部份的客戶想要做的東西都差不多,例如發票登錄來說,可以的話,也可以把這些常用的功能也做成「發票plugin」,也許下次要用的時候,就把plugin裝上去,可能再做一些微調就行了。

Q: 那,好維護嗎?

A: 基本上RoR的骨架是依照MVC的架構來實做的,所以某A或某B寫的程式碼,只要寫的人不要用太奇怪的寫法,大概都可以知道XX程式或XX功能會放在哪裡(當然改不改得動是另一回事)。重點是,很多網路活動都是短期的,活動結束就會下架,老實說並沒有太多所謂的維護的問題 :)

Q: RoR好像被我講得很神,那它缺點是什麼?

1. 它的學習曲線的高低決定於開發者的背景知識跟經驗

如果你之前就有用別的程式語言開發過網站,那RoR對你來說應該會比較快上手;如果你對於什麼是Form、什麼是GET/POST還不清楚的話,那這個對你 來說可能會有些吃力,很容易卡關。特別是如果沒有個導師帶入門,還真的很容易碰壁。 (PS: 其實最好還有一些*nix的操作經驗,以及熟悉一些版本控制系統如SVN或Git)

2. 台灣的hosting比較不好找

台灣目前使用的人口還是相當少,國外雖然有比較多的solution,但客戶的網路活動的TA大部份都是在台灣,可能會有連線速度不夠快的問題。這個我解決的方法就是,我自己弄一台機器放機房,自己弄環境起來 :)

結論

綜合以上各項優缺點,我真的覺得RoR挺適合拿來做網路活動,當然做長期的官網也合適,這裡就我自己工作上常遇到的狀況提供一些建議。

以上,僅以我這半年來用RoR的一些小小心得與大家分享,我也還是個一隻腳剛踏進去的新手門外漢,內容若有誤再請前輩、神人們不吝指教 :)

Rails Console / irb 裡的方便工具-Hirb

方便的工具,寫起來給自己做個備忘,免得到時候忘記 :)

在做資料庫存取的時候,常會把抓到的model object印出來看看到底內容正確與否。假設我有個名叫Book的model,我從裡面隨便抓一筆資料出來大概會長這樣:

image

除此之外,也可以透過y方法把內容以YAML格式輸出:

image

其實這樣也不會多難看,反正看久就習慣了。剛好最近在看Railscasts的時候才發現原來有更方便的gem可以用,它叫做Hirb,可以讓在console模式下輸出的內容更清楚了。

官網的安裝說明其實就很清楚了,簡單的說,就是用gem一個動作就搞定了:

> sudo gem install hirb

進入rails project裡的console模式後:

>> require 'hirb'
>> Hirb.enable

接下來,剛剛那個object會變成:

image

如果再加一下logger的設定,還可以讓輸出的同時把查詢的log也秀出來:

image

當你不想用的時候,只要用:

>> Hirb.disable

就可以把它關掉了。

如果覺得hirb很方便但又不想每次進console都打這些指令的話,也可以把上面這些動作放到.irbrc裡(檔名: ~/.irbrc)

1
2
3
4
5
require 'rubygems'
require 'hirb'
require 'active_record'
Hirb.enable
ActiveRecord::Base.logger = Logger.new(STDOUT)

當然,hirb不只有這樣而已,不過光是這樣我就覺得很棒了。更細節的說明請洽網站 :)

如何在 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

2009 PTT Flash 版聚

image

今年八月底聚集了一些對Flash有熱好的朋友們,讓大家互相認識一下平常在版上互動的人長什麼樣子,也順便透過幾個專題演講來分享一下心得。

活動照片:https://picasaweb.google.com/108852467279874647445/PTTFlash

這應該算是PTT Flash版第二度的板聚(上一次是在2008年的八月,已經相隔一年了)。

Flash在台灣一直比較少見有專門大型的研討會,比較大型的大多是伴隨產品上市的發表會一起辦。

雖然我們這個不是官方舉辦或贊助的活動,但我還是很羨幕能有像Adobe MAX或是Flash on the Beach那樣大型的研討會。

希望有一天能在台灣也能看到純粹技術發表的研討會 :)

至於我們這個PTT Flash Workshop,我想希望可以把這樣的聚會變成固定舉辦的活動,也許可以辦在寒、暑假,然後再來去借個比較大一點的場地來辦party好了。

影片

Jones - 製作自己專屬的Facebook應用程式

Cjcat - Stardust Particle Engine

高見龍 - MVC

戰利品

image

難得給自已排了一段時間出去玩,這趟去英國溜躂,音樂劇Les Miserables是心靈上最值回票價的體驗,當然物質上的一些戰利品也是不可少的啊(其實根本就是去買玩具的吧)

1. 電話亭存錢筒:

是鐵製的,拿在手上挺有份量(存錢這是一定要的啊)

image

2. 小羊鑰匙圈

這個是在湖區買的小羊鑰匙圈,造型真是超可愛

image

3. 懷舊寶箱

這是在二手店看到的小寶箱,不知道為什麼我看了就覺得很愛

image

4. 超難拼圖

號稱世上最難的拼圖,雖然只有529片,但拼起來像是4,000片?!

image

5. Doctor Who模型

大概二、三十公分高,人物部份要自已著色,Tardis在啟動的時候會發出跟影集一樣的聲音。

image

6. Daleks

有看Doctor Who的人一定知道這像垃圾筒的機器人是誰啊….

image

而且壓它還有音效的咧,真讚!

7. 其它

每個景點專屬的名信片當然也是不可少的(本來要寄的,但最後都捨不得只寄了一張最不喜歡的出去了..)

另外還有個搖控的小直昇機(給我爸玩的)

結論:

要努力工作,趕快把錢賺回來了..!!!