8/15/2007

Fedora Core 6 上安裝 beast (2)

不喜歡 apache 嗎?apache 沒辦法用 mod_proxy_balancer 嗎?沒關係,我找到另外一個不錯的方式了,是 pound + lighttpd. 如果不想用 apache 的話,上一篇的 httpd 相關的東西都不要灌,改灌:

sudo yum install Pound
sudo yum install lighttpd

我是覺得這邊分大小寫很沒意義,不過 yum 就是這樣﹍。

pound 是什麼?
The Pound program is a reverse proxy, load balancer and HTTPS front-end for
Web server(s). Pound was developed to enable distributing the load among
several Web-servers and to allow for a convenient SSL wrapper for those
Web servers that do not offer it natively.

lighttpd 我想大家都知道,一個非常快速的 http server, 只不過他的 mod_proxy 似乎有很多問題,以致於沒辦法用他。所以這部份用 pound 當前端解決這個問題,流程變成:

http request => pound => lighttpd or mongrel cluster

安裝好後,先來設定 pound, 不過 https 就先暫時略過不管。
sudo nano /etc/pound.cfg
=========================
User "pound"
Group "pound"

ListenHTTP
    Address 0.0.0.0
    Port 80
End

Service
    URL "/(images|stylesheets|javascripts)/"
    BackEnd
        Address 127.0.0.1
        Port    1999
    End
    Session
        Type    BASIC
        TTL     300
    End
End

Service
    BackEnd
        Address 127.0.0.1
        Port    2000
    End
    BackEnd
        Address 127.0.0.1
        Port    2001
    End
    BackEnd
        Address 127.0.0.1
        Port    2002
    End
    Session
        Type    BASIC
        TTL     300
    End
End
=========================
前面的設定是把 host/images, host/stylesheets, host/javascripts 這三個 url 下的 request 都丟給 port 1999 處理,也就是等會 lighttpd 要使用的 port. 這邊使用 regex, 可以自由設定自己要的規則。假使未來 user 要使用 file_column, 可以再加個 user/avatar 之類的。

下面的則是全部丟給 mongrel_cluster 處理。設定上應該比 apache 簡單地多。可以先測試看看,先把 lighttpd 的部份 comment 起來,啟動 pound 試試:
sudo pound

照理說這樣就能使用了。不行的話加個 config 檔路徑給他:
sudo pound -f /etc/pound.cfg

這邊我是在想要不要把 config 也放到 beast 裡面,不過 pound 應該只會有一個,所以我想還是放到 /etc 下好了。停止 pound 的方式:
sudo kill `sudo cat /var/run/pound.pid`

製作 lighttpd 設定檔:
nano config/lighttpd.conf
==========================
server.modules = (
"mod_access",
"mod_accesslog"
)

server.port = 1999
server.bind = "127.0.0.1"
server.document-root = "/home/YOUR_NAME/projects/beast/stable-1.0/public"

server.username = "YOUR_NAME"
server.groupname = "YOUR_NAME"
server.pid-file = "/home/YOUR_NAME/projects/beast/stable-1.0/tmp/pids/lighttpd.pid"
server.errorlog = "/home/YOUR_NAME/projects/beast/stable-1.0/log/lighttpd_error.log"
index-file.names = ( "index.html", "default.html" )
accesslog.filename = "/home/YOUR_NAME/projects/beast/stable-1.0/log/lighttpd_access.log"

# mimetype mapping
mimetype.assign = (
".rpm" => "application/x-rpm",
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "application/ogg",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".cpp" => "text/plain",
".log" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv",
".bz2" => "application/x-bzip",
".tbz" => "application/x-bzip-compressed-tar",
".tar.bz2" => "application/x-bzip-compressed-tar"
)
==========================
我很想省略 mime-type 設定,不過不設的話 firefox 好像會有問題。safari 和 opera 是 ok 的,IE 我沒得測就沒測了。反正要設就是了,有沒有問題不影響。只是希望 lighttpd 能內建這些設定﹍。

啟動 lighttpd
sudo lighttpd -f config/lighttpd.conf

這樣就算大功告成了!剛剛有測試 pound 的話記得改回原本設定並重新啟動。比較詳細的介紹與說明可以參考這篇:
Configuring pound with lighttpd and mongrel.

*

summary:

用到的 server:

(1)
apache2.2 =>
reverse proxy + load balancer + serve static files

(2)
pound => reverse proxy + load balancer
lighttpd => serve static files

(3)
mongrel_cluster => setup mongrel cluster
mongrel => serve beast

結果就是 ((1) or (2)) + (3)

操作 apache:
啟動:sudo apachectl start
停止:sudo apachectl stop
重啟:sudo apachectl restart

操作 pound:
啟動:sudo pound -f /etc/pound.cfg
 或:sudo /etc/init.d/pound start

