顯示具有 rails 標籤的文章。 顯示所有文章
顯示具有 rails 標籤的文章。 顯示所有文章

1/09/2009

ReCAPTCHA 與 ReCAPTCHA Rails Plugin

我們不管是註冊,或是去便當狗下載東西,都會有一張圖裡面有字,然後要你 keyin 正確的字。那個東西叫做 CAPTCHA。 CAPTCHA 是 ”Completely Automated Public Turing test to tell Computers and Humans Apart”的縮寫,主要用途是為了防止 robot Spam所做的手段。我看過最討厭,最難懂得 CAPTCHA 就是 Google 的 CAPTCHA,每次看都要硬猜個一兩次才會對,大概是他們整天被 Spam 攻擊,只好連真人都開始防堵 XD

而什麼是 ReCAPTCHA 呢? ReCAPTCHA 是卡內基美隆大學的一項計畫,由卡內基美濃學院資訊科學系執行。主要目的是為了加快實體文本數位化而作的一個手段,一般來說實體文本數位化需要的是數位掃描的技術,但是還是有很大量的書籍裡面的字礙誨難懂,有可能是紙張變質,也有可能是以前人的書寫習慣不公整(就是講我XD),這個時候就需要人力去辨識字的內容,並且給予機器正確的 return,順便 training 數位技術 Algorithm。很可惜的是,人力是要錢的。所以他們想出一個聰明到嚇死人的方式來處理這個問題。

他們開發了一個網站,叫做 ReCAPTCHA 。裡面提供 CAPTCHA API ,讓網站開發者當需要 CAPTCHA 的技術時候,可以直接 Implement 這個 API,而不需要依賴自己重新開發 CAPTCHA 技術。他們並且號招很多志願者開發各種語言版本的 API,來加速網站開發者 implement ReCAPTCHA的速度。以下是 ReCAPTCHA 的截圖



那為何他要免費提供這樣的服務呢?我們來仔細看他的技巧來自於 CAPTCHA 圖片本身,ReCAPTCHA 的圖片都是兩個英文字



左邊的字 following 是他們已經確認掃描正確的字,右邊的 finding 是他們還無法用掃描確定意義的字。當使用者在 keying 認證碼的同時,他只要確定左邊的「following」是正確的,他們就認為是正確的 input,予以放行,但是使用者不是也 keyin 右邊的字嗎?那那個 input,ReCAPTCHA網站就視為你順手幫我做的影像校正,存到資料庫裡面。網站開發者免費得到 CAPTCHA 技術,ReCAPTCHA 獲得數以百萬記的免費人力幫忙校正,是個典型的雙贏局面。

至於 Ruby 裡面有沒有 ReCAPTCHA API 呢?當然有。ReCAPTCHA for Ra ils 主頁在此。安裝方式就是


ruby script/plugin install git://github.com/ambethia/recaptcha.git



安裝完成之後,你必須要先去 ReCAPTCHA 網站註冊,他會給你一個 PUB key 跟 Private key,將他記錄在 Rails 的 config/enviroment.rb 裡面




1
2
ENV['RECAPTCHA_PUBLIC_KEY'] = "public_key_goes_here"  
ENV['RECAPTCHA_PRIVATE_KEY']= "private_key_goes_here"


接下來就是使用 ReCAPTCHA 啦,你要將 ReCAPTCHA 放在你需要的地方,這邊唯一要注意的地方在於你必須要放在 form 裡面



1
<%= recaptcha_tags %>  



接下來要驗證對方 keyin 的認證碼正不正確,我們只需要在相關的 controller 放入




1
2
3
4
5
if verify_recaptcha  
#正確的處理方式
else
# 錯誤的處理方式
end


然後,沒啦,很簡單吧!!!

12/24/2008

金剛合體:Merb 將要 merge 到 Rails 3

我以為 Rails 2.2 已經很有創意了,弄出很多新東西(or 應該做的東西) ,Rails 3 到底要做啥東西才能夠讓人家驚訝了呢?噹噹,我發現到 Rails 還是嚇到我了 。

Big news 就是 Merb 跟 Rails 3 預計在 2009 的 Rails conf Merge 起來,Merb 作者 wycats( Yehuda Katz)會加入 Rails Team。請注意,我知道很瘋狂,不過這個消息已經經過 Rails And Merb 雙方證實了。

DHH 指出這次的 Merge 有幾點工作要做

  1. Rails core:Rails 是一個 full stack framework ,不過他要參考 merb ,讓人很簡單的做出 rails myapp–core 和 rails myapp—flat
  2. 效能強化
  3. Framework 解構:雖然 Rails 預設依舊是 ActiveRecord 當 ORM,不過你也可以選擇 data mapper,Sequel,Template 可以選擇 HAML,AJAX 可以選擇 jquery
  4. Rigorous API:反正就是要解決 Rails 一升級 plugin 就炸掉的問題

怎麼看起來像是又生出一個 Merb ?

我來猜猜,DHH 認為 Merb 社群長久以來的堅持是對的,而 Rails 社群漸漸走錯路, DHH 眉頭一皺,認為案情並不單純,利用自己還很龐大的力量吃掉 Merb ,然後順便導正 Rails 該走的路。

It is not good news , and it is also not bad news,It's AMAZING news。


12/16/2008

Rails 2.2 在 Windows 上面有關 mysql Gem 的問題

今天要幫公司同事灌 Windows 上面 Rails ,發現安裝 mysql gem 出了點問題 ,本來是不用管,直接使用 Rails 附帶的 Mysql, 不過自從 Rails 2.2 之後,Rails 原本附帶的 mysql adaptor 已經移除掉adaptor,所以被迫得安裝 MySQL Gem 。

The bundled mysql.rb driver has been removed from rails 2.2. Please
install the mysql gem and try again: gem install mysql."
當我 gem i mysql,本來是沒問題的,不過現在不知道 gemspec 那裡改壞了,導致出現問題
Installing ri documentation for mysql-2.7.3-x86-mswin32...
Installing RDoc documentation for mysql-2.7.3-x86-mswin32...
ERROR: While generating documentation for mysql-2.7.3-x86-mswin32
... MESSAGE: Unhandled special: Special: type=17, text=""
... RDOC args: --op
所以第一個問題解法就是不搞 RDOC,直接使用 gem i mysql --no-ri --no-rdoc 安裝

當我以為已經解決的時候,沒想到出現第二個 Error
This error occurred while loading the following files: mysql
靠,Rails 2.2 + Windows 還真多災多難,解決方式根據這裡,裡面說到要把 MySQL 帶的 libmysql.dll 複製一份過去到 ruby\bin 底下
copy mysql\bin\dll\libmysql.dll ruby\bin\
我使用的是 Instant Rails ,所以 MySQL 跟 Ruby 都放在同一個資料夾下面,如果安裝方式不同請自行處理。


10/26/2008

Rails 2.2 :多個願望一次達成...

購物專家:來來,我們現在請到 Rails 副總來本購物台,想請問副總對於這次 Ruby on Rails 2.2 來本台銷售,帶來了什麼好康。

副總:這次的 Rails 2.2 可以說是本 Rails 產品史上最大的升級,可以說是把所有想的到聽的到看的到的問題都一次解決,保證這次不買你就是笨蛋,還去寫 PHP 或是 Java 只能祝福你。

購物專家:啥,那麼可怕,那我想問問,有關之前被人詬病的 Rails 國際化的部份有何加強。

