9/29/2006

Subversion + Ruby on Rails

研發程式時通常都會做 Version Control
現在當然很多 team 都使用 subversion 做 version control
但是今天才發現到
原來 Ruby on Rails 有 Subversion 支援,真令人意外
Ruby on Rails 裡面通常最難做 version control 的地方是
generator 產生的程式
因為產生的程式會放在不同資料夾下面
但是但是
Ruby on Rails 什麼都有什麼都賣,什麼都不奇怪
ruby script/generate xxx abc --svn
或是
ruby script/generate xxx abc -c
我們發現到他的輸出
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/abc.rb
A app/models/abc.rb
create test/unit/abc_test.rb
A test/unit/abc_test.rb
create test/fixtures/abcs.yml
A test/fixtures/abcs.yml
exists db/migrate
create db/migrate/008_create_abcs.rb
A db/migrate/008_create_abcs.rb
我們發現到他會幫你 svn add 所有產生的程式
雖然不是跨時代的發明
但是是一個貼心的好設計

ps . 底下是我之前寫的幫所有新增程式做 svn add 的 script
有這個小技巧,這隻程式就功成身退了

rails_locates.each do |i|
`#{find_command} #{i} -type d`.split("\n").each do |i|
`#{svn_command} add #{i}/*` unless i =~ /\/\.svn/
end
end

9/28/2006

Many Link about DSL in Ruby


RailsConf Europe 2006有趣的對話

“Nobody is passionate about something that they suck at.” – Kathy Sierra
我最喜歡這句

“My computer might be at risk. Well, oh well. Life on the edge.” – David Black

I would eat a monkey. - Tom Ward
I should qualify that: I wouldn’t go out of my way to eat a monkey. - The next morning Tom Ward 越描越黑?

I owe Matz a lot. I would do a lot for that man. - DHH (有 BL 的味道)

參考連結
  1. Snowblink Blog
  2. Robby on Rails

漫畫版的 Ruby 教學


Lighttpd 防止圖片盜連

說實在話,我這個 Blog 趨向 Ruby and Rails 的程度遠比Lighttpd 或是 SQLite 來得深 @@!
不過我的 MAC OS X 上面依舊還是靠這個組合跑得很開心
所以我要努力充實 SQLite 跟 Lighttpd 的觀念,VIVA~~~

今天看到 我 BlogLines 裡面被歸類為 IT 強者分類的 DK大大大丟出了一篇文章
發現剛好解決我最近一個小問題
趕緊記下來以免忘記

Lighttpd 防止圖片盜連
Just put $HTTPurl inside $HTTPreferer ,and $HTTPurl must be allways the last, at least in 1.4.x

因為 Lighttpd 設計上面的哲學,所以他不能將 http["url"] 放在 http["referer"] 的外面

在 Lighttpd Trac 上面的解答

$HTTP["referer"] !~ "^($|http://www\.fussball-forum\.de)" {
$HTTP["url"] =~ "^/images/" {
url.access-deny = ( ".jpg", ".jpeg", ".png", ".gif" )
}
}

9/27/2006

SQLite import CSV File

今天有一個 Excel File ,先轉成 CSV 檔案格式
發現到這個 CSV File 是用 ; 間隔
但是發現到
sqlite> .show
echo: off
explain: off
headers: off
mode: list
nullvalue: ""
output: stdout
separator: "|"
width:
他的預設 separator 是 |
所以我們要先修改 separator
進入sqlite3
create table test ( a , b , c , d , e );
.separator ;
.import 檔名.csv test

之後就成功 import

Multibyte for Rails?

看到InfoQ這篇 Multibyte for Rails: A Unicode Solution for Rails?

他提到 multibyte for rails project
ActiveSupport::Multibyte extends the standard Ruby string with a chars proxy method. This proxy allows you to use a multibyte encoded string as a sequence of characters (or Unicode codepoints) instead of bytes
但是雖然做的不錯,但是因為會拖慢 Rails 字串處理的速度
所以並沒有納入 Core ActiveSupport 裡面

另外 Comment 就有人幹橋
支援 MultiByte 應該是 Ruby 的事情吧 (我也是這麼覺得XD)

Migration Console

Migration 一般來說
要寫 migrate 的 file
然後打 rake migrate
有沒有辦法可以直接在 console 裡面直接操作 Migration
也就是用 console mode 直接做 DB admin
我常說 Rails 什麼都有,什麼都賣,什麼都不奇怪
所以一定有辦法 XD

1. 進入 Console Mode
ruby script/console
2. 進入 AR 環境
irb ActiveRecord::Base.connection
這樣就OK啦,我們可以console環境下
新增 table ,刪除 table ,看 table 的 column 之類的
以下簡單的操作法

checks 有多少 table
tables

check 某個 table 的 column
columns('table_name').each { |c| puts c.name }
columns('table_name').each { |c| puts c.name+' -> '+c.type.to_s }

新增 table,這裡用 Migration File 寫比較好
create_table "abc", :force => true do |t|
t.column "lala", :integer, :limit => 30, :default => 0, :null => false
end

drop table
drop_table :table_name

十年之前

Ruby on Rails 另一個讓我感到很驚訝的部份
就是他製作 lib 的想法,完全遵照 The Ruby Way
一切以為人為本

當你的程式越接近人的語言
那你程式就越好維護

不了解我的意思嗎?
給個小範例
if Time.now - session[:last_login_time] > 3600
上面這段代表什麼意思,如果上次 login 的時間距離現在的時間只要超過 3600 秒(也就是一小時)就成立

我們可以改成
if Time.now - session[:last_login_time] > 3600.seconds
上面這段什麼意思,跟上面一模一樣

我們還可以改成
if Time.now - session[:last_login_time] > 1.hours
上面這段什麼意思,跟上面一模一樣

最後最後我們還可以改成

Session[:last_login_time] < 1.hours.ago

所以陳奕迅的歌可以改成 10.years.ago

當然還有 1.hours + 2.days 這樣的搞法@@!

本 lib 是 Rails 裡面的 ActiveSupport::CoreExtensions::Numeric::Time
要在Ruby 裡面使用請先宣告
require 'active_support'
然後就可以使用啦
當然啦,沒有 metaprogramming 觀念的語言是搞不出這種玩意的:p


9/26/2006

Nginx ,另外一個高效率的web srserver