停止:sudo kill `sudo cat /var/run/pound.pid`
 或:sudo /etc/init.d/pound stop

重啟:sudo /etc/init.d/pound restart

操作 lighttpd:
啟動:sudo lighttpd -f config/lighttpd.conf
 或:sudo /etc/init.d/lighttpd start

停止:sudo kill `cat tmp/pids/lighttpd.pid`
 或:sudo /etc/init.d/lighttpd stop

重啟:sudo /etc/init.d/lighttpd restart

操作 mongrel_cluster:
啟動:sudo mongrel_rails cluster::start
停止:sudo mongrel_rails cluster::stop
重啟:sudo mongrel_rails cluster::restart

操作 postgresql:
啟動:sudo pg_ctl start
停止:sudo pg_ctl stop
重啟:sudo pg_ctl restart

8/14/2007

Fedora Core 6 上安裝 beast

我選擇 apache2.2 + mongrel_cluster + postgresql

安裝 apache 和其他的東西。
sudo yum install httpd
sudo yum install httpd-devel
sudo yum install openssl
sudo yum install readline

安裝 ruby
sudo yum install ruby
sudo yum install ruby-devel
sudo yum install ruby-libs
sudo yum install rdoc
sudo yum install irb

安裝 postgresql
sudo yum install postgresql
sudo yum install postgresql-devel
sudo yum install postgresql-libs
sudo yum install postgresql-server

初始化 postgres
sudo mkdir -p /var/db/postgresql81/defaultdb
sudo chown postgres:postgres /var/db/postgresql81/defaultdb
sudo -u postgres initdb -D /var/db/postgresql81/defaultdb
sudo -u postgres pg_ctl -D /var/db/postgresql81/defaultdb \
-l /var/db/postgresql81/defaultdb/postgres.log start

拿來抓 beast... 捨 svn 取 svk 因為我討厭一堆 .svn
sudo yum install perl-SVK

sudo yum install rubygems

至少要用這些 gem
sudo gem install rails -y
sudo gem install mongrel_cluster -y
sudo gem install redcloth -y
sudo gem install ruby-openid -y
sudo gem install ruby-postgres -y

建立目錄。
mkdir -p ~/projects/beast
cd ~/projects/beast

用 svk 抓不會有一堆 .svn 礙眼。
svk mirror //mirror/beast \
http://svn.techno-weenie.net/projects/beast/branches/stable-1.0/
svk sync -s HEAD //mirror/beast
cd stable-1.0

產生讓 beast 用的資料庫。
sudo -u postgres createdb beast_prod

設定資料庫設定檔。
cp config/database.example.yml config/database.yml

nano config/database.yml
=========================
production:
  database: beast_prod
  adapter: postgresql
  host: localhost
  username: postgres
  password:
=========================

初始化 beast 資料庫。
rake db:schema:load RAILS_ENV=production

非常囉唆的 cluster 設定,user 可以開 mongrel,
不過因為我懶所以用自己的帳號跑。
sudo mongrel_rails cluster::configure -e production \
-p 2000 -N 3 -c /home/YOUR_NAME/projects/beast/stable-1.0 \
-a 127.0.0.1 --user YOUR_NAME --group YOUR_NAME

啟動 mongrel_cluster 吧。
sudo mongrel_rails cluster::start

接著是 apache2.2 的 mod_proxy_balancer 的設定,
如果 mod_proxy_balancer 沒有啟動記得把他打開。

建立 balancer 導向 port 2000~2002
sudo nano /etc/httpd/conf/httpd.conf
=====================================
<Proxy balancer://YOUR_BALANCER>
  BalancerMember http://YOUR_ADDRESS:2000
  BalancerMember http://YOUR_ADDRESS:2001
  BalancerMember http://YOUR_ADDRESS:2002
</Proxy>

Listen 80
<VirtualHost *:80>
  ServerAdmin YOUR_EMAIL
  ServerName YOUR_ADDRESS
  ProxyPass / balancer://YOUR_BALANCER/
  ProxyPassReverse / balancer://YOUR_BALANCER/
  ProxyPreserveHost on
  ErrorLog /home/YOUR_NAME/projects/beast/stable-1.0/log/apache_error.log
  CustomLog /home/YOUR_NAME/projects/beast/stable-1.0/log/apache_access.log combined

  ProxyPass /images !
  ProxyPass /stylesheets !
  ProxyPass /javascripts !
  Alias /images /home/YOUR_NAME/projects/beast/stable-1.0/public/images
  Alias /stylesheets /home/YOUR_NAME/projects/beast/stable-1.0/public/stylesheets
  Alias /javascripts /home/YOUR_NAME/projects/beast/stable-1.0/public/javascripts
</VirtualHost>
=====================================

接著啟動 apache 就 ok 了。
sudo apachectl start