副總:記得上次我們來的時候怎麼講的?Rails 要國際化必須要 Gettext 或是 Plugin 來解決對不對。今天帶來的第一個優惠, Rails 2.2 直接內建 i18n 給你。如果有問題的話,請到Rails i18n來看看怎麼實作。基本上是寫一個 yaml 就可以解決了。

(罐頭歡呼聲)

購物專家:什麼,直接內建 i18n ,那其他 Framework 不是少了一個古老的暴點可以笑 Rails 了嗎?你們上面業績衝的很兇歐,優惠送那麼大。那以前最讓人詬病的效能問題呢?

副總:抱歉,其他家公司,你們災洗了。我們這次直接升級到 Ruby 1.9,讓你們享受 19 倍的效率提昇。(註:購物台都是隨便唬爛的,有提昇是沒錯,有沒有 19 倍就不知道XD)

(罐頭歡呼聲)

購物專家:什麼什麼,你們直接支援效率打敗 Python 2.5 的 Ruby 1.9 呀,那不就是筆記型電腦加 Ram 再加 ssd 這樣誇張歐。這樣你們不會獲利下降嗎?

副總:沒關係,只要可以服務購物台,我們可以流血犧牲

購物專家:導播,各位觀眾,我已經快受不了,那麼好的優惠,那麼好的內容只在今天的 XX 購物台。現在電話所有線路忙線中,請改撥語音專線可以優先訂購。副總,已經端出那麼多了,還有啥好康的?

副總:還有還有,我們改寫 ActiveRecord 了,支援 connection pool 摟,並且是 ThreadSafe 了。只要在 config/database.yml 裡面加上

development:
___adapter: mysql
___username: root
___database: sample_development
___pool: 100
___wait_timeout: 10

就等於以 100 connection 讓 model 讀讓你取,效能提昇直接上看 100倍 (註:購物台都是隨便唬爛的,有提昇是沒錯,有沒有 100 倍就不知道XD)

(罐頭歡呼聲)

購物專家:現在耳機傳來導播的聲音,現在我們備貨數量已經真的真的很少了,大家太踴躍了,記得撥打語音專線可以省一百塊歐。副總你們還有啥優惠嗎?

副總:我們加入了 HTTP 的 last modified since 的 support,讓上次沒有修改過的內容就直接回傳一個 empty response 回去。原本是要回傳幾百K的東西,現在至需要回傳 0 K ,效能提昇直接上看無限大(註:購物台都是.....算了,懶得講了XD )

(罐頭歡呼聲)
(罐頭歡呼聲)
(罐頭歡呼聲)

副總:ㄟㄟㄟ,導播,我的購物專家勒?

導播:他太激動了,回去學習 Ruby on Rails 了。

副總:ㄟㄟㄟ,那麼猴急,我還沒講完勒,http://guides.rubyonrails.org 上線了,可以直接上去看一些 online guilde。



言歸正題,此次的 Rails 2.2 真的是超乎想像的加碼再加碼,修正了很多以前的問題,也加入相當多新的功能。

1. i18n
2. Ruby 1.9
3. connection pool
4. thread safe
5. etag and last modified since

真的可以說是史上最大的升級 XD

6/05/2008

Gettext 在 Rails 2.1 下面的問題