根據 Scrazy 告訴我的情報,Nginx 似乎擁有完整的設定
以及相當快速的 performance
(這裡有兩份 benchmark1 , benchmark2
似乎可以看看( 這裡有一份Nginx介紹,這裡是 Nginx Wiki
不過就當我開始認為 Nginx 不錯的同時
LiteSpeed 開始大反擊,兩邊展開一場 Benchmark 大戰
期待 lighttpd 或是 apache 也能來個絕地大反攻 XD

9/24/2006

Migrate 初探

一開始對 Migrate 沒啥特別的好感的
因為 SQL 本身已經很成熟了,好像不需要重新造輪子
不過,SQL 雖然成熟,但是各家 RDBMS 又很混亂
你有支援這個,我有支援那個
對程式設計師跨 DB 操作感到很麻煩

對我來說,我的 Server 是用 MySQL
平時操作是使用 phpMyAdmin 這個好用的工具
但是 iBook 上面因為效率的關係,始用比較輕巧的 SQLite
雖然也有SQLite Database Browser這個還不錯的東西
但是總覺得不太好用
這時候,使用 Migration 就是一個不錯的選擇了
裡面 DB 操作管理完全使用 Ruby Code ,可以說是另外一個相當簡單方便的選擇

用 Migration 新增 Table
一開始,進入 Rails 資料夾,先將 config/database.yml 寫好
新增一個 Migration File
ruby script/generate migration add_a_new_table
或是
ruby script/generate model add_a_new_table
他會在 db/migrate/ 新增一個 migration 的 file
前面的數字是 version 的編號

然後進入修改這個file裡面的 self.up method
def self.up
create_table :users do |table|
table.column :name, :string
table.column :login, :string, :null => false
# This column will contain an MD5 hash.
table.column :password, :string, :limit => 32, :null => false
table.column :email, :string
end
end
形式大概是 table.column :column_name , :column_type , :option

Column Type :

Column type 可以指定 integer, float, datetime, date, timestamp, time, text, string, binary, boolean

Column Option
後面的選項可以有:
  • limit ( :limit => “50” )
  • default (:default => “預設值” )
  • null (:null => false 代表 NOT NULL)
Ruby code 寫好之後,打入
rake migrate
這樣就可以新增一個 DB table

不同 Version Control


Migration 有 Version Control 的概念, Version 的編號就是檔案前面的數字。 self.up 就是前進 version 要做的動作,self.down 就是 back version 所需的動作。兩者應該是相對的,一個是 create_table 另外一個就應該是 drop_table,使用方式是
rake migrate VERSION=version_number
例如
rake migrate VERSION=12
或是
rake migrate VERSION=0 (0就是回到最初的狀態)



不同環境的 Migrate
export RAILS_ENV=production
rake migrate
你可以選擇 migration 不同的環境 ( export RAILS_ENV=test , export RAILS_ENV=development )

Drop Table / Rename_table


rename_table :old_name , :new_name
drop_table :table_name

新增刪除修改 Column
add_column(table_name, column_name, type, options)
rename_column(table_name, column_name, new_column_name)
change_column(table_name, column_name, type, options)
remove_column(table_name, column_name)


裡面的 type 跟 option 就是新增 table 裡面的 column_type 跟 option

Index管理

add_index(table_name, column_name, index_type)
remove_index(table_name, column_name)

index_type 就是像是 unique 之類的 type

參考連結
  1. Migration Manual
  2. Rails Wiki
  3. Rails Migration

9/23/2006

Yahoo Developer Network 也開始加入 Ruby 選項

Yahoo 的 Web Service API 開發網站 Yahoo Developer Network
繼 JavaScript ,Flash,PHP,Python之後
也加入了 Ruby
雖然也不代表什麼特別重要的東西
但是繼 JRuby開發者被 Sun 延攬之後
又是另一個 Ruby 界的好消息
也代表 Ruby 開始走入主流語言~~~

HTTP 認證程式(不使用 ssl )

本來以為上面那一隻 script 可以偵測公司各個 server 的狀態 :p。但是果然事情沒那麼簡單,有一台我架的 test server 裡面的網頁是開發階段的,需要避免閒雜人等進來了解研發機密,所以我簡單的用 lighttpd 的 mod_auth module 去作 access control。所以我必須寫在裡面加入一般 http auth,這一段是可以認證的 HTTP 程式。
require 'net/http'

url_address = 'http://www.example.com'
url = URI.parse(url_address)
req = Net::HTTP::Get.new(url.path)
req.basic_auth 'user', 'password'
res = Net::HTTP.start(url.host, url.port) { |http| http.request(req) }
puts res.body

user / password 就是放在 req.basic_auth 後面即可。

9/22/2006

偵測HTTP 狀態程式

這裡有一段Check HTTP 狀態的程式
基本上是用 crontab 去跑就好
log function 大家就自由發揮吧,看你要寫到那邊都隨便你
值得注意的是 response 不能直接用 == 去判斷Net::HTTPSuccess或是其他的狀態
因為Net::HTTPSuccess是一種很像是 range 的 object
直接用 == 判斷會判斷不出來得
建議是用 case when 來判斷,他會使用 === 來判斷
但是如果你要用 if 來改寫的話 ,必須寫成 Net::HTTPSuccess === response
我覺得這樣比較不直覺 :p


require 'net/http'

url = 'http://www.example.com'

response = Net::HTTP.get_response(URI.parse(url))

begin
case response
when Net::HTTPSuccess
log 'OK'
when Net::HTTPNotFound
log '404 Error'
when Net::HTTPInternalServerError
log 'Internal Server Error'
when Net::HTTPUnauthorized
log 'Unauthorized'
when Net::HTTPClientError
log 'Client Error'
when Net::HTTPRedirection
log 'Redirect'
when Net::HTTPInformation
log 'Informantion'
when Net::HTTPServerError
log 'Server Error'
when Net::HTTPUnknownResponse
log 'Unknown Response'
end
rescue Timeout::Error
log 'System Timeout'
end

以下的 code 可以偵測這個網頁處於那種狀態
我特別將 404 error ,500 error 挑出來
因為他是最容易發現的錯誤,你可以根據這個錯誤去做判斷
基本上只要有 response ,不管是那門子的 HTTP error ,Ruby 是不會 raise error 的
你必須使用 response.error! 或是 response.value 去 raise error
這樣的特性也比較好 coding

但是最後,我們還是得去抓 Timeout::Error
為什麼?我們公司有一台 server ,跑 lighttpd
因為 lighttpd 會去 query application server 的 backend process
當backend process 掛掉的時候,他就不回應給 lighttpd
遇到這種情況 Lighttpd 就不回你錯誤訊息
這樣子,一定要抓 Timeout::Error 才能知道出了啥問題

參考自Introduction to Ruby Net::HTTP,寫的很好
HTTP Response 列表可以參考自 Ruby Manual

讓 Fastcgi 一直活著

FastCgi 好是好
只有一點不好
他常不吭聲就掛給你看@@!
當大家都快受不了的同時
有正義超人出來解救平民百姓了

Patrick Lenz 寫了一支 Killing me softly script(Fourm 也有發表
可以當 mod_fastcgi 掛掉時幫你 輕輕柔柔的 restart
( Killing me softly 是一首英文老歌, Susan Wang 唱這首唱得很棒)
他的目的是 check 跑 fastcgi 的 application server
如果沒有回應的話就 ssh 過去重啟
要先安裝 net-ssh

gem install net-ssh

並且 fastcgi 一定要跑 TCP Socket
如果是 localhost 端跑 UNIX Socket 是不行的

這裡可以 downlaod
我研究一下再發表感想

Ruby 為什麼叫 Ruby

老實說,我很喜歡 Ruby 這個名字
因為發音很好聽,意義也很棒
但是有一點很煩人,就是每次 google 東西時

都會出現一堆 林心如 的網頁

perl 取名就很高明,既有 pearl 的音,卻沒有 google 誤判的問題

好啦,為啥當初 Matz 要取名叫做 Ruby 呢?
這裡有一份 Matz 的現身說法

In fact, I'd like to choose the name from jewel, influenced by
Perl, but I named Ruby after my colleague's birthstone.

因為 Matz 取名時希望取做寶石的名字
而 Ruby 剛好是他同事的誕生石
但是後來他發現到上帝的安排很奇妙

Later, I realized ruby comes right after pearl in several situation,
like birthstones(pearl => June, ruby => July), font size(pearl => 5pt,
ruby => 5.5pt.). I thought Ruby was the good name for the scripting
language newer (and hopefully better) than Perl.

有幾個巧合點
pearl 是六月的誕生石,ruby是七月的誕生石
pearl 字體是 5pt , ruby 字體是 5.5pt
加上Matz本來也是 perl 愛好者
所以他希望 ruby 真的能成為 perl 的繼承者
並且他在設計 ruby 時加入了很多 perl 的語法進去
所以其實有為數不少的 perl programmer 被 ruby 抓走也不是沒有原因的 XD
天意呀!!!(我在我的Blog自 High 應該沒關係吧)

Active Record 連結到舊 DB(手動修改)

最近遇到幾個小 Project
都是要連結到本公司主力產品的DB
當然啦,那時候沒有用 Rails
所以DB Schema 絕對不可能按照 Rails 的預設安排
但是書上有說
There is always way to overridden it.

現在就來看看怎麼 ActiveReocrd overriden 原本的預設
這裡要注意的是,如果你的舊 DB 命名沒有任何規則可言的話
請使用我這個方式
但是,如果你的舊 DB 命名方式有點規則的話
像是 table name 只是沒加 s
或是開頭都有固定的字首
那還有比較好的方式可以設定
不過那些之後再提 :p

一開始,最重要的是 table 命名,以及 primary key 的命名問題
set_table_name 'table_name'
set_primary_key 'table_name_id'
只要指定這兩個就可以將 Model 連結到舊 table 了

處理relationship 的時候
要注意 class_name 以及 foreign_key 的不同
has_one :legacy_table_model ,
:class_name => 'legacy_class_name',
:foreign_key => 'key'

belongs_to :legacy_table_model ,
:class_name => 'legacy_class_name',
:foreign_key => 'key'

通常你希望使用正常的話, has_xxxxxxx 跟 belongs_to 最好乖乖設定好

這樣正常使用上就沒問題了

9/21/2006

Ruby 實用小技巧

看到railscn這一篇
裡面有相當多 Ruby 的小技巧,讓你 coding 比較 Ruby Way~~
我列出一些很好用的

1.
for i in (1..10)
puts i
end

可以這樣寫
(1..10).each{|i| puts i}

1.upto(10){|i| puts i}
2.
number = 1 if number.nil?
number = 1 unless number

可以這樣寫
number ||= 1


3.
result = []
(1..10).each{|item| result <<>

可以這樣寫
(1..10).inject([]){|array, item| array <<>

極有用~~~

4.
平行Assignment
a,b = 0, 1
a,b = b, a


5.
a, *b = [1,2,3] #a = 1, b = [2,3]


6.
begin
1/0
rescue
puts 'wrong'
end

可以變成
1/0 rescue puts 'wrong'


7.
opt中同樣Key 的內容會覆蓋default_opt中key的value
def image(opt={})
default_opt = {:height => 25, :width => 10}
default_opt.merge! opt
end

LL Ring Fight

LL 就是 Lightweight Languages 的簡稱
參賽者是Awk! Python, Scheme (Gauche),Squeak! Haskell? Kahua? Ruby, Perl, OCaml, Ethna, Django, FORTH!
官方網站在這裡
相關報導
  1. 台灣 Ruby 社群
  2. Telebody::Falcon
  3. 影片下載
  4. 日本 Ruby Magizne

日本人腦袋在想啥 ?
就連個程式比賽都要像電視冠軍一樣,搞個擂台
不過還蠻像WWF 的,快笑死了 XD







最後結果是~~~~~~~~~~~~~~~~~~~~~~

Ruby 獲勝~~~



贊助社團有

(株)アスキー(http://www.ascii.co.jp)
   日本UNIXユーザ会(http://www.jus.or.jp/)
   Kahuaプロジェクト(http://www.kahua.org/)
   Tokyo Perl Mongers(http://tokyo.pm.org/)
   日本PHPユーザ会(http://www.php.gr.jp/)
   日本Pythonユーザ会(http://www.python.jp/)
   日本Rubyの会(http://jp.rubyist.net/)
   日本 GNU AWK ユーザー会(http://gauc.no-ip.org/)
   (有)シングラム(http://www.syngram.co.jp/)
   (株)びぎねっと(http://begi.net/)
   サイボウズ・ラボ(株)(http://labs.cybozu.co.jp/)
   ほか

參賽者

01 Haskell:酒井政裕氏(慶應義塾大学大学院)
02 Scheme (Gauche):川合史朗氏(Scheme Arts )
03 awk:斉藤博文氏(日本GNU AWK ユーザー会)
04 OCaml:松本宗太郎氏(筑波大学大学院)
05 Lisp:中村正三郎氏(アンテナハウス本社グループマネージャ)
06 Scripting for Java Platform:櫻庭祐一氏(横河電機)
07 PHP:廣川 類氏(日本PHPユーザ会)
08 Perl:竹迫良範氏(サイボウズ・ラボ/Shibuya.pm)
09 JavaScript:天野仁史氏(ガイアックス/Shibuya.js)
10 Python:柴田 淳氏(ウェブコア/日本Pythonユーザ会)
11 Ruby:まつもとゆきひろ氏(ネットワーク応用通信研究所)
12 ActionScript:上条晃宏氏(アドビシステムズ)
13 FORTH:別府政通氏(構造計画研究所)
14 Squeak:梅澤真史氏、南谷千城氏(Squeak-ja)

Feed Reader 實做

RSS 2.0
Ruby 下面要實做 RSS 2.0 Reader 實在相當的簡單
require 'rss/2.0'
require 'open-uri'

feed_url = 'http://blog.yam.com/twmovie/rss.xml'
open(feed_url) do |feed|
RSS::Parser.parse(feed.read , false).items.each do |item|
puts 'Title ' + item.title
puts 'Link ' + item.link
puts 'Content ' + item.description
puts 'Time ' + item.pubDate.to_s
puts "\n\n"
end
end

上面這一段就是讀取相關的資訊
如果你只想取出前幾個 feed ,你可以使用 each_with_index
require 'rss/2.0'
require 'open-uri'

feed_url = 'http://blog.yam.com/twmovie/rss.xml'
@feed_contents = Array.new
open(feed_url) do |feed|
RSS::Parser.parse(feed.read , false).items.each_with_index do |item , i|
@feed_contents << class="keyword">if i < class="keyword">end
end
這段就是取出前三的 feed 的 content 放入 @feed_contents 這個 array

程式碼參考自此連結

PS.
文中的 RSS Feed
http://blog.yam.com/twmovie/rss.xml
是一個極有意義的 Blog
因為有您 國片起飛,支持 Ruby on Rails 也請支持優質國片

ATOM 1.0
ATOM 1.0 Reader 是這樣做的
首先要先用 gem install atom 套件
gem install atom
然後再用 http / uri / atom 來 parse
我不確定 open-uri 可不可以使用 atom @@!
require 'atom'
require 'net/http'
require 'uri'

feed_url = 'http://blog.yam.com/twmovie/atom.xml'

Atom::Feed.new(Net::HTTP::get(URI::parse(feed_url))).entries.each do |entry|
puts "'#{entry.title}' by #{entry.authors.first.name} on #{entry.published.strftime('%m/%d/%Y')}"
puts "'#{entry.summary}'"
puts "'#{entry.content.value}'"
end
本程式參考自這個網頁

9/20/2006

Rails Cache 第一步 : Page Cache

Rails 分為 Page Cache ,Action Cache,Partial Cache,現在就先來講 Page Cache。

首先,將你想 cache 的 page 在 controller 裡面加入 caches_page :action_name
class AbcController < ApplicationController
caches_page :index
end
然後確定你的環境底下有 enable cache,Rails 裡面 development 環境下是 disable cache ,production 環境下是 enable cache。如果要在 development 下面 enable cache,可以在 config/environments/development.rb 下面,加入
ActionController::Base.perform_caching = true
即可。

如此以後你 click 這個頁面,他就會幫你 cache 住頁面。如果你仔細翻 log 的話,你會發現有這一段
Cached page: /abc.html (0.00062)
然後你發現到 public 底下有一個 abc.html,裡面就是你的 cache page。如果是 index action ,他就直接 cache 成 controller 的名字。如果是其他 action ,他就 cache 成 controller_name/action_name.html。
像是 ABC controller 的 index action 就 cache 成 public/abc.html
像是 ABC controller 的 go action 就 cache 成 public/abc/go.html

你可能會覺得奇怪,為何他的 cache page 跟其他的 System 的 cache 不太一樣。其他系統的 cache 檔名可能是 ajbieohhwio.86787e27.87887073 之類,為啥Rails 的這個 page cache 這個那麼的好讀?

仔細思考一下,當我們 request abc controller 的 index action 時候。大家都是打 http://www.example.com/abc 。那麼 lighttpd 接受到這個 request 的時候(這裡僅僅指 lighttpd ,其他 web server 不確定)。他會先去 document root 尋找 abc.html。結果他就發現有 abc.html ( cache 的 page ),就立刻 return回去。當中完全沒有進到 ruby cgi,完全沒有 cgi 快慢的問題,所以這是最快的 cache 方式

如果你某種情況要 expire cache。像是修改 db 之後,要將原先的 cache reload。你可以使用
     expire_page :action => 'index'
來手動 expire 掉。

但是要注意,這種 page cache 沒有設定 expire time 的方式。所以說,要嘛就是當經過某些特定的動作時 expire 掉 cache page,要嘛就是跑一個 backend process 去清掉 cache。這個網頁有批評這個 page cache 沒有 expire 機制根本就沒有用。

不過,對這個網頁的論點,我呈反對態度。

就舉個例子,樂多Blog的作法就很類似。裡面我們個人Blog每個頁面都是靜態 HTML 頁面,而非 PHP,每當我們做出一個改變的時候,管理頁面就會幫我們產生新的靜態頁面。這樣的作法當然在產生的時候比較慢,但是 show 頁面的時候可以達成比任何 CGI 還要快的效率。

結論

Cache 好不好用是看你怎麼用,以及什麼情況要用啥方式。而不是設計出一個每個情況都可以用的方式,那只會顯得臃腫沒意義。

9/18/2006

Rails +MySQL UTF-8 問題

今天遇到 Rails + MySQL 4.1 取出 UTF-8 的問題(MySQL 5 就不會這樣:p)
請在 config/database.yml 裡面
加入 encoding: utf-8
development:
__adapter: mysql
__database: abc
__username: lala
__password: abc
__host: 1.2.3.4
__encoding: utf8
即可

Rails 底下的 RDOC

Rdoc 是 Ruby build in 的 doc system
因為做的很好
Rails 就直接採用了
如果你不想記格式
你只需要在你的 Rails 程式裡面寫註解
打入
rake appdoc
他就會在你的 doc/app/ 目錄下面產生 doc system 檔案
預設是 HTML 格式,直接看 index.html 就好了

再來,就先介紹一下最重要的技巧
1. 如果你想表示這個class ,就在所有Class的前面加入註解,他就會在那個 Class 頁面產生相關的描述檔
# 這個class用來來幹麻幹麻的
class ApplicationController
2. 如果你像對某個 method 做 doc ,就在那個 method 前面加入註解
#這個method用來幹麼幹麼的
def redirect_to_index_page
end
3. 如果你在 ABC Class 裡面想表示 DEF Class 裡面的 method lala()
要這樣寫 DEF#lala
這樣他就會自動幫你建 hyperlinik 到那邊

剩下的技巧我覺得不是特別常用
不過就當嘗試一下摟

1. 建立超連結就是 {超連結名字}[超連結url] {abc}[www.google.com]

2. #-- 下面的註解不會建立 rdoc ,但是之後加上 #++ ,後面的註解就會被 rdoc parse
#這裡記載著所有Global 變數以及Authoize 函式
#1這裡記載著所有Global 變數以及Authoize 函式
#
#--
#2這不會顯示
#
#++
#
#3這裡會顯示


3. List 就是加 * 像是
#* 123
#* iujoj
#* 789
4. h1 , h2 , h3 就是 = , == , ===
#= Header one
#== Header two
#=== Header three
5. 斜體字是用 _ 包起來,粗體是用 * 包起來
_page_ => page
*page* => page

Capistrano小介紹

這次 Rails Conf 2006 有提到 Capistrano 1.2 出了
Capistrano是ruby on rails提供的Scalable 方案
原名叫SwitchTower
這裡是 Capistrano 的 Manual
這裡有一篇對岸的簡介 ,相當的實用
這裡有一份 Introducing the Capistrano Shell
ps.
Capistrano有可能是洛杉磯南40公里的San Juan Capistrano市名
和當地出名的燕子教堂有關

[心得] Ruby 效能在 Linux 上面比 Windows 好

這裡有一位對岸網友提出的Ruby 跟 Python 比較,引發一陣討論。不過裡面的test case 主要是大量的字串操作比較消耗時間,Ruby 在這裡會輸主要原因是因為Ruby 的字串是 mutable,而 Python 字串是 immutable,其他的算法方面倒是沒輸多少。

不過這篇Performance裡面倒是提到一件很值得注意的事情
Ruby 在 Linux 上面跑得比 Windows 來得快,記憶體也比較省
至於這個推論,也有另外一個更誇張的證言
我在虚拟机里只给NetBSD 64M内存,居然比在1G内存的winxp上运行速度还快!
可見得 Ruby 的確是 Unix 的產物,如果要跑 Ruby on Rails 最好不要用到 Windows 比較好。

以下是實驗數據,不管是 Windows or Cygwin ,Ruby on Linux 都贏

TEST CASE 1 : 速度比較
Ruby1.8.4@Win32:0.641s
Ruby1.8.5@Cygwin:0.558s
Ruby-yarv@Cygwin:0.453s
Ruby1.8.5@Ubuntu: 0.434s
Ruby-yarv@Ubuntu: 0.373s

TEST CASE 2 : Memory比較
ruby1.8.4@win32: 5.63, 97M
ruby1.8.5@cygwin: 4.77s, 93M(3.84s, 95M)
ruby1.8.5@ubuntu: 3.78s, 91M (3.36s, 91M)

另外一個 TEST CASE
ruby1.8.4@win32: 7.3s, 5M
ruby1.8.5@cygwin: 6.45s, 5M(6.04s, 5M)
ruby1.8.5@ubuntu: 4.25s, 3.2M(4.11s, 3.2M)

PS.
Performance比較,一向是各說各話。尤其是硬體的比較(AMD 跟 Intel 效能比較更是烽火連天),不過 Ruby 的慢一向是有口皆碑 (笑)。所以跟 Python 比速度,好像也沒啥好比的 XD,反正我們是靠其他部份去贏人的。

HTML?New Template System ?

現行 Scripting Language 不管PHP,ASP,或是ERB大多是採用
              <td width="100%"> <b><%= link_to '請按此' , :action => 'index' %></b></td>
類似這樣的形式。也就是 HTML 為主,並且在裡面嵌入 scripting language code。Rails ERB也是這樣的作法,不過Ruby 愛好者有一種不知道是好還是壞的想法
VIVA ,Ruby 統一全世界
Javascript 用 RJS 併吞掉、SQL 用 ActiveRecord and Migrate 併吞掉、Web Service 用 Action Web Service、Mail 用 Action Mailer、甚至Web Server 都有 Ruby 版本( Webricks 還有 Mongrel )。這些我都相當的贊成,因為 Javascript 跟 SQL 的混亂已經嚴重影響到程式設計師
今天卻發現 HAML and MarkBy,可以讓 HTML 也危在旦夕(笑)。

HAML 就是原本的RHTML
<small><%= item.body %></small>
變成
%small= item.body
而MarkBy更是厲害
require 'markaby'

mab = Markaby::Builder.new
mab.html do
head { title "Boats.com" }
body do
h1 "Boats.com has great deals"
ul do
li "$49 for a canoe"
li "$39 for a raft"
li "$29 for a huge boot that floats and can fit 5 people"
end
end
end
puts mab.to_s
如此用 Ruby 寫出 HTML @@!。對岸這對於這方面的態度是將這些東西,歸類在近乎奇淫技巧 上面。

我的感覺是這樣

還記得 OSDC 2006 in TW上面,Ingy 在講到Wiki語法混亂的問題時(只憑記憶,有錯請見諒)
有人說 Wiki 語法太多太亂,需要 Standard 統一 ,但是依我所見,其實現在早就有 WIKI Standard 了,那就是 HTML
Ruby 統一全世界當然是一個美好的夢想,畢竟我是100%原汁的 Ruby 派。但是當你在商業應用上,版面設計通常交給美工,他們只需要會 Dreamweaver 之類的東西
這時候,難道你要教美工 Ruby 程式設計?
所以,除非 Dreamweaver 或是 Frontpage 支援 Ruby @@!,不然大家還是先把 HTML 當成網頁程式設計的最大公約數好了。

Why Rails use Ruby

這是一個很敏感的話題
到底其他語言寫不寫得出 Rails ?
或是說,能不能寫的跟 Ruby 版的 Rails 一樣好

我本身用過 CakePHP
(摸了快一個月,而且公司的 Project 要用,所以是很認真的摸)
發現到雖然有 Rails 的型,但是卻沒有神髓
光是一個 ORM 的 ActiveReocrd
1 : M 的 relation 就讓人搖搖頭(save 不能幫忙處理 releationship)
我也有跟可能是國內最熟悉 CakePHP 的人聊天(阿凱兄)
他也承認雖然兩者是一樣的架構,CakePHP 目前的確比不上 Rails
(記住,是目前,以後會怎樣不知道)


DHH 在開發 Rails 初期也嘗試過用 PHP 寫
一直到他發現 PHP 的作法實在太過 Dirty
他才開始改用 Ruby 寫,他也很滿意 Ruby 寫出來的成果

JAVA 或是 Python 或是 .NET 的 Rails Clone 版我沒 test 過
所以我無法下定論

我想說的是
雖然Rails Clone架構大同小異
但是語言本身的能力不同,所產生的 Power 具有很大很大的不同
雖然只要是 tuning machine 架構下的程式語言
理論上可以做到一樣的事情
但是剖析字串的程式,大家應該都不會用C去寫,你說是吧 :p

Rails Conf 2006 in Europe 討論議題大觀


9/15/2006

Martin Fowler 認為 DHH 跟 Kent Beck 同等地位

Martin Fowler(知名的IT作者) 在這篇文章 提到
他認為Rails的作者 David Heinemeier Hansson 已經跟提出XP programming 概念的 Kent Back 同等地位

Rails will do what it does, and will not complicate itself to support things it doesn't like.
Rails 會專注他想達成的事情,並且不會引入一些他認為不重要的事情來將 Rails 複雜化

In this sense I see a startling parallel between DHH and Kent Beck. For either of them,if you present them with a constrained world, they'll look at constraints we take for granted, consider them to be unessential, and create a world without them.
在他心目中,
他認為Rails的作者 David Heinemeier Hansson 已經跟 Kent Back 同等地位
因為他們都會努力去消除一些我們容許的瑣碎事

That's why they can create things like Extreme Programming and Rails which really give the industry a jolt.
這也是為什麼他們會創造出像是 Extreme Programming 或是 Rails 這種翻動業界的事情

Rails 1.2 以及 Rails 2.0 的目標

原出處在此
DHH在 Rails Conf 2006 in Europe講到 Rails 1.2 以及 Rails 2.0 預計的目標

Rails 1.2

Rails 1.2 RC 快要完成了,目標是
  • RESTful ,不懂這個意思的人可以看 Wikipedia
  • contollers , models 都將 support modules
  • Routing 有大改變
  • bug fixes,Tiny tweaks....之類的繁雜事
New Scaffold

會有更漂亮,更多功能的 Scaffold (不過我聽說還是一樣醜 XD)

Rails 2.0

View 會是有比較重大的變化,會引進一個 modeule SimplyHelpful。
他會增加這些的功能
  • div_for (新功能)
  • RJS
  • partials
  • collections
  • form_for
  • Link_to

我個人是對 RESTful 有興趣啦,不過我目前實在是不太了解他的意思
allow us to focus on more important things Rails is not about inventing your own style for doing common things
有人知道嗎?

ps. 這篇講到 Rails 的 RESTful 作法

學習 Programming 的歷程

這位作者提出他的學習 Programming 的歷程
他說到他的學習過程是
PHP -> OO PHP -> Ruby -> OCaml -> Lisp
每個歷程約為各一年
我覺得好像不是特別好的建議
PHP是個好開始,因為你不需要 Handle Type 的問題
可以直接學習程式流程
但是學習PHP 需要架設 Server 的概念
實在有點麻煩
而且學習OO ,用PHP去學就很奇怪了
PHP 不是一個培育 OO 的好教室呀~~~
PHP也不用學習兩年呀@@!半年就差不多了
題外話:OCaml 是啥語言呀? 有人知道嗎

ㄜ,我是個人的學習歷程是
C -> JAVA -> Perl -> PHP(SQL) -> Javascript(AJAX) -> Ruby
最近想要回去溫習 C ,以後有機會去碰碰 LISP

  • C 花了半年,學習了許多程式設計的最基本觀念,但是其他記憶體操作,指標的部份我早就忘光了。
  • JAVA 學了最久,三年多。學會了基本的 OO 概念。但是 JAVA 的書籍實在太多也太複雜,我實在掌握不住 JAVA OO的精髓。直到有一天,我跟別人要一份JAVA code ,花了一個下午看懂後,突然發現我的 OO 概念長了一倍。
  • Perl 學了半年,因為我當時為了交一份將 C code 轉換成 machine code 的作業才學的。因為這份作業牽涉到極為複雜的字串剖析跟代換,那半年的學習,讓我相當紮實的學會了 regular expression ,也可能是我學到最有用的東西之一。一直到我碰到 Ruby 之前,Perl 一直在我心目中一直是第一名的language,也是我的工具箱裡最萬能的瑞士刀。
  • PHP 是接案子才學的,有Perl 跟 C 的基礎的我,學習 PHP 一點難度都沒有(PHP是改自Perl)。唯一有難度的地方在於 HTTP 的 coding 需要很多特殊的技巧(GET,POST,Session ,Cookie),所以學習 PHP 可以順便學習紮實的 WEB Progarmming 概念。在我心目中,PHP是Web Programming裡面的C,是最基礎的語言。
  • JAVAScript 是為了學習 AJAX 才開始真正摸的。學習的過程發現他很有趣,很棒,但是很難 Debug ,Browser 又一堆 Bug。其實不是一個很好的學習環境。
  • Ruby 是為了 Ruby on Rails 才學的。但是開始學的第三天,我就開始把所有的 Perl code 轉成 Ruby 了。Ruby 兼具我喜歡 Perl 的所有特點(Regular Expression強大,語法多變,效率高),又擁有所有我希望 Perl 擁有的特色(語法清楚,物件導向明確,好學好記,下一次看到 code 還知道我之前在寫啥)

如果要我給予建議,一個從零到有的網頁程式設計師
我建議是這樣的路
HTML (CSS) -> Javascript -> PHP -> SQL -> AJAX -> Ruby -> Ruby on Rails
  • HTML 不是程式語言,但是他是網頁設計師必修
  • Javascript 當作第二個的原因是因為他好學,Dynamic typing 的元素也方便初學者使用,並且他不需要架設任何的 Server 的相關知識,所以上手比 PHP 更容易
  • PHP 可以學到許多網頁設計的基本概念,並且資源眾多,相當容易上手
  • SQL 要學的原因是因為有時候牽涉到資料庫運作時,有時候一句 SQL 抵的過 30 行 PHP code 。
  • AJAX 要學的原因是因為不學就找不到 網頁設計的飯碗,並且他代表一種開發新流程。
  • Ruby 跟 Ruby on Rails 要學的原因是因為在我心目中,他是目前第一名的網頁開發框架。並且你在學習 Ruby 的過程中,你可以學到太多跟 PHP 不一樣的概念。


初探 ActionMailer : 使用 sendmail 寄信

Action Mailer 是 Ruby on Rails 上面收發信的機制
不過因為收信實在牽涉到太多東西
我們先不予以探索,只論發信的機制

就算是寄信,也有很多方式
可以用 SMTP Server 來寄
但是也要設定很多東西
身為 Rails Programmer ,而非網管
我們先使用最最最簡單的 sendmail 來寄信

測試Sendmail

要用寄信前,請先在命令列上打
sendmail abc@example.com
等到他出現下一行
就可以亂打一些內容
然後按下 Ctrl + D 寄出
麻煩請記住
如果連手動使用 sendmail 都寄不出去
代表你現在的機器寄信機制上面有問題
ActionMailer 更不可能記得出去

如果你的機器上面沒有 sendmail 指令
請按照這個網頁安裝 Postfix

使用 ActionMailer

如果您想要繼續看這一章
麻煩請先把上一章測試成功
上一章 sendmail 測試不成功,這一章是不可能成功的

設定

要設定 ActionMailer ,請到 config/enviroments/
修改你現在環境的設定檔
ActionMailer::Base.delivery_method = :sendmail
ActionMailer::Base.default_content_type = 'text/html'
第一行是告訴我們寄信的機制是用 sendmail ( 預設是 smtp )
第二行是告訴我們寄信的內容是以 HTML 格式來寄的 (預設是 plain text)

產生 Mailer

Mailer 在 Rails 裡面地位跟 Model 一樣
我們都是使用
ruby script/generate mailer 你要的mailer名字
來產生 Mailer

填寫內容

產生出來的 Mailer 會在 model 層裡面產生一個
class MyMail < ActionMailer::Base
end
我們可以加入一些 Method
class MyMail < ActionMailer::Base
def send_mail( user )
# Email header info MUST be added here
@recipients = 'thegiive@gmail.com'
@from = "accounts@mywebsite.com"
@subject = "測試標題許功蓋"
end
end

我們剛剛的 method 還沒有填寫內容
如果你眼尖一點
你會發現 Generator 會幫你在 view 裡面產生相關 Mailer 的目錄
沒錯
Mailer 裡面他也把 MVC 切的清清楚楚的
我們剛剛撰寫一個 send_mail method
我們就必須跑到相關的 view 目錄下
產生一個 send_mail.rhtml 來撰寫信件裡面的內容

但是如果我們想在 Mailer View 裡面使用一些 變數
我們必須在 Mailer method 加入一些 @ 變數
class MyMail < ActionMailer::Base
def send_mail( user )
# Email header info MUST be added here
@recipients = 'thegiive@gmail.com'
@from = "accounts@mywebsite.com"
@subject = "測試標題許功蓋"

# Email body substitutions go here
@body["first_name"]='first name'
@body["last_name"]='last name'
end
end
如此 Mailer 撰寫就沒啥大問題

使用

要怎麼使用他當然是在 controller 裡面使用啦
MyMail::deliver_send_mail @user

我們可以發現
我們直接使用 Mailer 而不實體化一個 Mailer Object
並且記住,我們剛剛寫了 send_mail method
但是要使用不是直接呼叫 send_mail
而是在前面加入 deliver_ 成為 deliver_send_mail

測試結果

Mailer 預設寄信的 Charset 是 UTF-8,我也測試過標題安放中文
我發現到不管是標題或是內文,都沒有中文的問題
算是很方便的機制

9/14/2006

database.yml 要注意的地方

今天遇到一個網友的一個小問題
當他在 ruby script/generate model abc 時
出現了這個 error
in `establish_connection': development database is not configured (ActiveRecord::AdapterNotSpecified)

看到這個問題,心中不自覺想到是 database.yml 設定問題
但是我發現到
Rails 根本就不管有沒有這個 database ,他都會 generate 這個 model file
或是給錯誤的帳號密碼設定,他依舊會沒 error 的 generate 這個 model file
也就是,他不會真的去 connect database 去查真的有沒有這個 table
所以不是這個問題

那到底是那裡的問題呢?

我發現到將
development:
___adapter: mysql
___database:
___username: root
___password:
___host: localhost

改成
developmentlalal:
___adapter: mysql
___database:
___username: root
___password:
___host: localhost

就會出現這個 error
也就是說,Rails 雖然不會 connect database
但是 generator 會先去查是不是有這個config

我就請他 show 一下 database.yml
發現到他是這樣的
development:
adapter: mysql
database:
username: root
password:
host: localhost
答案揭曉,YAML檔案沒有縮排

其實大家可能對 YAML 不太了解
YAML 是靠縮排跟 : 來代表結構的 ( 有點像 python )
縮排的 element 就是上面的 element 的 sub element

這告訴我們在 YAML 裡面,請注意縮排

lighttpd 跑 Rails 設定

最簡單的方式

其實Rails 自己就有附設定檔,如果你不想自己寫那麼多設定,只想用 lighttpd 跑 Rails。您首先要確定您這個 user 擁有可以使用 lighttpd 的權限 ,然後在 Rails 資料夾下打
ruby script/server
他應該就會直接使用 lighttpd,下面是啟動 lighttpd 正常的message
=> Booting lighttpd (use 'script/server webrick' to force WEBrick)
=> Rails application started on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server (see config/lighttpd.conf for options)
如果出現下面的message,代表只有啟動 Webrick
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
那通常就是您這個 user 不可以使用 lighttpd 的權限
打入
ruby script/server lighttpd

這個指令強制使用 lighttpd ,用於 Debug 用途。並且會將 lighttpd config copy 一份到rails 資料匣下面的 conf/lighttpd.conf ,config 預設 port 是 3000,如果你想要直接用 port 80,可以到裡面把改成
server.port = 80

詳細的方式

Var 的部份
var.railsbasedir = '/rails/root/dir/'
這裡是設定 config 檔的變數,以後修改位置改這裡就好了

Server Port的部份

server.bind = "1.2.3.4"
server.port = 3000
Server.bind 就是server 的 ip,server.port 就是lighttpd listen 的 port。

Modules 的部份
server.modules = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi","mod_compress", "mod_expire" )
這裡是相關一定要enable的 Modules


其他設定的部份
# Ruby on Rails Config
server.error-handler-404 = "/dispatch.fcgi"
server.document-root = var.railsbasedir + "/public/"
server.errorlog = var.railsbasedir + "/log/lighttpd.error.log"
accesslog.filename = var.railsbasedir + "/log/lighttpd.access.log"

url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )

compress.filetype = ( "text/plain", "text/html", "text/css", "text/javascript" )
compress.cache-dir = var.railsbasedir + "/tmp/cache"

expire.url = ( "/favicon.ico" => "access 3 days",
"/images/" => "access 3 days",
"/stylesheets/" => "access 3 days",
"/javascripts/" => "access 3 days"
)

fastcgi.server = (
".fcgi" => ( "localhost" => (
"min-procs" => 1,
"max-procs" => 3,
"socket" => var.railsbasedir + "/tmp/sockets/fcgi.socket",
"bin-path" => var.railsbasedir + "/public/dispatch.fcgi",
"bin-environment" => ( "RAILS_ENV" => "development" )
)))

$HTTP["useragent"] =~ "^(.*MSIE.*)|(.*AppleWebKit.*)$" {
server.max-keep-alive-requests = 0
}

有幾個地方可以注意
  1. var.railsbasedir的部份只是因為以後搬目錄方便設定,其實那裡都可以用絕對路徑設定
  2. "min-procs" => 1, "max-procs" => 3 代表最少fastcgi process 一個,最多三個
  3. "bin-environment" => ( "RAILS_ENV" => "development" ) 代表現在 rails 是啟動 development 的環境,可以轉成 production 跟 test

9/13/2006

Rail 的 log level

參考自 RailsCN這裡
Rails可以選擇不同的log level 來控制 log 輸出
可以使用的log level 有
  • :debug -> 提供最詳盡的log,連SQL都會紀錄,很快就會很大
  • :info -> production下的預設值,不會寫出SQL,但也很多。如果是ActiveMailer,它會記錄下每封信的內容
  • :warn -> 只記錄 warning 等重要的信息.
  • :error
  • :fata

修改 log level 就修改 config/environment.rb
config.log_level = :debug
不過修改這裡會讓所有環境下面都使用同樣的 log level ,請注意

Lighttpd 的 Rails加 PHP 設定 (相同 Domain不同目錄)

上一篇介紹了不同 Domain 的 Rails 加 PHP 設定。而根據Syschen這篇文章,裡面介紹了 Single Domain 不同 web dir的設定方式,實在受益良多。但是根據 ihower 的回報
成功跑起來之後,我發現 public 裡面的css跟圖片連結等還是不對。
我嘗試了一下,當我將一個跑 Multi Domain 的 Lighttpd 改成單一 Domain ,將一個原本顯示好好的圖片,像是 http://rails.host.com/images/abc.jpg 改成了 http://rails.host.com/rails/images/abc.jpg 之後,然後 reload,他會出現

Routing Error

Recognition failed for "/rails/images/logo_friends.gif"
恩,很明顯出現上面 ihower 兄講的問題。根據 PTT 版友 tka  提示我
小弟 亦遇此問題 經由google搜尋後
自中這個網頁獲得解答
關鍵在於 alias.url 這個設定值 若依照該網頁之方式設定 便可得到 正常的結果
所以我仔細看了一下這個網頁,簡單列出幾個重點,除了我上一篇的重點外,還要加上 alias.url 才可正常 work

Lighttpd Conf

var 部份一樣加入一個 var.railsbasedir
var.railsbasedir = "/var/www/friends.roodo.com/rail1/"

Modules 要加上 mod_fastcgi , mod_rewrite, mod_redirect , mod_alias
server.modules += ("mod_fastcgi" , "mod_rewrite", "mod_redirect" , "mod_alias")

index file 要加上 dispatch.fcgi
index-file.names = ( "index.php", "index.html","index.htm", "default.htm", "dispatch.fcgi" )
Virtual Domain 要改成 目錄設定
$HTTP["url"] =~ "^/rails/" {
include 'rails.conf'
}

$HTTP["url"] =~ "^/php/" {
include 'php.conf'
}
rails.conf 等下就會講到怎麼寫,php.conf 可以看 Lighttpd 的 Rails and PHP 設定 (不同 Domain ) 寫法

在 lighttpd config 底下新開一個 Rails.conf

我加入了 alias.url
server.document-root = var.railsbasedir+"public/"
alias.url = ( "/rails/" => var.railsbasedir+"public/" )
server.error-handler-404 = "/dispatch.fcgi"
server.indexfiles = ( "dispatch.fcgi", "index.html" )
fastcgi.server = ( ".fcgi" =>
( "localhost" =>
(
"min-proc" => 1,
"max-proc" => 1,
"socket" => var.railsbasedir+"tmp/app1-fastcgi.socket",
"bin-path" => var.railsbasedir+"public/dispatch.fcgi",
"bin-environment" => ( "RAILS_ENV" => "development" )
)
)
)


Rails Routing Config

修改 conf/route.rb,告訴 Rails 您處於 http://www.abc.com/rails/ 底下
map.connect 'rails/:controller/service.wsdl', :action => 'wsdl'
map.connect 'rails/:controller/:action/:id'

顯示結果

如此當我將一個原本顯示好好的圖片,像是 http://rails.host.com/images/abc.jpg 改成了 http://rails.host.com/rails/images/abc.jpg 之後,他的確可以正常顯示了。

不過,我程式所有的圖片路徑都是直接指定 img src="/images/abc.jpg",所以我的程式裡面的圖片完全無法正確顯示,所以我的程式寫法是無法從 Multi Domain 改成 Signal Domain 的。解決方式就是在 helper 裡面,將所有圖片顯示的地方都用 helper 來幫我顯示。

延伸閱讀
  1. 這裡有一份 Apache + Reverse Proxy + Mongrel 的相同 Domain 不同目錄的設定方式

Lighttpd 的 Rails and PHP 設定 (不同 Domain )

Rails 跑在 Lighttpd 上面的設定一向不簡單,尤其是可以同時跑 PHP 跟 Rails 的設定更是麻煩,下面我參考 Rails 內附的 lighttpd 修改出相關的設定檔。我是使用 不同 domain 來區別 fastcgi 的設定。
  • Rails Domain 是 rails.example.com
  • PHP Domain 是 php.example.com
Rails + PHP 的 lighttpd 設定檔

Var 的部份
var.railsbasedir = '/rails/root/dir/'

Modules 的部份
server.modules = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi", "mod_compress", "mod_expire" )

Virtual Host Rails 的部份

$HTTP["host"] == "rails.example.com" {

# Ruby on Rails Config

server.error-handler-404 = "/dispatch.fcgi"
server.document-root = var.railsbasedir + "/public/"
server.errorlog = var.railsbasedir + "/log/lighttpd.error.log"
accesslog.filename = var.railsbasedir + "/log/lighttpd.access.log"
url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
compress.filetype = ( "text/plain", "text/html", "text/css", "text/javascript" )
compress.cache-dir = var.railsbasedir + "/tmp/cache"
expire.url = ( "/favicon.ico" => "access 3 days",
"/images/" => "access 3 days",
"/stylesheets/" => "access 3 days",
"/javascripts/" => "access 3 days" )


fastcgi.server = ( ".fcgi" => ( "localhost" => (
"min-procs" => 1,
"max-procs" => 3,
"socket" => var.railsbasedir + "/tmp/sockets/fcgi.socket",
"bin-path" => var.railsbasedir + "/public/dispatch.fcgi",
"bin-environment" => ( "RAILS_ENV" => "development" )
}

Virtual Host PHP 的部份

$HTTP["host"] == "php.example.com" {
# PHP mod_fastcgi config
server.document-root = "/php/doc/root/dir"
fastcgi.server = ( ".php" =>
( "localhost" =>
(
"host" => "61.218.90.243",
"port" => 1026,
"bin-path" => "/usr/bin/php-cgi",
"min-procs" => 1,
"max-procs" => 1,
)
)
)
}


要注意的部份

有幾個地方可以微調
  1. var.railsbasedir 填入你的 rails 的 base dir
  2. "min-procs" => 1, "max-procs" => 3 代表最少fastcgi process 一個,最多三個
  3. "bin-environment" => ( "RAILS_ENV" => "development" ) 代表現在 rails 是啟動 development 的環境,可以轉成 production 跟 test

Ruby 的 " 延續 " 觀念 : Continuation

試著想想這個程式(出自 Beyound JAVA 範例)

1 def con_loop

2 for i in 1..5 do
3 puts i
4 callcc { |a| return a } if i == 2
5 puts '#'
6 end
7 return nil
8 end

9 puts
'Before loop call'
10 cont = con_loop()
11 puts 'After loop call'
12 cont.call if cont
13 puts 'After continuation call'
想想這個程式會怎麼做?

這是這個程式的結果,很出人意料之外吧
Before loop call
1
#
2
After loop call
#
3
#
4
#
5
#
After loop call
After continuation call
為什麼 After loop call 執行了兩次?為什麼 1 2 跟 3 4 5 中間夾了一個 After Call。這就講到一個有趣的物件 Continuation,他是一個物件,裡面記載著程式運作的狀態
沒錯,他記載了程式運行的狀態!!!
好像遊戲打到一半,媽媽叫你去吃飯,你只好心不甘情不願的去存檔。可是吃完飯,load 剛剛存的檔,你的角色的經驗值都還在,寶物都不會不見一樣。Continuation 就是那個遊戲的紀錄檔

現在可以討論這個程式了。

  1. i 進入loop裡面 ,puts 1 ,puts 2。
  2. 當 i = 2 時,下一行也就是第四行,因為 if i == 2 這個敘述的緣故 媽媽叫你吃飯了)
  3. 會被強迫跳出這個 loop ( return a ) (只好關機)
  4. callcc { |a| return a } 他會 return 一個 continuation 物件,continuation 裡面的內容就是你遊戲的紀錄檔,他會在 10行裡面,存到 cont 這個 Object 裡面 (存檔等下再來玩),所以程式會 puts 1 , 2 之後,就進入第11行的 puts 'After loop call',這也是第一次的 After loop call。
  5. 第12行,他會檢查 cont 這個物件有沒有值 (檢查存檔在不在)
  6. 發現到有值,就執行 Continuation 物件的 call method (呼叫存檔)
  7. 他會到 loop function 裡面 ,剛剛 return continuation 物件的地方。最重要的是他會紀錄 i 這個 tmp variable 是 2,因為他本來就會紀錄程式狀態。
  8. 執行第五行的 puts '#',這也是為啥,結果裡面第一個 After loop call 後面是 #。
  9. 他會繼續的進行 loop ,從 i =3 ~ 5
  10. 最後執行結束,他會在第7行 return null 值,交給 cont 物件。
  11. 之後他就會執行第二次的 After loop call。

這個機制怎麼玩呢?

我們可以想到,程式執行到一半,有個 interrupt,他出去執行一下 error handler ,然後回來繼續處理剛剛的事情,當然我們當然可以用其他方式做到類似的事情,不過這個作法的確提供很方便的方式。

根據 Wikipedia 的講法,Lisp ,Ruby ,Smalltalk 都有提供類似的作法(果然 Ruby 是 Perl + SmallTalk + Lisp ),但是我比較好奇 C 也有這種作法?

改天我會提到利用這個觀念,產生的延續性伺服器的想法。

RoR Access Control : before_filter

基本上,RoR要做到 Access Control 的部份
使用 before_filter 是最簡單的方式

before_filter 就是當你進入這個 controller
他會先經過這個 filter
然後才會執行相關的 action
所以我們可以在 before_filter 裡面
加入 session 裡面是不是有 user 資料
如果沒有,導入到 login 頁面
如果有,就直接自動執行

範例,一個 Controller 名字叫做 Apple ,他裡面要做 access control
class AppleController < ActionController::Base
before_filter :authorize

def authorize
redirect_to :controller => 'login' , :action => 'index' unless session[:user]
end
end

但是問題來了
今天一堆 controller
如果每個都得加上 before_filter
那不是加到累死了
而且嚴重違反 DRY 準則

所以我們想到了
所有controller 都繼承 ApplicationController
那我們加到 ApplicationController 裡面就好了
class ApplicationController < ActionController::Base
before_filter :authorize

def authorize
redirect_to :controller => 'login' , :action => 'index' unless session[:user]
end
end
呵呵,這樣就萬事大成了嗎?
怎麼瀏覽器不動了?
看看 log ,發現到 before_filter 的確 work
但是就連我們的登入頁面 LoginController 裡面的 index action 進入 before filter
所以呈現一個無窮迴圈

要怎麼做呢?
其實我們只要每個 controller 都有 before filter
但是 login controller 不需要 before filter 即可
class LoginController < ActionController::Base
skip_before_filter :authorize

.....
.....
.....
end
我們在 LoginController 裡面使用 skil_before_filter 將 before_filter skip 掉
大功告成!!!

9/12/2006

BreakPointer

Rails 什麼都有什麼都賣什麼都不奇怪
當然有Debug 摟~~~~
噹噹鐺~~~讓我們歡迎 breakpoint~~~

Deamon 執行方式
他是一個 deamon
只要按下
ruby script/breakpointer

他就會啟動這個 deamon
但是如果你發現到你的DNS沒設定好
他就會很快的 echo 一個 error
/usr/lib/ruby/1.8/drb/drb.rb:837:in `getaddrinfo': getaddrinfo: Name or service not known (SocketError)

其實不用害怕,遇到這個問題
只要設定好 hosts file
或是用
ruby script/breakpointer -c druby://localhost:12345
他就會自動解決這個小問題

Debug 方式

將你預計要設定 breakpoint 的地方使用
breakpoint
像是這段 code

@user = get_user_from_db
@message = Model.find(...)
breakpoint

再來,只要你的web server 點到這個部份的 code
你就會發現你的 rails 程式一直 loading 就是不動(因為 breakpoint了)
這時候去看看你的 breakpoint console
原本是
Tries to connect will be made every 2 seconds...
會變成
Executing break point at .......
有這個 message 就是代表breakpoint這個 deamon 已經抓到 breakpoint
並且他正 hand 在那邊等你的debug
當抓到breakpoint
就會會有一個 irb 介面
你就乖乖的使用 irb 去 show 出你要的 message
很簡單,而且超有用的工具

SQLite GUI : SQLite Database Browser

網址在此
看起來是市面上最好的 SQLite DB Browser 了
該有的都有,也支援 OSX ,也是 Open Source
不過 create 實在麻煩,要一個 column 一個 column 的加
不過有更好的 SQLite Browser 嗎?

使用 migrate 將 MySQL db porting 到 sqlite

Migrate 就是Rails 提供的工具
簡單的說法就是使用 Ruby 寫 SQL code
他有很多用法
不過到最後我還是直接用 phpMyAdmin 去寫
因為他無法做到一些 DB Schema 的微調
(像是 enum 之類的東西)
至於 version 的部份,也可以用 svn 去做
所以感覺不是特別實用

但是他有一個功能很偉大
就是他可以做到 跨平台的 DB Schema porting
VIVA ,Rails 萬歲

我們來使用看看吧
操作方式是從 MySQL porting 到 SQLite
(記得將 config database.yml 設定正確)

1. 在MySQL機器上面的 Rails 目錄下
rake db_schema_dump
他會將所有的schema 轉換成 db/schema.rb

2. 在另外一個SQLite Rails 的目錄下
將剛剛export出來的 schema.rb 放入 db/ 底下
rake db_schema_import

他會將 schema.rb 裡面的 migrate code 轉換成 SQLite 的 sql
然後 import 進去 SQLite 的資料庫


如此就可以很簡單的做到 跨 DB 的 Schema import

9/11/2006

validates_associated 的用法

如果當你在一個 Form 裡面使用兩個以上的 Model
而且你又想要 跨 Model Validate
那你就得使用 validates_associated

舉個例子
兩個 Model : user , : user_data
user 裡面有 name , email 需要 validate
user_data 裡面有 sex 需要 validate
並且我們將 view 加入 error_message_of
# Model
class User < ActiveRecord::Base
has_one :user_data
validates_presence_of :name , :email
validates_associated :user_data
end

class UserData < ActiveRecord::Base
belongs_to :user
validates_presence_of :sex
end

#View
<%= error_validate_of 'user' %>

如此當我們這些欄位有 insert 過不了 validate
他就會顯示跨 Model 的 error message

但是這裡有一個問題

如果這個例子中的 sex 出現 validate error
我們會發現到他僅僅顯示

There were problems with the following fields:

  • User data is invalid
  • Email can't be blank
只會顯示 Model Name is invalid
並非是原先我們預計的 sex can't be blank
代表雖然他會去 involke associated model 的 validate function
但是顯示還不夠

如果加上
<%= error_messages_for 'user_data' %>
那就會顯示 Sex can't be blank

There were problems with the following fields:
  • User data is invalid
  • Email can't be blank

There were problems with the following fields:

  • Sex can't be blank

問題又來了
我們不需要兩個 error message 呀~~~
而且 User data 跟 sex 其實是重複的 message

難道這兩個真的難兩全?

解決方式

其實解決方式很簡單,我們捨棄 error_message_of 的作法
採用國內常使用的error message 跟在 form input field 後面的作法
那麼就很簡單啦

model validate 如果出現 error
Rails 在 Model Variable 當中會增加一個 errors hash
如果今天 User 裡面的 email 欄位有 validate error
而我們又用 @user 來代表這個 Model variable
@user.errors['email'] 就會有值,而那個值就是 validate_...._of 裡面的 message 變數
(像上面的例子用 error_message_for,他會出現 Email can't be blank
@user.errors['email'] 就是 'can't be blank' )

那麼我們可以這樣寫
<p><label for="user_email">Emaillabel><br/>
<%= text_field 'user', 'email' %><%= @user.errors['email'] %>p>

<p><label for="user_email">Sexlabel><br/>
<%= text_field 'user_data', 'sex' %>
<%=
@user.user_data.errors['sex'] if @user.user_data %>p>
我們發現 error message 就會跟在錯誤的欄位旁邊
更棒的是,錯誤會將欄位包起來的紅色框框依舊存在

更好用的是,這個方式他不會 show 出 database 的 column 英文名字
只會 show 出 message 變數
所以我們可以更大膽的使用
  validates_presence_of :sex , :message => '請加入性別'
如此就會出現


擷圖_1


Viva ,我們解決了這個問題,還能順便中文化耶~~~

ps.

errors message 也可以這樣玩

9/08/2006

Rails 優缺點比較

又是一篇比較文

Ruby on Rails, and Ruby as a Language

優點

  1. MVC framework幫助 Ruby 使用者區分 presentation 還有 business logic
  2. Unit tests 直接 Build in
  3. DRY(Don’t repeat yourself) 原則讓開發較為容易
  4. 快速敏捷,而且不需要compile time的開發
  5. Convention over configuration 還有 meta-programming 讓 XML Configuration 絕跡,而這些都是 JAVA Platform 像是 Stuts 所必須的工作
  6. Active Record orm( Object Relational Mapper )不需要configuaration(如果不算 connection 的 config)
  7. 完整的Ajax Support( Prototype and Scriptaculous )
  8. Built in XML Web Services
  9. Ruby 實在是太簡單了,每個東西都是 Object ,而且code簡單好讀read.
  10. Easy model validations
  11. 當你 database schema 確定了,開發程式會速度非常快
  12. Session 資料會以存在硬碟或是資料庫的方式處理,所以 Session 資料將很容易被很多台 Server 存取
  13. Ruby是dynamically typed 語言,這讓開發變得很容易。
  14. Ruby 是 Open Class ,所以你可以在 Run Time 很容易加入任何新的Methods。這讓Active Record 可以動態的在Run Time產生每個 Database Entry。也因此,Ruby on Rails不需要更新 Database mapping Schema。
  15. Ruby 支援 Closures ,這讓 Ruby 可以使用較少的code就達成許多 JAVA 很辛苦才能達成的事情。
  16. Ruby on Rails將開發者良好的觀念帶入框架之中,他保護開發者免於一些很平常的錯誤。而這個架構也對於新的架構提供最好的實做方式。
  17. Mongrel Web Server 給予 Rails 開發者專屬於自己的Container,並且速度很快。
  18. Ruby 其實在 1993年就開始開發了。所以他不僅限於Web 開發,他有很多方面的應用。
缺點

  1. 對於已經存在的 Database Schema 要支援是比較弱的。雖然你總是可以在 Model 加入一些 mapping 來改進,但是你在同時也會失去一點點 ActiveRecord的便利性。最重要的是,他們沒有想過要改進現存database schema 的支援度。
  2. API 文件相當嚴重的缺乏。
  3. 如果你無法依照他給你的指示方式去做,那你就必須自己手動去寫。
  4. 如果當你不依照 Rails 的指定的方式去做,Scaffold 的 generator 產生的東西就沒太多意義。
  5. 要讓程式擁有很高的 Scale 是很花記憶體的,雖然現在 memory 很便宜。
  6. 沒有 build in 國際化的支援。
  7. SQL Server 的支援較弱(PHP也是如此的情況)
  8. Rails 開發時間較為年輕(跟PHP或是JAVA相比),所以他的開發者較少,不過最近成長數量相當驚人
  9. IDE數量不足,Textmate是最好的,但是他是 MAC only 的。


Sun 僱用了 JRuby 核心開發者

JRuby 來得訊息,應該是 Official 的訊息了。Charles Nutter and Thomas Enebo 這兩位 JRuby 的核心成員被 Sun 所僱用了。簡單介紹一下 JRuby ,JRuby 就是可以在 JVM 上面 跑 Ruby 的東西。

我條列幾個我認為做重要的消息
  1. Charles Nutter and Thomas Enebo他們倆加入Sun ,主要工作是將 JRuby 推到 1.0 ,並且是 Full Time 開發 JRuby ( Excellent !!!)
  2. Sun 並不擁有 JRuby ( 政治正確的決定)
  3. Sun 對 JRuby 擁有一些期望
  • 他希望獲得一些 Dynamic Language 的經驗
  • 他希望 Ruby 能夠讓 JAVA 使用者使用(知道厲害了吧)
  • 他們希望 JAVA Platform 上面能夠有像是 Ruby 這樣的選擇可以使用
最後他有提到 Sun 並不認為 Ruby 已經在 Dynamic Language 大獲全勝。他們認為 PHP ,Python 還是很強的對手,並且他們有在努力實做 VB 跟 JAVAScript 的 JAVA 版本( JAVAScript 真的要變成 JAVA 的 Script XD )

根據 Beyound JAVA 的說法,他相當推崇 JVM 的設計,並且認為 JVM 已經佔有率相當可怕。JAVA 可能會被取代,但是 JVM 很難被取代(太多Embeded System Support ),Beyound JAVA 並且認為 JRuby 是 Ruby 邁向主流語言的一個很好的切入點。

9/06/2006

簡單的 Ruby HTTP GET 程式

今天寫了一個判斷某組 cgi 是否活著的程式
不管活著還是掛掉都 log 起來的程式
裡面有簡單的 HTTP GET 操作


#! /usr/bin/ruby

require 'net/http'

url
= URI.parse('http://cgi.abc.com/search?q=ipod')
req
= Net::HTTP::Get.new(url.path)
res
= Net::HTTP.start(url.host, url.port) {|http| http.request(req) }

case res.class.to_s
when
'Net::HTTPOK'
log res
when
'Net::HTTPServiceUnavailable'
trigger event
log res
when
'Net::HTTPMethodNotAllowed'
trigger event2
log res
else

trigger event3
log res
end



9/05/2006

Ruby,Python,Perl 中心思想的不同

Ruby,Python,Perl的中心思想都有不同
今天不比較他們的好壞,轉而討論中心思想的差異

Ruby


Ruby 的作者在接受訪問的時候提到
Often people, especially computer engineers, focus on the machines. They think, "By doing this, the machine will run faster. By doing this, the machine will run more effectively. By doing this, the machine will something something something." They are focusing on machines. But in fact we need to focus on humans, on how humans care about doing programming or operating the application of the machines. We are the masters. They are the slaves.

代表的意思是人們寫程式時不應該選擇了解機器做啥事情,以及怎麼做機器比較有效率
而應該考慮到 "人" 怎麼去想
人是比機器更高級的東西,應該要去思考該思考的東西
而不是將時間花在記憶體配置等等的問題上面
並且 Ruby 採用 the principle of least surprise (POLS),代表我們應該用機器應該照人們的意思去做,而不是機器常常給人 Surprise !!!

People are the masters. Machine are the slaves.

這就是Ruby最主要的中心思想,一切以人為本,方便人們思考為最重要的方向。
有些人形容讀 Ruby 就跟讀詩一樣,有些人覺得Ruby的文法很像英文
這些都是因為這個中心思想的原因。
Duck Typing 跟 Parallel Assignment 都是在此思想下的產物。
但是 Ruby 很重視 Perl 的 There is more than one way to do it. 的觀念
(因為人本來就是喜歡多變化的,這也是符合以人為本的中心思想)
所以他也提供許多不同的文法選擇。

Python

Python的中心思想,在由Tim Peters寫的python格言(稱為The Zen of Python)裡面寫為為:
There should be one-- and preferably only one --obvious way to do it.

它的作者有意的設計限制性很強的語法,使得不好的coding習慣(例如if語句的下一行不向右縮進)都不能通過compile。這樣有意的強制programmer養成良好的coding習慣。Python也因此被稱為是一門清晰的語言。因為它的作者在設計它的時候,中心思想是,對於一個特定的問題,只要有一種最好的方法來解決就好了。

另外Python在其他部分的設計上也堅持了清晰劃一的風格,這使得Python稱為一門易讀性、易維護性好,並且被大量用戶所歡迎的、用途廣泛的語言。

Perl

一個Perl程式設計師常常想起的Perl俗語是

Easy things should be easy, and hard things should be possible
代表著Perl 可以比其他語言更容易做到簡單的事情(因為有一堆符號@@!)

Perl語言的中心思想更可以集成為一句話

There's More Than One Way To Do It

代表著今天要做同一件事情,可以有很多種寫法。

同一行程式,Perl 通常都有兩種以上的寫法,一種是比較 dirty 但是比較迅速的方式,另一種是比較清晰好整理但是確比較慢的方式。通常在 Perl 當中,實現相同功能的程序代碼長度可以相差十倍。通常保持良好閱讀的coding習慣是很重要的,但是 Perl Programmer 通常為符合 Easy things should be easy的觀念,大家都選用 Easy way XD。

最能夠形容 Perl 的就是 駭客與畫家 裡面提到的拋棄式程式,那是一種Deadline將近,火燒眉頭時,利用 Perl 這種快速而方便的語言趕快做完事情,然後只用一次就忘了他。也正是因為Perl的靈活性和「過度」的簡化語法,也因此獲得了write-only的「美譽」,因為許多Perl程序的代碼令人難以閱讀,三個月過去了,自己也看不懂,只好重新寫一個XD


結論


我本身不太相信這些語言一開始開發的時候,就一定已經有這些中心思想。但是我相信這些語言的主導群組應該都是意氣相投的人,開發語言時,他們都是用心良苦的向自己心目中最好的方向去發展。到最後的結果大家也看到了,Programmer 在Coding時,會有意無意的被這些語言的中心思想所引導,產生了風格類似的 Code 。

至於我喜歡的思想呢?我是 Perl 出身的,很喜歡 Perl 的想法,但是我卻對 Perl 的 write-only 感到很麻煩。後來投身 Ruby ,對Ruby的以人為本想法感到相當的疑惑,真的有那麼神奇嗎?後來漸漸的發覺,我寫出來的code擁有簡單的結構,但是產生的效率跟可讀性非常的棒。並且 Ruby API 可以用猜的就猜出他的規律,不像 JAVA 還要 Documention 放在旁邊 =_= 。漸漸的,我好像稍微可以掌握 The Ruby Way ... 至於 Python ,我想喜歡 Perl 的人都很難去喜歡 Python 的感覺吧(這兩個中心思想是相對的)。