最後來設定 beast 的 smtp, 如果想用 gmail 寄信的話,
我之前試過兩個方式,一個是使用 msmtp,
另一個是有人有寫一小段 ssl smtp 的 patch, 可以參考這裡:
http://www.stephenchu.com/2006/06/how-to-use-gmail-smtp-server-to-send.html

nano config/environment.rb
加入:
===========================
ActionMailer::Base.smtp_settings = {
  :address => "smtp.gmail.com",
  :port => 587,
  :domain => 'YOUR_DOMAIN',
  :user_name => "YOUR_GMAIL_ACCOUNT",
  :password => 'YOUR_GMAIL_PASSWORD',
  :authentication => :plain
}
===========================

nano app/models/user_mailer.rb
===============================
@from = 'YOUR_GMAIL_ACCOUNT@gmail.com'
===============================
我不是很確定這一步要不要做,我猜不做會被 gamil 擋掉。

接下來把那段程式碼 copy 到可以被 require 的地方,我是放在 lib 下:
nano lib/smtp_tls.rb
接著把他 require 進去:
nano config/environment.rb
===========================
require 'smtp_tls'
===========================

sudo mongrel_rails cluster::restart
註冊一下試試寄信吧!如果失敗的話,那篇連結裡有個 workaround:

nano lib/smtp_tls.rb
=====================
rescue EOFError
  ;
rescue OpenSSL::SSL::SSLError => e
  puts('Unexpected Exception: ' + e.message + ' : ' +
    e.backtrace.join("\n") )
end
=====================

我之前用是沒有這個問題,但在這台 fedora 上卻有這個問題,
也許是要更新 openssl 的版本?不管如何,加入上面那段暫時可用。

結論:
還是 macports 好用,速度快,更新快,只是 build 要很久就是了。

8/13/2007

ludy 0.0.4 released

花了一點時間再測試了一下 curry 的實做,i am glad to announce that ludy 0.0.4 is released. 原本的 Proc#curry 被改名為 Proc#__curry__, 我很想把他
deprecate 掉,但是後來的 Kernel#curry 也有運用到該實做,是有在考慮把他從 public 改到 private, 但考慮到也許還有用處,還是暫時放在 public 下,以後如果發現真的沒用了,會改到 private 下。

現在的用法是:

multiply = lambda{|l,r| l*r}

double = multiply.curry[2]
assert_equal 8, double[4]
assert_equal 6, double[3]

xd = multiply['XD', 5]
assert_equal 'XDXDXDXDXD', xd

assert_equal 29, :+.to_proc.curry[18][11]
assert_equal (0..4).to_a, lambda{|a,b,c,d,e|[a,b,c,d,e]}.curry[0][1][2][3][4]

只要呼叫到了 Kernel#curry, 且 caller 本身回應(respond_to?):call 和 :[], 則回傳一個 curry function, 這樣就可以有更強的 consistency, 不需要注意什麼時候使用 () 而什麼時候使用 [], 請一律使用 function call/[], 不用擔心參數是否足夠,足夠時就會回傳真正的答案,否則再度傳回 curry function.

所以其實我是在想,以下兩者是否相同?
class Array; include Curry; end
func1 = [].cfoldr
func2 = [].method(:foldr).curry
func1 == func2 # => true? or false?

我的希望是相同,當然。只是我直接換上這樣的實做似乎有點問題,這個狀況可能在 0.0.5 中解決,使 curry module 和 kernel#curry 也能夠擁有該有的一致性。

另一個棘手問題是 ruby 的 block, 乍看很好用,實際上也是,但是卻造成了很大的不一致。這一點也真的是很難搞定,之前的 this 就有碰上這樣的大問題,使用 yield 似乎無法產生正確的 call stack.

*

最後則是 0.0.4 上的實做問題,由於 :*.to_proc 的這個 proc 無法預測其正確的 arity, 就像 :message_that_you_never_know.to_proc 也不可能能知道他的 arity 是多少一樣,這造成了難以判斷何時該回傳正確的值而非另一個 curry function. 我是想要從 Symbol#to_proc 去竄改,不過這會碰上另外兩個問題:

1. 會跟其他人的實做衝突
2. caller 和 arity 是合併在一起的,意味還沒 call 之前都不會知道 arity, 這樣我就沒辦法強迫 Symbol#to_proc 能產生正確的 arity.

所以我只好用另外一個很愚蠢的方式:trial & error.
begin # let's try if arguments are ready
  self.__send__ :orig_call, *args, &block
rescue ArgumentError # oops, let's curry it
  method(:call).to_proc.__send__ :__curry__, *args
end

效率問題就別提了,我真的覺得這樣很蠢,可是好像也想不到更好的方式。

所以 0.0.4 就只暫時強化了 curry 的實做,離完善還有很大的一段距離。雖然心血來潮度極高,但好像還算有進展?

gem install ludy
to see detail.

8/07/2007

ludy 0.0.3 released