Gettext 1.19 遇到 Rails 2.1 會發生以下的問題
NoMethodError (undefined method `file_exists?' for #):


解決方式就是寫一個 config/initializers/gettext.rb

require 'gettext/rails'
module ActionView
class Base
delegate :file_exists?, :to => :finder unless respond_to?(:file_exists?)
end
end



重起即可。

5/01/2008

Rails 2.1 RC Release

Rails 學習之路最困難的一件事就是,社群活力太強,改版太快了。Rails 2.1 RC Release了。請到Rails最新時尚的 github 去取用。

3/31/2008

Rails 2.0 使用 Big5 編碼

沒想到我的有生之年居然還會使用到 Big5!!!

沒錯,基於機緣巧合,我碰到了一個需要使用 Big5 資料庫,網頁也是 Big5 顯示的request。所以我必須匿天而行,強迫 Rails 2.0 使用 Big5。方法很簡單,

config/enviroment.rb 裡面加入
config.action_controller.default_charset = "Big5"

config/database.yml記得加入
development:
host: xxx
adapter: mysql
database: lala
port: 3306
username: root
password:
encoding: Big5

Html 記得加入
<meta http-equiv="content-type" content="text/html;charset=Big5" />

大概就這樣吧,我抱著參觀懷舊的心情來寫這個網頁 XD

7/28/2007

Active Scaffold Upload Branch

我真的被 rails plugin 嚇到了。這幾天試著做 web development, 大家應該都知道我跟這個領域不太熟吧?可是,忽然間我覺得,這樣幾乎什麼都能做了。三個 plugin, 依照我嘗試的時間順序:

1. FileColumn
2. LoginGenerator
3. Active Scaffold Upload Branch

由於一開始使用 FileColumn 碰到了點問題,所以我有稍微 trace 他的 code, 基本上這個 plugin 我覺得算是小玩具,很簡單的小功能,居然沒內建到 rails 中我覺得有點奇怪。畢竟 binary file 一般都是存到 file system 而非 db 中吧?搭配 rmagick 的感覺還算不錯,可惜 helper 似乎不太健全,雖然堪用了。

接下來碰到需要 auth 的部份,懶得自己寫,因為只是小地方需要,並不真正屬於我目前所要寫的東西的一部份。重新發明輪子我覺得不是問題,但如果只是想到旁邊的便利商店買罐飲料卻因此得重新發明輪子,呃,這是成本考量與優先序的問題啊。

於是試了一下 LoginGenerator, 發現﹍。嗯,還真的是很方便。雖然他是個 gem, 但是 generate 出來之後就沒有 dependency 了,而且要自己改什麼也都很容易。於是 auth 的部份也解決了,畢竟我只是要最簡單的 auth.(雖然他安全性做得如何我就不清楚了,沒有仔細看他的程式碼)

到這裡,我都還不覺得怎麼樣。第三個 ActiveScaffold 就真的嚇到我了。因為那真的是非常完整的實做。rails 內建的 scaffold 實在陽春過頭,(儘管陽春過頭了,第一次看到還是著實相當驚豔,現在有種鄉巴佬進城的感覺)除非是最簡單的資料,不然碰上 association 或是 file column, 全部都沒辦法應付。當然,我是可以自己去改自己去擴充,但同樣是成本與優先序的問題,一定得需要用別人的東西。

先是找到了 scaffold extensions, 其實我是比較欣賞這種模式。不要產生一堆程式碼,要改什麼,利用 ruby 的 dynamic 機制就好了。只是他文件實在不多,看來看去要擴充好像還滿有難度的,而且雖然號稱支援 association, 卻是﹍。我只能說真的很難用,不過是多個 link 出來罷了,是比沒有好啦,但是應該會邊用邊想打人吧?

接下來我乾脆直接 google file column + scaffold 算了。就找到了 Active Scaffold Upload Branch, which is evolved from ajax scaffold. 他的 file column 範例,呃,問題一大堆,有些根本就寫錯了,害我試半天試不出來,有點火大。想說調查一下他的 form 到底是怎麼回事,卻發現他居然整個是用 ajax 寫的,所以 rails 有錯誤不見得會回傳,而且 form 也因為動態產生而無法顯示!

幸好有 firefox 的 web developer 這個 add-on, 裝了很久卻很少在用,帶有僥倖的心情找了一下,發現他的 display 功能非常強大,舉凡頁面上出得來的,全部都能顯示。然後才發現,active scaffold 額外產生了一個 record 去存,這樣的話下面這一行就明顯不正確了!
<%= file_column_field 'entry', 'file' %>
我還是頭一次看到範例有這麼嚴重錯誤的,太相信他結果找錯誤找半天。:( 害我又一直懷疑到 file column 和 rmagick 上,看半天覺得應該沒錯才對。而且這一個 method 也不知道是幹嘛的:
 def file_form_column(record, input_name)
  file_column_field 'record', :file
 end
不是明明就有 partial 了嗎?還是這可以取代 partial? 存疑,不管了。

總而言之,現在不只是 file column 的 image upload 沒問題,要自訂欄位也沒問題,版面上又相當漂亮,不會因為拉扯而變形,排序、搜尋等等,也都相當不錯。最重要的是,他的 association 模式跟我所需要的幾乎完全一致!可以同步修改,可以隱藏,可以追加,所有基本功能似乎都有了。

本來昏昏欲睡的我,看到這邊實在是很興奮,這東西就算要直接拿來用,都夠格了。而且我相信有什麼不足的話,要修改也容易得很。這有一大半要感謝 Ruby :)

老實講,我真想知道硬幹派的 php programmer 看到這些會有什麼想法。一個 phpBB 我看隨便寫一寫說不定就跑出來了﹍。搞不好再過不久,寫網站就真的是只要呼叫 => 修改 => 呼叫 => 修改就結束了。這還真的是很恐怖的一件事。不過同時這也代表著,我們應該把眼光放遠點了。來做個 YouOS 吧!! XD

==

最後我還想講一件事,就是寫程式寫這麼久,從來沒碰過幾次時程預估是太長的。但寫 rails 這兩次,很明顯我都估太長了。而且縮短的程度也是非常的多!我想除了要感謝 library 的強大外,雖然我還沒引進 unit test, 只用了人工 test, debug 難度就已經沒有很高了,大部份的錯誤都不會很難抓。當然,這有一個很大的原因是我現在在寫的東西難度很低,可是在這麼不熟悉的情況下,我覺得能夠這樣就真的是非常厲害的了。

另一方面其實我是覺得寫 Flash 有趣得多,難度也高得多。但 Flash debug 真的是會讓人想抓狂的一件事﹍。不知道有沒有什麼好用的 debug tool? 不然寫 Flash 永遠進度落後實在是很煩。

==

script/plugin install http://opensvn.csie.org/rails_file_column/plugins/file_column/trunk

gem install login_generator
script/generate login

svn export http://activescaffold.googlecode.com/svn/branches/upload vendor/plugins/active_scaffold_upload

有時候真不知道用 rubygems 好還是 rails plugin 好,但我想比較不穩定的東西,用後者應該是會好很多吧?self contained 這種事,有時候還滿重要的。

鄉巴佬全文完。

2007.07.28

6/08/2007

JRuby on Rails 安裝方式

有了號稱可以跑 Rails 的 JRuby ,當然要來試試看 JRuby on Rails 怎麼安裝。因為我的 test 機器上面已經有裝好原先的 Ruby 跟 Rails ,所以我們就每個指令都打全部的執行檔路徑避免搞混。本 Tutorial 在 Linux 上面測試成功。



安裝 Rails

JRuby 有附帶一個 gems 在 $JRuby_PATH/bin/ 底下。我們使用我們最熟悉的方式來裝,就是 gem 安裝
$JRuby_PATH/bin/gem i rails -y
他會安裝 Rails package 到 $JRuby_PATH/lib/ruby/gems/1.8/gems/ 底下。以後所有的 JRuby Gems 都會安裝到這個下面。安裝好了後,$JRuby_PATH/bin/ 會多出 rails 這個指令,這個就是我們熟悉的 rails command。

安裝 MySQL JDBC Driver

因為 JRuby 不能裝我們熟悉的 ruby-mysql,所以使用 JDBC 來操作 MySQL。你可以到任何地方去安裝 MySQL JDBC Driver 的 jar 檔案,但是如果不知道那裡抓的話,可以試試看 MySQL 官方的 Connector/J 。然後 mysql-connector-java.jar 檔案放到 $JRuby_HOME/lib/ 底下。(註1)

安裝 ActiveRecord-JDBC

這是 Active Record 跟 JDBC 的介面,用 gem 來安裝
$JRuby_PATH/bin/gem i ActiveRecord-JDBC
安裝好了之後就可以使用 JDBC 來 Connection MySQL。

新增 Rails APP
熟悉的指令來了,只是請確定你是使用 JRuby 的 rails 來新增 Rails App
$JRuby_HOME/bin/rails app_name
修改 Enviroment config
rails 的 config/enviroment.rb 要稍微修改
Rails::Initializer.run do |config|
end
裡面要加上
RAILS_CONNECTION_ADAPTERS = 'jdbc'
這句,不然會出現 error。(註2)

Database config 修改

因為改成 JDBC 了,所以 config/database.yml 也會改成
development:
__adapter: jdbc
__driver: com.mysql.jdbc.Driver
__url: jdbc:mysql://localhost:3306/資料庫名稱
__username: root
__password:
這個樣子的設定方式,原先的 database 選項是無效的,必須放在 url 選項的最後面才可以使用。

啟動 Rails Server

終於要啟動了,啟動方式就是
jruby script/server
他會啟動 webrick 。

註1
我試過 $CLASSPATH 的方式,但是無法還是無法正確 connection ,會出現以下 error
The driver encountered an error: cannot load Java class com.mysql.jdbc.Driver
所以我採用這個方式。

註2
當沒有寫入 config/enviroment.rb ,啟動時會出現下列 error
> jruby script/server
=> Booting WEBrick...
/home/wisely/jruby-1.0/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/abstract/connection_specification.rb:209:in `establish_connection': database configuration specifies nonexistent jdbc adapter (ActiveRecord::AdapterNotFound)

6/03/2007

Rails 2.0 預計的新特色

Ruby on Rails 作者 DHH 在 Rails Conf 2007 主題演講 A peak at Rails 2.0,裡面提到很多 Rails 2.0 預計有的新功能,大致上還是走「加強 REST ,修改許多細節,尤其是 performance 」的方向走,現在我們就來 check 一下吧。由於投影片沒有講得很清楚,我也順便看了 Robbin 的 railsconf2007大会介绍




更好的 REST 支援
  • ActiveResource 正式 release ,不再只能去 check svn 來 download
  • 有了更輕巧的 REST , Action Web Service 要移出 Rails Core
  • HTTP Basic Auth 支援,解決 REST 對於 Auth 的問題
  • respond_to 可以直接輸出 RJS
  • 更強大的 mine_type


更好的 performance

最大的消息就是有 query cache 可以用了。並且他加了不少 static file 處理的強化技巧。可以用
<%= javascript_include_tag :all , :cache => true %>
<%= stylesheet_include_tag :all , :cache => true %>
自動合併以及壓縮一些 css 或是 js static file,順便下 cache。

而且可以在 config 裡面設置
config.action_controller.asset_host = 'static%d.example.com'
讓 images ,js,css 的 render 出來的路徑變成
<img src="static1.example.com">
<img src="static2.example.com">
類似這樣的形式,可以簡單的將 static file 分離到 static server。


更簡單的 Migration

你很討厭寫 t.column 嗎?現在,你可以這樣寫
create_table :people do |t| 
t.first_name :type=>:string
...
end
以及
create_table :people do |t| 
t.string :first_name
...
end
後者更勝一籌。

一些小細節

Breakpoint 回來啦。可以在啟動 Rails 的時候,將初始化動作寫到 initializers 裡面。

這裡是這個投影片


5/10/2007

Twitter , Rails , Scalibility...More

Twitter 是一個最近非常熱的 Web Site,他們主要是可以利用簡訊,網頁更新自己的近況。Twitter 的開發者 Alex Payne 在接受訪問的時候,拋出了一個震撼性的議題
Rails Scalibility 到底好不好的問題
夠震撼吧。我發現到很多人都開始發表了 「Rails 遇到效能上的問題...」。看到只能說,這是 Rails 社群第一個大挑戰,但是請不要太過武斷就直接認為是 Rails 的問題。如果大家仔細了解這個事情的情況,就可以大略推估應該是 Twitter Team 成功的太快,整個 team 的成長跟不上網站的成長速度。我們來看看到底事情的始末。

故事開始

Alex Payne 是 Twitter Team 其中一員,他接受了 Radical Behavior 的訪問,當被問到 Ruby on Rails 如何應付高速成長的流量時,他指出他認為 Ruby on Rails 有不少 Scability 的議題,Alex 的論點如下
  1. Ruby is slow
  2. Rails 一次不能 connection multi database
  3. Rails 有些東西 component,性能消耗太大,必須不去使用
此話一出,當然引起了 Rails 社群的積極辯護,跟其他語言社群基於「良心」的建議。當然,我們 Rails 社群當中的老大DHH ,也不落人後的提出相關的建議。DHH 似乎覺得 Twitter 有點太過於懶惰了點,Twitter 比很多人幸運,有機會碰到那麼大的流量,那就該好好的想辦法處理相關的問題。而不是等著別人幫你解決你應該解決的問題。Open source 的成功,是來自使用者遇到相關的問題,並且解決他,回報給社群,這樣社群才會繼續壯大起來。
Second, when you work with open source and you discover new requirements not met by the software, it's your shining opportunity to give something back. Rather than just sit around idle waiting for some vendor to fix your problems, you get the unique chance of being a steward of your own destiny. To become a participant in the community rather than a mere spectator. This is especially true with frameworks like Rails that are implemented in high-level languages like Ruby. The barriers to contribution are exceptionally low
至於 Rails 一次連結到多個 DB 的問題,老實說,這根本不是問題,Dr. Nick 早就提出了Magic Multi Connections,可以有效解決這個問題。至於 Twitter 團隊是不知道這個東西,還是試過這個 Plugin 之後發現不夠用呢?InfoQ 訪問了 Twitter 另外一個開發者 Blaine Cook,說到 Dr. Nic 的 Plugin 很棒,很有幫助, Blaine Cook 表示目前 Twitter 的 DB Connection 是 600 req/sec,雖然很高,但是現在 Twitter 沒有暫時 DB 的問題。(那之前 alex 不是質疑說他們的問題是在 DB ? 到底 Twitter Bottleneck 在那裡?有點不了解他們的 Point 。
"Dr. Nic's approach is a great first step, and adds some welcome helpers to selecting from a number of database connections." but noted that "Twitter isn't currently database bound, and won't be for a while yet"
這時 Dr. Nic 也出來打圓場了,他說「Twitter 已經貢獻了 Jabber API 了」,其實不用苛求太多啦。
The guys at Twitter have already contributed code to Ruby community (Jabber API)
話說回來,Twitter Team 也似乎感受到 Rails 社群對他們處於制高點的期待,也開始對社群做出了幫助。Blaine Cook 在 SDForum 發表了 「Scaling Twitter」,裡面提到許多他們的問題以及解決方式。

進入討論

OK ,我們已經把故事講完了,現在討論正經點的事情。
到底是 Twitter 技術上不夠厲害,或是 Rails 不夠 Scalibility?
我認為這個 case 無法認定那一個結論是正確的。我們看看 twitter 的流量成長
很明顯的,Twitter 在 2007 年的流量是一個暴發戶的成長方式,通常網站遇到了這樣突然爆增的流量,原本編制的 RD 跟網管應該都完全無法應付吧,那麼一時之間無法解決也是非常正常的事情。所以,他們應該也不是太懶惰,只是成功的太突然,沒辦法吃下來。

再來,根據Blaine Cook 在 SDForum 發表的「Scaling Twitter」投影片,他們一共有 180 Mongrel Instances,但是卻只有使用兩個 DB Server,這似乎完全不符合一般大網站所遇到的情況(就是一海票的 DB Server,每個 table 都是橫切縱切隨便切)。當然我們不排除 twitter 的 application 型態其實並不需要太多 DB 的 request ,不過180 Mongrel Instant 居然只有 兩台 DB Server ,未免也太少了點,也不符合比例原則。

Twitter 是 host 在 joyent 上面的,像這種有一定規模的網站公司居然沒有自己專屬的系統管理者,光是這點這就看出 Twitter 成長真的太快了。一般來說,Web Host 很難做到專門為某個 Service 最佳化吧。Scalibility 本來就是軟體開發者跟系統管理者並肩合作的工程,Twitter 該多請幾個系統設計師摟。

至於 Ruby 是不是真的太慢,Rails 是不是真的不夠 Scalibilty呢?

這個問題我也不知道,畢竟我沒 run 過那麼大的 site,而我看到的 site 都是效率很不錯的。LAMP 發展了很久,Java 跟 Python 發展了很久,他們擁有 Ruby and Rails 社群無法比擬的成熟度,這是不爭的事實。但是,為什麼 Rails 會讓那麼多人趨之若騖,而非那些其他的語言呢?
那是因為 Ruby on Rails 擁有一些別人沒有的東西。而那些東西是很難被取代的。

今天這件事情沒有誰對誰錯,Twitter 點出這個議題,並且強調這個議題的重要性,Rails 社群接收到 Twitter 的訊息,大家一起幫忙解決,這是一個良性的溝通。我相信 Scalibility 是可以被克服的議題,也是值得一起去加油的議題。


延伸閱讀

5/05/2007

script/plugin

well, 由於我跟 Rails 不熟,所以很多地方只能憑空臆測,如果有誤也望請指點。很多跟 Rails 有直接關係的細節我也難以深究,所以大概只能從 Ruby 的角度看下去。總而言之呢,Rails 的 plugin 比起 rubygems 還更要簡單得多,根本沒有任何需要設定的部份,只要把目錄開好就可以輕易使用了。目錄結構大約是:

init.rb
install.rb
lib/*.rb
test/*.rb

init.rb 是 Rails 在 load up 時會執行的部份,所以 plugin 要把 init up 的程式碼放到這邊,例如最常見的恐怕是:
ActiveRecord::Base.send(:include, OOO)
這邊使用 send 而不是直接用 . 的緣故是 include 本身是 private method, 用 . 的會有 NoMethodError. 不過我建議可能可以考慮使用 __send__ 而非 send, 因為有些 class 的 send 有被 override, 一不小心可能會有未預期的錯誤。(雖然應該一看就知道會是出什麼問題就是了)

install.rb 則是在安裝時才會執行的程式,例如使用 script/plugin install OOO 就會先執行此 install.rb, 而日後不管何時使用此 plugin 時,install.rb 都不會再被執行。這部份可能會放的程式碼也許是會動到目錄結構的程式,例如在 app/models 下寫入 OOO_plugin.rb, 像這種只需要在安裝時執行一次即可。這樣做有什麼好處呢?我想感覺就有點像 script/generate OOO_plugin 的感覺吧。

至於 lib/ 則會自動加入 load path, 這部份跟 rubygems 裡 gemspec 裡的 s.require_path = 'lib' 是相同的概念。test/ 的部份也和 rubygems 相同。

接下來還有什麼好講的嗎?有的,參考〈acts_as_taggable Plugin 使用方式〉一文,可以發現 acts_as_taggable.rb 寫得相當複雜,這當然是有原因的。我參考了官方網站裡跟 plugin 有關的系列文章,發現 ActsAsOOO 是個相當常見的 plugin 模式,其中似乎有某種 pattern 在其中…。就是 ClassMethods, SingletonMethods, 與 InstanceMethods.

這樣做的原因很簡單,就只是加強模組化與避免名稱污染,所以多繞了幾個圈。在這種模式的做法下,ActiveRecord::Base 所多出來的 method 只有 acts_as_taggable. 而當你在寫 class MyModel < ActiveRecord::Base; acts_as_taggable; end 時,只有 MyModel 多出 class method => find_tagged_with, instance methods => tag_with, tag_list. 而不是整個 ActiveRecord::Base 都多出這些 methods.

其實這做法也有點像 meta-programming, 只不過是用 mixin 的形式。在前一陣子裡,由於我搞不太清楚 include 與 extend 的差異,所以調查了一下,並做了些筆記:(我知道多半連不上,所以 copy 一份到這)

=begin
2007.02.13

include 將 module 內的所有 method 原封不動拿過來,extend 則是在原本的 method 前面多加個 self. 即 class C 內,寫 include M 則追加 instance method. 如果寫 extend M 則追加 class method.

module 內也可以使用 include 與 extend, 結果同 class. include 為 instance level, extend 為 class level. 基本上這兩樣東西是很接近的。至於 module level 的 method, 則不受 include 也不受 extend 影響。其實跟 meta-programming 很像。

module M
def m; end # 只能由 include 或 extend 取用
def self.m; end # 只能用 M.m 來呼叫
end

class C
include C # 獲得 instance method m
extend C # 獲得 class method m
end
=end

除此之外,module 還有個特別的東西叫 module_function, 這東西我還沒仔細研究,但初步認識的結果是,他會造成指定的 method 同時定義 def m; end 與 def self.m; end 詳細的內容可以當成讀者練習,呵。(雖然其實可能就只是這樣而已)

回到 acts_as_taggable, 在 module Taggable 中唯一的 method 是:
def self.included(base)
base.extend(ClassMethods)
end
這個相信大家都很熟了,就是指當此 module 被 include 時會執行的 method, 有點 hook 的概念。這裡的 base 就是 includer, who include this module. 在 Taggable 中,當然就是 ActiveRecord::Base 了。extend 上面看到了,會促使該 class 擁有此 module 中所有的 method 成為 class method, 所以 ActiveRecord::Base 多了個 acts_as_taggable 為 class method. 如此一來,就能寫:
class MyModel < ActiveRecord::Base
acts_as_taggable
end

其實我覺得這樣實作有點過於複雜,我會比較喜歡這種形式:
class ActiveRecord::Base
def self.acts_as_taggable
# ...
end
end
這樣就一目了然了。缺點當然就是會比較難將此 method 丟給其他人用,例如假設哪天有了一個 class 是 PassiveRecord, 也想將 act_as_taggable 丟給他使用,可能就必須 copy & paste, 或是一些非常詭譎的方式,這邊不多提。

而在 acts_as_taggable 的最下方,呼叫了:
include ActiveRecord::Acts::Taggable::InstanceMethods
extend ActiveRecord::Acts::Taggable::SingletonMethods
的意思就非常明顯了,將 module InstanceMethods 的 method 變成 MyModel 的 instance method, 將 module SingletonMethods 的 method 變成 MyModel 的 class method.(不太明白為何要取做 SinglethonMethods, 也許只是因為 ClassMethods 用過了吧?)

原因是 :include 與 :extend 的 message receiver 是 MyModel 而不是 ActiveRecord::Base, 所以被擴充的是 MyModel; ActiveRecord::Base 不受影響。

大抵上就是這樣了。

abstract:
init.rb 在 Rails load up 後會執行
install.rb 在 plugin install 後會執行
lib/ 自動加入 load path

下次再研究 rake 要怎麼用,相信以之拿來做 test 會很方便。

2007.05.05 godfat 真常

延伸閱讀

4/18/2007

搞清楚你的 Rails Version

目前使用 Rails 的網站,Version 分成好幾派。
  • 0.X
  • 1.0
  • 1.1
  • 1.2


Rails 0.X 的就是幾年前就開始在使用 Rails 的始祖,據我所知國內也是有網站還再用 0.X ,他們之所以死撐不換的原因只有一個,就是怕升級上去原本的 code 根本不相容。1.0 的時候我沒趕上,那可能要問一些長輩才知道 1.0 的模樣是怎麼樣子。

我進入 Rails 圈子剛好是 1.1 的剛剛出的時代,那時候 RJS 剛剛出來,大家叫好又叫座。我也因此全新投入了 Rails,1.1 的穩定度很高,速度也不賴。 Rails .12 是今年才出的,REST 加上 has_many :through 實在非常的吸引人,不過有 benchmark 表示 Rails 1.2 在速度上輸給 Rails 1.1,不過也沒輸太多。所以綜觀起來,要使用 Rails 1.1 或是 1.2 是要看你的需求而定的,沒有一定要使用那個版本的建議。


我現在手邊的 Project 都是用 1.1 ,而且絕大多數已經上線在跑的,短期間不太可能轉換到 1.2 。但是我一定會花很多時間在了解 1.2 的情況。也就是說,我希望能夠在我的機器上面裝 1.1 跟 1.2,有沒有辦法達成呢?

當然有,這很基本!!!
首先,你的 gem 已經安裝了你所要求的 Rails Version,像是我希望在我的機器上面可以自由使用 1.1 或是 1.2 的 Rails,所以我的 gem 安裝情況就是
rails (1.2.3, 1.1.6)
Web-application framework with template engine, control-flow layer,
and ORM.

問題來了,要怎麼一次安裝兩個以上的 version 呢?假設你的 Rails 已經安裝了 1.1.6 ,可是又要安裝 1.2.3 ,就這樣打吧。
sudo gem install -v=1.2.3 rails
當然,如果你想安裝的是最新的 Rails Release,那麼這樣也是可以的
sudo gem i rails
如此就可以在同一台機器上面安裝不同 version 的 rails 。

確定了你的 gem 已經安裝了多個 version,我們必須了較 freeze code 的概念,很多時候我們的某個 Project 的 code 是在某個版本的 rails 開發的。當rails 出了新的 release,原本run 好好的 code 就可能出現一堆 error,所以我們必須要在這個 Project 也包入 Rails 這個 version 的 code。使用方式如下
rake rails:freeze:edge TAG=rel_1-1-6
rake rails:freeze:edge TAG=rel_1-2-3
顧名思義,你可以再 TAG 下面指定你要的 Rails Version,這個指令會把 Rails 放入 vender/rails/ 底下,以後伺服器執行前,他都會去這個資料夾尋找,如果有 vender/rails/ 的資料夾,他就不會使用系統預設的 Rails version ,而是使用已經包在這個 Rails 資料夾的 version。

當然,如果我們將已經包好的 Rails Version 解除,重新使用系統預設的 Rails Version,就這樣打即可
rake rails:unfreeze
他做的事情其實就只是 rm -fr vender/rails/ 資料夾而已。

最後一點,如果你不確定你的 Rails Package 到底使用那個 Version 的 Rails ,你可以打入
ruby script/about

他會跟你講的一清二楚的。

4/05/2007

Persistent Login on Rails

Rails 社群一向是使用最方便的 Session 來做 login,而且 Rails 的 Session 也實做的非常方便。不過有些時候 Session 也有不方便的地方,像是關掉 Borwser 就要重新 Login,如果我們希望能夠做到永久 Login ,那該怎麼搞呢?

我採用這個網頁的方式,他的好處是只在 cookie 存入一個隨機的 id ,並不會存任何資訊在 cookie 裡面。



1. 安裝 UUID 的套件
因為要隨機產生 id ,總是要一個亂數產生的套件,這裡我們就用原作者的選擇 UUID
gem i uuidtools

2. DB 裡面新增一個 Persistent Login 的 table
我們新增一個 table ,把他叫做persistent_logins,結構大概是 id , uid , user_id 當作 interger ,create_on 當作 datetime。user_id 代表相對應的 user table id ,uid 就是放到 cookie 裡面的隨機 id 。

3. 建立一個 PersistentLogin 的 Model
裡面的結構很簡單,之前說的 UUID 就是用在這裡,其實可以在這裡換另外一個亂數產生器。
class PersistentLogin < ActiveRecord::Base

belongs_to :user
before_create :assign_uid

private

def assign_uid
self.uid = UUID.random_create.to_s
end
end
3. 在使用者已經 login 成功的的地方放入下面的 code
 p = PersistentLogin.create(:user=>u)
cookies[:p_session_id] = {:value => p.uid, :expires => Time.now + 7.days}
在裡面,u 就是這個使用者的 user model ,他會在 cookie 裡面放 p_session_id 這個值,expire time 是 7天。

4. 在判斷是否 login 的地方加入這些 code

def get_current_user
if session[:user].nil?
if cookies[:p_session_id] && p = PersistentLogin.find_by_uid(cookies[:p_session_id])
session[:user] = c_user.id
end
end
end

5. logout 的地方加入這樣的code

def logout
session[:user] = nil

if cookies[:p_session_id] && p = PersistentLogin.find_by_uid(cookies[:p_session_id])
p.destroy
end

cookies.delete :p_session_id
end
這裡的code 相當的 rough ,但是卻很容易實做出來。

4/02/2007

Rails 1.1 跟 1.2 的 Performance 比較

之前有一個報導顯示, Rails 1.2 遠遠的比 Rails 1.1 來的慢,慢到快兩倍左右。
Results of RoR 1.2.1 shocked “a little”: falling of productivity in 2-4 times in comparison with 1.1.6 version
Stefan Kaes 做了一個 Performance Benchmark,打破了這個有點像謠言的 Benchmark。這裡的結果還是 1.2 還是比 1.1 來的慢,不過相差不遠。

裡面發現到幾點
  1. Rails 1.2 的 action cache 跟 1.1 比起來,還是相當程度的需要 performance optimize
  2. pstore 的 session container 效率實在不佳
  3. 當 Rails code base 越來越大的時候,效率似乎還是難免會下降(原因在於 GC 跟 Intpreter 的問題),一個良好的 vm 還是解決的最佳解

3/28/2007

Fragment Cache Part 2

上次介紹了如何使用最基本的 Fragment Cache,現在來加上一些小小的小 trick。上次介紹在 View 裡面要這樣使用Fragment Cache


<% cache do %>
我們要 cache 的 content
<% end %>
Cache 會出現在 RailsRoot/tmp/cache/你的host/你的controller/你的action.cache 這個檔案裡面。假設上面的 action 是在 lala controller 的 haha action ,那們上面的寫法跟 下面的寫法效果是一樣的
<% cache(:controller => 'lala' , :action => 'haha' ) do %>
我們要 cache 的 content
<% end %>
簡單講,就是最上面的 cache do 寫法算是預設值,寫不寫好像沒差,當這個頁面只有這裡需要 cache 時,這樣寫就很方便。不過如果這個 haha action 會依照後面接的 id 的不同顯示不同的內容時,你可以很直覺的加上 :id 這個選項
<% cache(:controller => 'lala' , :action => 'haha' , :id => params[:id] ) do %>
我們要 cache 的 content
<% end %>
Cache 會出現在 RailsRoot/tmp/cache/你的host/你的controller/你的action/這個頁面的id.cache 這個檔案裡面。簡單講,又多了一層以 action 為名的目錄,目錄裡面每個 id 都有自己的 cache file。這樣的作法可以簡單依照變數的不同區分 cache file 。

不過?

但是老實說,到目前為止,這個 fragment cache 用途還是不大。我們一個 Action 只能使用一個 Partial Cache,那如果同一個頁面有兩個以上的地方要 cache 呢?Partial Cache 的概念就是一個網站有很多個 block 可以被許多頁面共用。假設某個頁面有兩個部份要 cache ,一個是 RSS ,另外一個是 Info 這個地方。那我們依照 Rails 聖經本的方法,使用 part 變數供我們使用
<% cache(:controller => 'lala' , :action => 'haha' , :id => params[:id] , :part => 'rss' ) do %>
我們要 cache 的 rss content
<% end %>
<% cache(:controller => 'lala' , :action => 'haha' , :id => params[:id] , :part => 'info' ) do %>
我們要 cache 的 info content
<% end %>
如此,相關的 Cache 會產生在 RailsRoot/tmp/cache/你的host/你的controller/你的action/這個頁面的id.part=rss.cache 還有 RailsRoot/tmp/cache/你的host/你的controller/你的action/這個頁面的id.part=info.cache 這兩個 file 裡面。仔細看就知道,他的命名機制就是 id. part=part_name.cache 這樣的寫法。很簡單吧。

到現在 Partial Cache 才從還好變成好用的階段。read_fragment 或是 exprie_fragment 都是使用
:controller => 'lala' , :action => 'haha' , :id => params[:id] , :part => 'info' 
這樣的命名機制來操作,不難吧。

還有嗎?

到現在為止,我還沒看過 :part 還不夠的情況,不過如果真的覺得用 controller , action , id , part 都不夠區分你的 cache ,還有最後一招
<% cache(:controller => 'lala' , :action => 'haha' , :id => params[:id] , :part => 'rss' , :part2 => 'rss'
, :part3 => 'rss' .... ) do %>
我們要 cache 的 rss content
<% end %>
簡單講,其實 :part 只是聖經本這樣寫,所以在 Rails Fragment Cache 機制裡,id 以後的 fragment 命名方式不限制,而且數量不限制,只要你不嫌打字太累,你大可以用幾百層去命名你的 cache 。出來的 cache file 命名規則是這樣 ailsRoot/tmp/cache/你的host/你的controller/你的action/這個頁面的id.part=rss&part2=rss&part3=rss.cache。總之隨便你怎麼命名 :p

3/27/2007

Fragment Cache Part 1

之前介紹過 Page Cache,那是一個將所有動態內容都變成靜態頁面,藉由不經過 CGI ,來達到最大覆載度的技巧。但是他的應用性不夠廣泛,僅限於下面的用途


  1. 流量超級大的頁面
    通常是 index page,一個網站可能有幾百個頁面,但是通常 index page 一個頁面就佔了三到五成的流量,而且 index page 通常每個網站都是一個小時,了不起 15 分鐘更新一次,這個時候用 index page 可以大幅度增加網站覆載度。
  2. 頁面修改的頻率遠遠小於讀取的頻率
    例如 Blog 系統,我可能一天寫一篇文章,但是一天讀取我的 Blog 的人可能有幾千個,這個時候為什麼每次讀取頁面的時候都得去 DB 撈資料,組合,Render 出來呢?還不如用靜態頁面儲存,然後每次修改頁面時都重新產生新的 HTML 即可
但是,如果要利用 Page Cache 做到某些部份的功能實在有點麻煩。像是在同一個頁面裡面,有些地方是很少機會修改,希望能夠 Cache 起來,但是有些地方修改頻率超頻繁,不能夠用 Page Cache 來做。這個時候,我們就可以使用 Partial Cache 的方式來加快速度,在Rails 裡面 Partial Cache 叫做 Fragment Cache。

使用方式如下

在 View 裡面

選定要 Cache 的部份,將他用 cache block 包起來
<% cache do %>
我們要 cache 的 content
<% end %>
如此,當我們用 production mode 的時候,你就會發現會出現 tmp/cache/你的controller/你的action.cache 這個檔案,也就是 cache 的內容。並且 reload 的時候,那個 block 的 content 都是不變的。

理論上 cache 是 work 的,但是如果你去翻 log 時,你會發現 db 一樣會去做相關的 operation。原因是因為我們並沒有告訴 Rails ,Controller 裡面某段 code 是已經 cache 好的東西,不需要去執行。我們都知道 db 通常是最大的 bottleneck ,如果 cache 沒辦法避免 db operation,那這樣的 cache 也僅僅省去了 render 的時間,並沒有太多幫助。

在 Controller 裡面

為了避免這樣,我們要在 controller 裡面指明,當 Fragment Cache work 時,某些 operation 是可以不用執行的。
unless read_fragment :action => '這個action的名字'
跟 cache 有關的 operation
只要有 cache ,就不需要去執行的 code.....

end
如此如果已經 cache 過了, Rails 就會避開這段 code 的執行。

要如何 Expire Cache

Expire Cache 也是相當的方便,在 controller 裡面使用 expire_fragment 即可。
expire_fragment :action => '這個action的名字'
以上都是最最最基本的 Fragment Cache 的機制,但是如果只知道這些東西,根本做不了太多事情,我們下次來談談比較實用的 Fragment Cache 機制。

Ajax auto-update

Ajax 跟 Ruby on Rails 的整合已經眾所皆知,今天心血來潮,想來實做 digg-spy 形式的資料更新。但是我的需求是每隔一段固定的時間更新,而不是一直 append 新的訊息。在參考了幾個網站後,驚訝的發現,這種 auto-update 的 Ajax 怎麼可以這麼簡單(請用 thegiive 的口氣自行想像)。

*** 斜體字為你尚未命名或已經命名好的名稱 ***

[summary] 我們得準備:
  • 一個 controller 裡面的新的 action,假設該 action 名稱為 action-name,該 controller 名稱為 controller-name
  • 一份 RJS template,置放在 /app/views/controller-name/action-name.rjs。
  • 一份 partial template,假設置放在 /app/views/controller-name/_show_topics.rhtml。
  • 在適當的地方加入 javascript。


step 0:
首先在預設的 layout 中掛入 prototype 等 JavaScript。寫法如下:

<%= javascript_include_tag :defaults %>

其中 :defaults 會將 /public/javascripts/ 中預設的 JavaScript 都掛載上來。如果不想全部掛上來的話,可以改寫成 :script-name

step 1:
接著在你的 controller 裡面寫入一個新的 action。假設我們想要每分鐘更新文章列表:

def update_topics
# 取出最新的 forum 文章,get_all_topics 請自己在 model / controller 撰寫
@all_topics = get_all_topics(10) # hard code 是不好的,小朋友不要學。也可以順便做錯誤處理。
end

step 2:
接著是你必須建立一個檔案 /app/views/controller-name/action-name.rjs,在這個 RJS template 裡面使用 prototype helper 裡面的 methods 來與瀏覽器溝通。內容如下:

page.replace_html 'update-topics-div', :partial => 'show_topics', :object => @all_topics
page.visual_effect :highlight, "update-topics-div"

我們用了 replace.html 來幫我們做取代的動作,如果是 digg-spy-style 的話,通常我們會用 insert.html,有空再來寫。值得注意的是,在這個檔案裡面無須使用 <%%> 來內嵌,直接撰寫即可。update-topics-div 這邊代表的是欲取代的 html element id,show_topics 則是欲取代的 partial template(注意 template 的命名有 prefix _,render 時無須 prefix _,也無須副檔名),再利用 :object 將變數 binding 到 template。visual_effect 效果我就不介紹了。

step 3:
當然你要有輸出的 view,我們假設是 /app/views/controller-name/another-action-name.rhtml。部份的片段應該有:

...
<%= render :partial => 'show_topics' %>
...

而我們的 template _show_topics.rhtml 假設長成這個樣子:

<div id='update-topics-div'>
<ul>
<% @all_topics.each{ |topic| %>
<li><%= topic.title %></li>
<% } %>
</ul>
</div>

step 4:
最後我們挑選一個適當的地方加入 JavaScript,什麼叫適當的地方呢?因為有些 template 會共用的關係,如果把 JavaScript 加入到共用的 template,會出現 RJS 找不到 html element id 的錯誤,所以說得擺在適當的地方,當然跟瀏覽器的支援程度也有關係。假設找到了適當的地方,則我們加入 JavaScript 如下所示:

var updateInterval = 60 * 1000; // 每分鐘 update 一次
var timer;

function goUpdateTopics() {
timer = setInterval('updateTopics()', updateTopicsInterval);
}

function updateTopics() {
// url 記得寫對!
url = "/controller-name/action-name";
new Ajax.Request(url, {
asynchronous: true,
method: "get",
});
}
goUpdateTopics();


Well, 經過這四步驟,你應該已經可以快樂地使用 Ajax 整合在 Rails 裡所提供的功能與樂趣。:)

延伸閱讀

3/11/2007

Rails 當中使用 Google Map:YM4R


最近發現 Google Map 很好玩,想要玩玩 Ruby on Rails 上面的 Google Map,就找上了 YM4R 這個 Rails Plugin,發現 YM4R 使用 Google Map真是方便呀,彈指之間搞定。YM4R 這個 Plugin 就是讓Ruby on Rails 上面使用 Yahoo Map 跟 Google Map 的 Rails Plugin。以下介紹一下這個 YM4R 怎麼使用。


安裝:

ruby script/plugin install svn://rubyforge.org/var/svn/ym4r/Plugins/GM/trunk/ym4r_gm


這個動作會把相關的 file copy 到 config,vendor/plugins, public/javascripts 裡面去,如此就把這個 YM4Rplugin 裝完了。

使用:
要使用這個 plugin 一共分成 controller 跟 view 兩個部份。

Controller:

在 controller 裡面加入底下的 code ,底下的 code 是改自這個網頁,他會 initial 一個 Gmap Object,顯示我們指定的位址。我們這裡舉台北為例子,台北的經緯度根據 Wikipedia ,為東經121.6度,北緯24.9度,放大的程度為 7,並且有一個 info_windows。
latitude = '24.9'
longitude = '121.6'
@map = GMap.new("map_div_id")
@map.control_init(:large_map => true, :map_type => true)
@map.center_zoom_init([latitude,longitude], 7)

marker = GMarker.new([latitude, longitude],
:title => "台北", :info_window => "我在台北呦~")
@map.overlay_init(marker)

View :

Google Map 使用方式都是 Javascript。要在 Ruby on Rails View 裡面使用方式,分成兩個步驟,第一個是在 head 裡面加入相關的 Javascript
<%= GMap.header %>

第二個就是在 Body 裡面,顯示剛剛在 controller 裡面設定完成的 @map 變數,這裡寬跟高都設定 600
<%= @map.to_html %>
<%= @map.div(:width => 600, :height => 600) %>


啟動:

請打入 http://localhost:3000/controller_name/ 就可以看到台北的 Google Map 了

等等,我都沒看到 Google API Key ?

是的,我們上面的例子都不用設定 Google API Key 的原因,是因為我們使用的 Domain 是 http://localhost:3000/ ,而這個 Plugin 已經預設裝入 http://localhost:3000/ 的 Google API Key 。

如果你想設定你的 Domain ,請到 config/gmaps_api_key.yml 裡面設定,請將你申請的 Google API Key ,放入 production 設定底下。話說,我不知道為啥我明明跑的是 development mode,但是將申請好的 API Key 放在 development 底下他不 work,反正似乎只能放在 production mode 才可以 work。

延伸閱讀:
  1. Rails Google Maps Plugin 教學
  2. YM4R Project Hompage

2/28/2007

一個在 Rails 跟 Django 徘徊設計師的真情告白



AjaxWhoIs 這個網站的作者最近發表一篇文章 Why I moved from Ruby on Rails to Python/Django and back,講解他開發 AjaxWhoIs 2.0 的時候為何先用 Rails 開發,後來採用 Django 開發,最後還是轉回 Rails 的紀錄。

Any newcomer to Rails will quickly discover why it is so talked about. First of all, even though I don’t consider myself anywhere near a decent Rails programmer, I am now at least twice as productive with Ruby on Rails than with ASP.NET and C#. Thanks to the MVC (Model, View, Controller), DRY (Don’t Repeat Yourself) and Convention Over Configuration mindset of Rails.

他一開始是一個 ASP.NEt 跟 C# 的程式設計師,所以他是第一次使用 Rails ,但是當他使用 Rails 開發的時候,他發現 Rails 的三大法則 MVC ,DRY ,Convention Over Configuration 至少讓他生產力比原本很熟悉的 ASP.NET 快了兩倍以上。

I found out that Mongrel was known to not play well with 64 bit Ubuntu (I lost the links to this evidence, unfortunately). Mongrel was patched accordingly, but it didn’t solve my problem. It still crashed many times a day and I just couldn’t figure out what was wrong. I asked my good friends Rich Collins and Adam Thorsen (of Guruza.com) who are both experienced Rails developers and they had no idea either. I was stuck.

但是當他寫完AJAXWhoIs 2.0的時候,他發現到 Mongrel 跑 64bit Ubuntu 的時候有詭異的問題,導致 Mongrel 常常當機,屢試不爽。

I picked up a Python book and rewrote the whole site one more time, in Python using the Django framework this time. I had never programmed in Python before and much less used Django. However, the switch was easy enough since Ruby and Python are somewhat similar.

相當令人覺得很了不起,也相當令人覺得不可思議的事情發生了。他已經寫完了整個 Project ,只是遇到了這個 Hardware 問題,他居然跑去用 Django 重新寫了一次 AjaxWhoIs 2.0,而且這還是他第一次寫 Python。一般人應該都會換台機器跑就好了不是嗎 @@!

However, I soon missed Ruby and Rails. Ruby supports “real” private/public/protected methods (Python just fakes that with its naming convention) and it’s syntax is more forgiving (the need for () at the end of each method call in Python was killing me). Django is not as straightforward as Rails, and requires more code to get things done. There are also many little things that don’t “feel right” in Django, like the need to manually pass variables to a view. Data access is confusing at best while Rails’ ActiveRecord is god-sent. These little things really add up.

但是當他寫完的同時,他開始相當懷念 Rails 了,有許許多多小地方他認為 Rails 做的比 Django 來的好,Rails 作法也比 Django 來的更 straightforward 。要完成同樣一件事情,Django 也需要比 Rails 更多 code 。 Django 有太多東西讓他覺得 don't feel right了。

On the flip side, Python is faster than Ruby and less memory hungry. I was also surprised to actually love Python’s significant indentation (see example). But more importantly, Python and Django just worked! I never experienced weird crashes like I did with my Rails stack.

的確,Python 比 Ruby 快,需要 memory 也比較少。而且最重要的是,Django 可以動,Mongrel 在 64 bit CPU 不能動。

So, why am I back to Rails for my next project? 3 letters: FUN. I find Ruby and Rails to be pleasant to use. The community large, active and very helpful. The number of freely available Rails plugins and the fast evolution of the core code are also welcomed additions. Rails requires less code, less self.__awkward_method_calls(), has built-in AJAX and REST support, and has much more flexible data access and templating engines.

而為何他又要跑回 Rails 了呢?Just for FUN。他發現到 Ruby on Rails 會讓人非常愉快,社群很大,活動力強,而且都會互相幫忙。免費的 Rails Plugin 跟 code 快速的進展都很棒。Rails 需要較少的 code ,較少的可怕的 method call,而且還內建 AJAX 跟 REST。而且 Data Access 跟 template engine 都更有彈性。

But what about those crashes? What about the speed issue? Well, I don’t have the crashes anymore. Don’t ask me why, I don’t know, but it’s fine now, I swear! Something somewhere got fixed and it seems to have solved the problem. However, speed is still one of the low points of Rails. Using caching and proper code optimization should take you a long way, though. Matz, the author of Ruby, is working hard on a new virtual machine that should make Ruby just as fast, if not faster than Python.

最神奇的是,當他回去 Rails 時,Mongrel 不再 crash 了@@!不知道Mongrel 哪裡修正了,反正問題解決了。但是效率依舊是一個問題所在。

My recommendation is, try both for long enough to figure out what works and what doesn’t for you. If you are a long-time Python user, Django might be more compelling for you, but if you are coming from a Java, Perl or Smalltalk background, Ruby and Rails will most likely be what you’ll end up using. Either way, I don’t think you can go wrong.

他的建議是如果你是 Python 長期的使用者,用 Django 吧,如果你是 JAVA,Perl,Smalltalk背景的人,來用 Ruby on Rails 吧。

我的小結論:

從他的字裡行間可以看出幾件事情

  1. Rails 跟 Django 開發時間都很快速
  2. 他真的很不會利用時間,居然用 Rails 跟 Python 各開發了一次,只是因為 Mongrel 對 Hareware 有點 bug