睽違已久,忽然心血來潮多加了幾個東西。
in CHANGES:
==============================
ludy 0.0.3, 2007.08.07

1. ludy_ext:
added:
1. Proc#curry
2. Proc#compose
3. Proc#chain
4. Symbol#to_proc
5. Array#foldl
6. Array#foldr
7. Array#filter

removed:
1. Fixnum#collect # see tc_ludy_ext.rb#test_fixnum_collect for reason

info:
1. ruby2ruby has NilClass#method_missing return nil,
so i can't just make it return blackhole

2. module Curry:
see test/tc_curry.rb for usage

see unit test for usage
==============================
雖然說請看 unit test 來揣摩用法,不過這樣真的有點無趣,所以還是來稍微介紹一下。這次之所以忽然心血來潮想做,是因為看到James Edward Gray II 的 higher-order ruby 專欄:
http://blog.grayproductions.net/articles/category/higher-order-ruby
第六篇的:Currying, 他的 curry 實做:
class Proc
def curry(&args_munger)
lambda { |*args| call(*args_munger[args]) }
end
end

老實講,不是說看不懂,可是我不明白為什麼要寫得那麼複雜,乍看之下實在看不太出來。丟掉他的實做,我試著做了一個:
class Proc
def curry *pre
lambda{ |*post| self[*(pre + post)] }
end
end

就我自己測試起來,效果是一樣的,我認為應該簡潔易懂多了。可是這根本不太像 curried function 吧?內心吶喊著。不過在看我後來寫的 curry module 之前,先來簡單介紹一下 currying.

在 lambda caculus 中,每個 function 都只能有一個 argument, 這是因為 lambda caculus 是一種極簡的語言,用來研究某些模型的語言。但是如果 function 只能吃一個 argument, 有很多事是會做不到的。於是我們可以靠著 tuple 把許多的 argument 包成一個 argument, 例如在 Haskell 中,tuple 就是 (1,2,3), 用括號括起來的就是 tuple. 所以上面的 (1,2,3) 是一個有三個值的 tuple, 可以把他視為一個值。

比方說有個 function 叫 power, 像是:power 2, 10 會回傳 1024. uncurried function 就會是 power (2, 10), 他吃一個有兩個元素的 tuple, 吐出一個 1024 的值。可是如果是這樣使用的話,其實是很不方便的。有一個方法可以讓 function 依然只吃一個 argument, 但是又不需要使用 tuple, 可以一個值一個值傳入,那就是 curried function.

在 functional programming 中,function 的地位極高,不管在做什麼事,幾乎都是在操作 function. 這也就是所謂的 higher-order function, 操作 function 的 function, 或是產生 function 的 function 諸如此類。curried function 的效果就是當所吃入的 argument 不足時,他會再吐出另一個 function 去吃其他 argument,直到 argument 足夠時才會吐出結果。

power 2 的回傳會是一個 function, 他記住了 2, 當他再吃一個 argument 後,則會再把 2 拿出來跟 argument 做運算。所以 power 的 type 會是:
power :: Int -> Int -> Int
結合順序是從右邊開始,所以是吃一個 Int, 吐出 (Int -> Int), 也就是吃一個 Int 吐出一個 Int 的 function. 可以想成這樣:

power2 = power 2
result = power2 10
result # => 1024

也就是說,可以把他看成是一個會不斷記憶 argument 的 function. 看看 James Edward Gray II 的範例:
multiply = lambda { |l, r| l * r }
double = multiply.curry { |args| args + [2] }
triple = multiply.curry { |args| args << 3 }

multiply[5, 2] # => 10
double[5] # => 10
triple[5] # => 15
triple["Howdy "] # => "Howdy Howdy Howdy "

所以他的實做其實很簡單,就是用一個 array 記憶 arguments,
最後再 prepend 到最後的 arguments 裡。
class Proc
def curry(&args_munger)
lambda { |*args| call(*args_munger[args]) }
end
end

不過我覺得不用寫得那麼複雜,所以改寫為:
class Proc
def curry *pre
lambda{ |*post| self[*(pre + post)] }
end
end

這裡利用了 ruby 的 lambda 有 closure 的特性,把 *pre 紀錄下來,再把他 prepend 到 post 上,最後再呼叫原本的自己(self)。

可是這樣不完整,因為你必須明確表達你需要做 curry, 而 Haskell 的 curried function 是可以讓你忽略這件事的。

lambda{|a,b,c,d,e|}.curry(1).curry(2).curry(3).curry(4).carry(5)

這樣不煩死才怪。我希望能用:

lambda{|a,b,c,d,e|}[1][2][3][4][5]
也能使用:
lambda{|a,b,c,d,e|}[1,2][3][4,5]

可惜我暫時還沒找到好做法 XD 目前暫時僅提供可以 mixin 的 module, 大概是這樣用:

class Array; include Curry; end

接著 array 所有以字母開頭的 method 會多個 curried 版,prefix c. i.e., map => cmap; foldr => cfoldr

func1 = [1,2,3].cfoldr[:-.to_proc]
assert_equal 2, func1[0]

做法其實很簡單,就只是檢查參數夠了沒,不夠就重新 curry 一份,夠了就呼叫原始 method. 我原本一直不希望前綴 c, 而以相同名字命名之,然後原本的名字改為:orig_method. 可惜不管怎麼試都失敗,原因不是很清楚,但這種 side-effect 超大的動作,會失敗其實也不怎麼奇怪吧,我想。雖然我總覺得以正常呼叫法而言,應該是沒什麼差才對,也許我有哪裡寫錯了,只是還沒發現而已。

*

至於其他新增的東西,這裡也稍微介紹一下。首先 Array#filter 只是 select 的 alias, Array#foldl 也只是 inject 的 wrapper. Array#foldr 稍微複雜些,不過概念上只是類似反過來的 inject 而已。Symbol#to_proc 大家應該都很熟,連 active_support 裡面也有。只是單純的 message/method 轉換而已。

比較需要提的應該是 Proc#compose 和 Proc#chain. 前者就是數學上的 compose, facets 裡其實也有,不過手癢還是自己做了一份。他有點類似反向的 inject, 很容易做出來。至於 chain, 這是模仿 C++ 的 loki 中的 functor 中的 chain. 效果很單純,就是把 function 串起來而已。這拿來做 callback 應該還算方便,例如:

button.on_click = menu.method(:popup).chain button.method(:hide)
接著當按鈕被按下去後,選單就會彈出,且按鈕自動隱藏。
至於 arguments 和 returns 呢?arguments 會統一給所有人。
f1.chain(f2)['XD']
這樣 f1 和 f2 都會接到 'XD' 這個 argument. return 則會蒐集成一個 array 並 flatten 回去。
[f1 的結果, f2 的結果, f3 的結果,...]
如果 f3 的結果是 array, 則會依序儲存:
[..., f3 的結果1, f3 的結果2, f4 的結果, ...]

這樣做的原因是要讓 chain 還能繼續 chain 而不會出現非常恐怖的 nested array.
f1.chain(f2).chain(f3).chain(f4)
但是其實可以這樣 chain:
f1.chain(f2, f3, f4)
結果和上面的會是相同的。

在 chain 之間的 travel 還沒做,下次有機會時會做。

gem install ludy # to see detail

ruby 寫起來真的很簡潔,很多功能 10 行內都能解決。

2007.08.07

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

7/25/2007

[心得] RubyCocoa

轉自 ptt MacDev 板

作者 godfat (godfat 真常) 看板 MacDev
標題 [心得] RubyCocoa
時間 Tue Jul 24 21:40:20 2007
──────────────────

嫌 objective-c 太難用嗎?還在等 obj-c 2.0 嗎?先試試 RubyCocoa 吧 XD
http://rubycocoa.sourceforge.net/

[quote]
It lets you write full-stack Cocoa applications in Ruby. It is also possible to write a Cocoa application that mixes Ruby and Objective-C code.
[/quote]

how to install?
有灌 macports 的話:(當然,xcode 是一定要灌的)
sudo port install ruby
sudo port install rb-rubygems(非必要,但建議)
sudo port install rb-cocoa

文件放在:
/Developer/Documentation/RubyCocoa

範例放在:(相當豐富)
/Developer/Examples/RubyCocoa

我這裡有一個簡化的 hello world, 可以小瞥一下:

require 'osx/cocoa'
include OSX

app = NSApplication.sharedApplication

hello = NSWindow.alloc.initWithContentRect_styleMask_backing_defer [200.0, 300.0, 100.0, 100.0], 15, 2, 0
hello.setTitle 'Hello'

btn = NSButton.alloc.initWithFrame [10.0, 10.0, 80.0, 80.0]
hello.contentView.addSubview btn
btn.setTitle 'Hello World!'
btn.setAction 'stop:'
btn.setBezelStyle 4

hello.display
hello.orderFrontRegardless

app.run

另外可以順便請教一下,如何讓視窗在按下 X 時關閉而不是隱藏嗎?我沒寫過 obj-c(XD), 昨天文件翻來翻去找不到要怎麼做



另一方面,如果只是要做 GUI 而不需要 call mac 的 api 的話,個人強力推薦 QtRuby

sudo port install qt4-mac(這個會 build 很久)
sudo port install cmake

QtRuby 沒有這麼方便的安裝法,要到
http://rubyforge.org/projects/korundum/
抓 qt4-qtruby, 然後進到目錄下:

cmake .
make
sudo make install

於是就可以正常使用 QtRuby 了,hello world 如下:

require 'qt'
include Qt
app = Application.new ARGV
hello = PushButton.new "Hello World!"
hello.resize 100, 30
hello.show
app.exec



Qt 是什麼?
http://trolltech.com/products/qt
是我用過最棒的 GUI toolkit, 使用 C++ 語言。可用 GPL 授權或用商業授權(很貴),二擇一(這是 Qt 唯一被批評的點)

Ruby 是什麼?
http://ruby-lang.org/
是我用過最方便的 programming language, 使用 Ruby license(很自由)歡迎來 ptt Ruby 板參觀 :p



Mac OS 10.5 將搭載 Ruby 1.8.6 + Rails 1.2.3, 想試試 Rails 寫網站嗎?
sudo gem install rails
不需要等 10.5 就可以輕鬆試驗了 XD
http://rubyonrails.org/

==

發完神經了,有傷眼請見諒 XD

--
#!/usr/bin/ruby          [露比] /Programming (Kn|N)ight/            看板《Ruby》
# if a dog nailed extra legs that http://www.ptt.cc/bbs/Ruby/index.html
# walks like an octopus, and Welcome ~Ruby@ptt~
# talks like an octopus, then ◢█◣ http://www.ruby-lang.org/
# we are happy to treat it as █ http://www.ruby-doc.org/
# if it were an octopus. ◥ ◤ http://www.rubyforge.org/
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 220.135.28.18

7/22/2007

Ruby GUI

[begin edited2]
夠了,不要再修改了,後續請看 telnet://ptt.cc 的 Ruby 板或是:
http://godfat.tw/viewtopic.php?p=1779#1779
(我電腦關機或沒使用網路時連不上)
[end edited2]

[begin edited]

作者 godfat (godfat 真常) 看板 Ruby
標題 Re: [心得] GUI
時間 Sun Jul 22 18:13:32 2007
───────────────────────────────────────

說是這樣說,不過真正開始測試的時候,我發覺﹍

1. wxRuby
實在有點難用﹍有一堆不該有的參數要傳來傳去﹍放棄

2. FXRuby
mac 上其實是可以用啦﹍只是需要 X11, 不能用 Aqua
結果就是﹍對 user 來說很難用,放棄

3. QtRuby
結果還是得回到 QtRuby...
cmake .
make
sudo make install

測試了一下,正常可用 Qt4
不過市面上的文件好像以 Qt3 為多就是了

嘖,沒有 port install qtruby 真的很可惜
這樣以後要升級或是反安裝就比較麻煩了
不知道當初安裝的 source 是不是要留下來以便反安裝作業

--
--
※ 發信站: 批踢踢實業坊(ptt.cc)

[end edited]

關於 computer science 這個領域,除了程式語言(programming language)與物件導向(object-oriented)外,再下一個興趣大概就是使用者圖形介面了吧(GUI). 所以我一直想找個自己喜歡的 toolkit 來用,在 C++ 中無非就是 Qt4 了,不過在 Ruby 中,似乎沒有一個比較完美的 solution, 一個個來看。

在這之前,我先說明一下我用過的 C/C++/Java GUI toolkit.

1. MFC, 學校在教如何剪貼 MFC code, 我是叛逆份子,所以一個字都沒聽進去。不用別人說很糟,光看第一眼就覺得很糟,更何況他又是被罵得半死的東西。

2. wxWidgets, 這算是我第一個使用的 GUI toolkit, 基本上,我認為這真的是個很強大的東西,沒話說。不過最令人嘖嘖稱奇的應該是他跨平台的能力,如果我沒記錯的話,他跨的平台好像比 Java 還多!不過因為風格問題,我不是很喜歡。基本上,我認為 wxWidgets 的風格有些過於老舊,寫起來感覺不是很好。Ruby binding 叫 wxRuby.

3. Qt, 我想正是因為 wxWidgets 跟我不太合,所以才繼續找下去。不料找到 Qt4, 我認為這應該是接近完美的 solution 了。有人說,Qt 至少領先其他人十年,也許這麼講並不為過。最令人詬病的,大概就是你只能從 GPL 或超昂貴的商業 solution 兩種 license 中選一種。其實我不覺得這對我來說會是很大的困擾,open source 沒什麼不好,雖然 GPL 是有點太激進了些。Ruby binding 叫 QtRuby.

4. GTK+, 這個其實我不只沒用過,甚至連看都沒看過。理由很簡單,就是我不想用 C 想用 C++. 當然我知道 GTK+ 評價不錯,也有 Glade 和 win32 的 port, 只是似乎沒有足夠的推力讓我去嘗嘗看。

5. Swing, 會碰這個完全只是因為需要用 Java. 我個人是覺得他跟 wxWidgets 各有千秋,但同樣遠遜於 Qt. 不過也許這樣比較並不太公平,Java 天生就是沒 C++ 強。所以 Qt 遠勝可能是理所當然的?Ruby 要用可以使用 JRuby.

6. Ultimate++, 其實這個我沒用過,不過看起來是不錯。沒去試主要有三個原因,一,他綁 IDE, 這個我很不爽。二,他的命名法我不能接受。三,Qt 很好。XD 其實你去看他的網站,他有一個範例程式,並用各種 toolkit 來實做。我個人認為這個動作滿不誠懇的,他有故意挑其他人弱點的嫌疑。而且各 toolkit 做到的東西明顯有差異,這樣比並不公平。不誠懇,不喜歡。

7. Notus/VCF, 這兩樣 toolkit 都有一個很大的特色,就是 template template and template... 基本上用 C++ 當然是要用 template 啊!!不過知道這兩樣東西時我已經有一段時間沒碰 GUI 了,所以並沒有仔細調查這兩個 toolkit 感覺如何。但我想如果沒辦法接受 Qt 的話,這兩套 toolkit 值得一看。

8. .NET/mono, 這個其實我只用過 Visual C++ 2005 Express 稍微拉了一下介面,感覺還不錯,但直接寫起程式不知道是什麼感覺。Ruby 要用的話,或許可以試試 IronRuby, 不過我個人對 .NET 沒什麼好感,就不去試了。

9. VCL, Borland 的招牌,我用過 Turbo C++ Explorer, 感覺是﹍基本上我覺得沒有 .NET 好的感覺。而且要跨平台恐怕有點難,所以就不考慮了。更何況這些好像也不算 open source 的吧?

10. FOX, 這個是我最沒聽過的 toolkit, 是去找 Ruby GUI 時才發現他的存在,所以我一點都不知道他的狀況究竟如何。評價似乎是不錯,而且好像很年輕。只是呢﹍FXRuby 似乎沒有 mac 的 port, 而我也已經有點懶得用 C++ 寫 GUI 了,所以還是暫時跳過吧﹍。基本上沒有跨 win32/x11/aqua 三大系統的 toolkit, 我就沒什麼興趣試了。

本來只想隨意講一點的,不知不覺就扯多了。接下來看 Ruby 能使用的東西:

1. tk, 沒記錯的話,這個被 ruby 內建起來了。不過似乎是用過的都說爛,所以還是不要多考慮吧?

2. FXRuby, FOX 的 ruby binding, 評價相當不錯,唯一的缺點也許是還沒有 mac 版的樣子?我想未來應該值得繼續關切,雖然我還沒看過他程式寫起來像什麼樣子。要試用的話,除了需要安裝 FOX, FXRuby 就:gem install fxruby

3. wxRuby, wxWidgets 的 ruby binding, 我是看到有人批評寫起來像在寫 C++, 這應該算是一個滿大的缺點,不過看在他功能強大,跨平台能力驚人的份上,還是值得試試看吧。要試用的話,除了需要安裝 wxWidgets, wxRuby 就:gem install wxruby

4. QtRuby, Qt3 與 Qt4 的 ruby binding, 基本上評價仍然是相當地好,唯一的兩大缺點,第一個是沒有 gem, 不能 gem install qtruby, 第二個是在 windows 上要安裝似乎非常困難,沒有簡單的方法。但我相信假以時日,這兩個問題都能夠解決(嗚嗚)。

5. Swing, 要灌 JRuby, 我的 macports 似乎找不到 jruby, 我又不想從 sun 的網站抓下來灌,再說,我也沒喜歡過 swing 過,所以就懶得試了。

6. Mono, 我有灌 mono, 不過不是很清楚能不能用 IronRuby 之類的東西存取,之所以寫在這,算是做個筆記,說不定可以使用這樣。

7. GTK+, 我不是很確定有沒有 ruby binding, 但我想應該會有才是。不試的理由同上,所以也只是暫時筆記。

Ruby 的 GUI 比較有名的大概就這幾個了吧。其實我是一直想試 QtRuby 的,雖然 windows 上似乎不太容易使用,但相信假以時日一定可以的。而讓我卻步最大的問題點在於,不能用 gem 安裝,macports 也找不到 QtRuby. 也就是說,我得親自 ./configure make make install, 但我不喜歡這樣,管理不便。所以我決定還是暫時放下他,等他有 gem 或是 port 時再說吧!

FXRuby 因為沒用過 FOX, 所以也還是放著再說。更何況據說是沒有 mac 版,那我試屁啊﹍。所以左右思量之下,我還是決定先試 wxRuby! 安裝方便,強大的跨平台能力,雖然是 C++ style 而非 the ruby way, 但對我來說 C++ style 也不是那麼地令人排斥,也許有點詭異,但我覺得也可以用自己想要的方式重新 wrap 一次,對 ruby 來說那是如此簡單的事!

so have a try with wxRuby!

this is the hello world:

require 'rubygems'
require 'wx'

class HelloWorld < Wx::App
include Wx
def on_init
helloframe = Frame.new nil, -1, "Hello World"
StaticText.new helloframe, -1, "Hello World"
helloframe.show
end
end

HelloWorld.new.main_loop


sudo port install wxwidgets
sudo gem install wxruby

閉關百日

即日起,本人即將到關西營區閉關百日,休養生息以及盡男生該盡的義務,大概今年十一月回來。新兵訓練出來後,就是國防役的生活啦。

對了,因為新訓好像可以看點書,所以預計會帶本練功秘笈進去練功
HTTP:The Definitive Guide
前陣子太忙了,都沒時間看書練鍊基本功好像也不錯。

7/11/2007

Passion

昨天我的一個好朋友告訴我「我好像失去了程式設計的熱情了。」我聽著這句話,突然覺得我蠻幸福。

要說我從來沒有這種感嘆是騙人的。上上次擁有這種想法的時候,我那時正被論文跟 PHP 搞的很疲憊,剛好遇到 Ruby on Rails ,那種超乎想像的設計讓我完全掉入這個世界,也重新找回程式真正的熱情。

上次出現這樣的想法的時候,剛好是前陣子案子太忙,事情多到我想殺人。一連串的忙碌讓我忘記了寫程式帶給我的快樂,只是一直感覺到累累累。Ruby on Rails 的東西也是這樣,成長的很快,但是我是不是跟著他一起成長呢?還是一直原地踏步呢?我到底追求的是什麼呢?我得到了什麼呢?

很幸運,這種疲憊感很快的就不見了,因為我接了這個 Case ,29屆瓊斯盃網站
小時候,我看著東方,阿龍,阿三等人在瓊斯盃奮鬥,現在呢?我以官網製作者的身份,坐在場邊看著中華隊在場上奮鬥。不一樣的我,一樣為中華隊加油的心情。我為了這個案子,連續熬夜好幾個晚上,連 Blog 都沒時間寫了,就是為了呈現一個最好的瓊斯盃網站。這麼堅持的理由不是因為專業,只是因為我是一個運動迷,我是一個中華隊的球迷,我要為他們加油。

累是超級累,但是我找回了失去的「Passion」。一個專門屬於我的 Passion。不瞞各位,我 7/23 就要進去軍中新訓三個月,在進去軍中之前可以做出一個這們有意義的東西,真的很開心。

一年前我開了這個 Blog ,也是這樣的熱情不是嗎?

我以身為 Ruby on Rails 使用者自豪,我以身為第29屆瓊斯盃官網製作者自豪。不管你是誰,用什麼語言,什麼 Framework,別忘記你的熱情,千萬要對你的工作感到自豪。

7/08/2007

ruby facets

老實講,ruby 有一個地方真討厭,那就是每次都把我想做的東西做完了。有了 ruby, 我不覺得還有需要再自己弄一個程式語言,做得比我想像中的還好。唯一的缺點大概只有 lib 還不夠多,但有了 facets 的話,那也真的是差不多了。
http://facets.rubyforge.org/
該有的幾乎都有了,差的只剩下一些小東西。如果早點發現這東西,大概就不會想弄 ludy 了吧。

不過想用 facets 有點麻煩,例如我想用 Array#shuffle, 就必須
require 'rubygems'
require 'facets'
require 'array/shuffle'
這麻煩到爆炸了吧!!不過有個好東西:

require 'rubygems'
require 'facets/automatic'

接下來,就放心使用 Array#shuffle 吧!因為 automatic 會自動幫你 require... 我沒去看他的實做,但我猜是建 table 吧。有一個小缺點,例如你想這樣做:

[1,2,3].shuffle.map &:to_s

很不幸,這樣會失敗。shuffle 可以正常自動 require, 但是 Symbol#to_proc 不能藉由這個方法自動 require. 如果你寫:

[1,2,3].shuffle.map &:to_s.to_proc

這樣是 ok 的,automatic 可以自動 require 該 require 的東西。問題大概出在,&:to_s 是 implicit call, automatic 偵測不到。所以第一次使用,還是必須明確說明需要用到 Symbol#to_proc, 接下來就可以快樂地使用

[1,2,3].shuffle.map &:to_s

了。或是前面自己手動 require:

require 'rubygems'
require 'facets'
require 'facets/automatic'

第二行的 require 可以讓你使用 Symbol#to_proc, 所以就可以直接寫:

[1,2,3].shuffle.map &:to_s

快去安裝 facets 吧!!
gem install facets
另一個 ruby web framework nitro 0.41.0 也有用到 facets !! 雖然是用 1.4.5 版的,而現在最新的 facets 是 1.8.54(以 2007.07.08 為準的最新版本)有機會也可以試試 nitro
gem install nitro
官方網站是:
http://www.nitroproject.org/
缺點非常明確!幾乎沒有文件 XD 我很少看到一個東西的文件可以少到這樣。

不多說了,我現在頭痛就算了,眼睛也很痛。