10/30/2006

從 SQL 看 ActiveRecord 實做方式 (2) :Count

自從上次寫了 從 SQL 看 ActiveRecord 實做方式 ,我養成了寫完程式,會順便看 SQL 的好習慣。今天早上我花點時間來測試一下,計算資料庫數量的多寡的重要函式 Count 。之前我要寫數量多寡都是直接用 .size 來寫。像是找出 User 裡面名字叫做 a 的所有資料數量
User.find_all_by_nickname('a').size
結果出現的 SQL 是
User Load (0.000444) SELECT * FROM users WHERE (users.`nickname` = 'a' )
媽呀,真是刺激。也就是說,他會將所有的 nickname 為 a 的資料全部傳出來(1),塞給一個 array(2) ,然後計算 array 的數量大小。假設資料數量一大起來,1的傳輸時間會大幅度的增加,2的 array 所佔用的記憶體也會暴增。

再來,我來測試一下,關聯性 has_many 遇到計算數量時候的作法
a = User.find(1)
a.blogs.size
出現的 SQL 是
User Load (0.000339) SELECT * FROM users WHERE (users.id = 1) LIMIT 1
Blog Load (0.000320) SELECT * FROM blogs WHERE (blogs.user_id = 1)
媽呀,真是跟上面一樣刺激,會遇到的問題也跟上面一樣麻煩。也就是說,用 .size 的作法完全不可取。

我們換個方式,我看到了關聯性 has_many 支援一個 function ,就是在has_many 後面加入一個 _count
a = User.find(1)
a.blogs_count
出現的 SQL 是
User Load (0.000339) SELECT * FROM users WHERE (users.id = 1) LIMIT 1
Blog Load (0.000320) SELECT * FROM blogs WHERE (blogs.user_id = 1)
天呀,居然跟 .size 一樣,真是刺激的作法(有寫跟沒寫一樣)。

這個時候,臉上不禁出現三條線,那麼簡單的事情 Ruby on Rails 會沒考慮到?怎麼可能,我通常遇到這個問題時,會先 Chanllege 自己一下『應該是我不熟悉,而是沒有這個東西』。這時拿出居家旅行必備良書 Agile Web Programming with Rails 出來拜一拜,發現到如果我們使用
User.count(["nickname = ?" , "a"])
出現的 SQL 是
SQL (0.000324) SELECT count(*) AS count_all FROM users WHERE (nickname = 'a')
而關聯性資料庫
a.blogs.count
出現的 SQL 是
SQL (0.000296) SELECT count(*) AS count_all FROM blogs WHERE (blogs.user_id = 1)
拉拉,就是這個就是這個。 在 DB 處理 select count(*) 本來就會比 select * 來得快而且省記憶體。並且在資料庫傳輸到程式語言裡面只傳一個數字遠比傳一組 array 來得快。傳到程式語言裡面變數佔用的記憶體也有天壤之別。

結論是請大量使用 .count ,而請不要使用 _count ,或是 .size 來計算資料庫數量的大小。

Happy 100 篇

仔細看看,自從 8 月中開啟這個 Blog 之後,已經兩個多月的時間了 ,而今天正式邁入 100 篇。不管怎麼說,這個 Blog 的產量絕對是驚人的高。但是這也是一個隱憂,量多是不是也代表質不精?畢竟這個 Blog 只有我一個人寫,要衝高文章數,跟要撰寫好文章是互相牴觸的。未來的目標會是『量尚可但是追求質精』的目標前進,文章產生的速度未來一定會減緩,也會開始檢視過去文章,缺乏或是不好的部份給予修改。畢竟我的目標不是新聞網站,而是希望往『大家來這裡,都可以獲得點什麼』的目標給予自己期許。

以後這 Blog 文章會分幾個走向
  1. 工作時發現的小技巧
  2. Ruby on Rails 新聞
  3. 評論
  4. 安裝 Ruby on Rails Tutorial
  5. 個人的私語
個人的私語的部份會盡量的少,不過有時候吃的東西太多也會吐出一點東西,這是沒辦法的事情 XD。安裝 Ruby on Rails 的 Tutorial 部份是新加入的部份,目的是幫助所有人可以快速的安裝 Ruby on Rails 以及 lighttpd 。

Ruby on Rails 學到現在,我發現不是我已經摸透他了,而是自己還是趕不上 Ruby on Rails 的程度,每天依舊可以發現新的玩法,真是一個相當偉大的 Framework 。所以我的工作筆記依舊會持續下去,每天發現什麼就寫些什麼,一直到當兵的那天,或是 Ruby on Rails 已經無法滿足我的知識慾的那天。

COSUP 之後,我發現到大家對於 Ruby on Rails 已經有相當的興趣了,你們可以去 Taiwan Ruby User Group 討論,或是多翻翻 Ruby on Rails Planet ,JavaEye 裡面的文章,可以獲得很多東西歐。

以下是大家一直問我的問題

當兵

我在一個多月來的心得裡面提到
因為本人正在等兵單,有可能隨時被抓去當兵,卻又有可能因為奇怪的理由一直沒辦法去當兵
到目前為止,我還沒接到兵單,聽說我的兵種正在塞,所以拖到明年的可能性很高。不管怎麼說,我在工作上面還沒有一個很好的作品,寫書的事情還在繼續努力,所以我很希望等到明年再當兵,這個 Blog 會在我當兵時沒有更新的動作,不過我當兵前一定會公告一聲的。如果我有機會國防役,我會希望能夠找網路公司任職,不然我還是寧願去當兵。

Ruby on Rails 如何著手

基本來說,如果你是已經有相當網頁概念,買本 Agile Web Programming with Rails 足夠你從初學者邁入專家。但是如果你網頁程式什麼都不了解,我推薦你去讀 PHP 一個月再過來。如果你對於『相當網頁程式概念』這個標準有疑慮,你可以問問自己『我自己可不可以寫出有權限控管的討論區』,我個人是希望以此當作 Ruby on Rails 的入門標準。 Ruby on Rails 夠簡單,但是我不希望你一邊學 Ruby on Rails 一邊問 Cookie 是啥。話說到這,我想起以前我遇到 Yahoo! 的面試考題還不錯,簡單的問題但是要回答的好,實在不容易,還蠻能測試出基本能力的,可惜不能公開給大家欣賞。

10/28/2006

COSUP 感想

呼,這次的 Session Talk 順利的結束了。在裡面遇到不少好久不見的朋友,像是當兵中的 Contagion,超久不見的 far,以及上次才請教過 CakePHP 的 darkhero,也遇到從沒見過面的 Xdite 站長,一直跟我聯繫安排相關事項的 pofeng 也見到面,還有 lzy,每個人在網路感覺跟真實世界的感覺真的不太一樣。每個 talk ,我都從中得到或多或少的收穫,可說是技術含量相當高的 Conference。

關於 Session Talk 的內容,我一開始就設定在『開發時間』的議題,所以一直利用不同的數據,故事,資料來反覆證明 Ruby on Rails 的高速開發。並且試著分析出 Ruby on Rails 高速開發的原因。所以我將所有 Controller ,View 的部分都大筆刪去,僅僅介紹 Ruby on Rails 核心中的核心 ,Active Record。至於技術方面的 stuff ,我認為在我這個 Session 甚為貧乏,原因是因為我將議題定位在『開發時間』,所以不應該提出更多其她的東西來轉移焦點。

演講風格方面,我個人是喜歡帶動氣氛的感覺,或許是因為以前待過活動組的緣故。所以有人給予『電視購物』,『傳道士』之類的評語,我覺得這是對這種演講風格最好的肯定。關於我的真實體驗那一段,好像效果(笑果)比想像中好多了。講完出去買飲料時,我聽到我朋友對我一句話,讓我著實高興了好久
你那個 talk 好 High 呀!!!
最後,非常謝謝大家的抬愛。

PS. 本人的投影片 Slides 可以在此下載。

10/27/2006

Ubuntu 6.10 安裝 Ruby on Rails + lighttpd

這裡有Ubuntu 上面安裝 Ruby on Rails (精簡版),如果想要一步一步來安裝。請參照這裡。

1. 安裝make相關套件
修改 /etc/apt/source.list ,將這四行 uncomment 掉(為了安裝 lighttpd)
deb http://tw.archive.ubuntu.com/ubuntu/ edgy universe
deb-src http://tw.archive.ubuntu.com/ubuntu/ edgy universe
deb http://tw.archive.ubuntu.com/ubuntu/ edgy-backports main restricted universe multiverse
deb-src http://tw.archive.ubuntu.com/ubuntu/ edgy-backports main restricted universe multiverse

再來是安裝相關 make 套件
apt-get install build-essential
這樣安裝 Mongrel 才不會有相關問題

2. 安裝 Ruby
apt-get install ruby libzlib-ruby rdoc ri irb ruby1.8-dev
你可以順便安裝相關的 lib
apt-get install librmagick-ruby libgd-ruby1.8 libyaml-ruby libzlib-ruby
3. 安裝 gem
奇怪的是,我找不到任何可以用 apt 安裝 ruby-gem 的方式。所以請到 RubyForge 去下載 RubyGems 。解壓縮之後,打入
ruby setup.rb
最後在 update 一下 gems
gem install rubygems-update

4. 安裝 Rails
gem i rails -y
5. 安裝 Mongrel
我們用 gem 安裝
gem i mongrel mongrel_cluster -y
6. 安裝 Lighttpd
apt-get install lighttpd
7. 安裝 fastcgi
apt-get install libfcgi0 libfcgi-ruby1.8
8. 安裝 MySQL
apt-get install mysql-server-5.0
9. 安裝 MySQL Ruby Lib
apt-get install libmysql-ruby1.8
10. 安裝 SQLite3 and sqlite3 ruby lib
apt-get install sqlite3 libdbd-sqlite3-ruby1.8

PS.
  1. 我知道可以 apt-get install rails 一次安裝完,但是我個人偏好使用 gem 去作所有的 Ruby 套件安裝。
  2. 如果出現
    extconf.rb:1:in `require': no such file to load -- mkmf
    這樣的問題,那代表你沒安裝 ruby1.8-dev ,請回到步驟 2 安裝 ruby
  3. 如果你安裝完 Mongrel ,出現了這樣的情況
    uninitialized constant Mongrel::HttpHandler (NameError)
    請到 步驟一 去安裝 make 相關套件
  4. gem i fcgi 在 ubuntu 上面似乎不 work ,會出現下面 error message
    uninitialized constant RailsFCGIHandler
    請用 apt-get install libfcgi-ruby1.8 代替

參考連結:
  1. Ubuntu安装使用系列: Ruby语言运行环境1.8.4
  2. no such file to load — mkmf
  3. Install Mongrel on Ubuntu 6.06 Dapper
  4. [Ruby 筆記] Rails 於 lighttpd 上的設定
  5. Ubuntu安装使用系列: 图片处理软件ImageMagick的Ruby语言RMagick
  6. Install Ruby Rails on Ubuntu Edgy Eft

10/26/2006

Getting Real Online 免費釋出

在廖大的Blog看到的消息,37signal 熱銷 20000 本,介紹網路服務新模式的好書 Getting Real 可以 Online 免費閱讀了。這裡還有對岸人人整理了一份學習筆記,方便閱讀。

Cache Class

如果你看過 Ruby on Rails 的書,他上面會有說在 Development Mode 裡面,Ruby on Rails 預設會auto reload class ,而不是將 class cache 住。這樣的好處是你不需要重新 restart server ,方便開發時間。這個選項在 config/enviroments/xxxxx.rb 裡面也可以手動修改設定
config.cache_classes = false
如果你要 cache class ,就把這個改成 true 即可。但是 Ruby on Rails 真的會 auto reload 所有的東西嗎?結論似乎不是這樣的。我開始開發時就發現,他只會 auto reload app/ 底下的 class ,所以所有的 config ,lib 之類的 class 有修改過都必須要重新啟動 server 。

這樣還好怎麼不麻煩,但是我今天又發現到,就算是 app/ 底下的 class ,也有一些不會 auto reload。今天有一個 Model Class ,他在 app/models/ 資料夾底下,他的 code 是這樣的
class User < ActiveRecord::Base
end
當你修改他的檔案內容,他會很正常的 auto reload。但是,同樣是在 app/models/ 資料夾下,如果他的Class 並不繼承 ActiveRecord ,像是這樣
class User
end
很奇怪的是他居然不會 auto reload 這個 class ,而是會將他 cache 住。如果一不小心,就很容易 debug de 半天。

COSCUP 2006


本人受邀在 COSCUP 2006 的Web-Oriented Architecture 談談 Ruby on Rails 。時間是 2006 年 10 月 28 日(六)16:55 ~ 17:20,地點在台大綜合體育館二樓 248 室 。

因為時間不長,本人不會談論技術層次的問題,而是將重點著重於開發時間的議題,這也是 Ruby on Rails 的強項。也就是說,現場不會有太多稱得上技術層面的 stuff ,並且有些人可能會感覺我的 talk 廣告層面居多,這一切都是因為時間有限的因素。

如果您對於技術層面有問題,您可以在 17:45 的座談詢問,或是你對我的言論不滿,丟雞蛋也是可以接受的。(只要不是臭掉的就好)

10/25/2006

Rails 的原始碼行數比?

實例

看完 JavaEye 今天的文章,發現到根據 Robbin 估計,
网站来说,包含了forum,blog,SNS三种大型软件 的主要功能,每个部分单独去做,都要花好几个月,合起来的代码量(包括XML配置行数)保守估计至少要3-5万行。现在用ruby on rails编写,ruby代码量只有不到5000行。
根據 Robbin 這一篇截至现在JavaEye2.0 CVS上面代码行数,目前只有 3243行code。他保守估計 Java 跟 Rails 原始碼行數,大概是 6 : 1 ~ 10:1 的份量。

根據 poocs.net 在這篇文章的說法
The old codebase roughly consisted of around 50.000 lines of PHP code (plus a closed-source CMS that’s not included in this calculation). We’ve rewritten most of it (some features were left out on purpose) in about 5.000 lines of Rails code.
他用 Rails 改寫 PHP 現有的 Project ,做出來的原始碼行數 PHP : Rails 是 10 : 1。(註1)

根據 Beyond JAVA 裡面,Justin Gehtland 用 Rails 重寫一個用 JAVA Spring/Hibernate 寫好的 Project,他發現程式碼比例 JAVA :Rails 大概是 3.48 : 1 。附帶一提,他重寫的開發時間開發時間比是 16 : 1,更噁心的數字。(註2)

我曾經將以前寫過的一個小小 PHP Project 重寫,之前使用的 Framework 是我自己寫的 MVC 架構的PHP 程式。扣掉 HTML code ,程式碼行數大概PHP :Rails 是 8 : 1 左右吧。

GMANE 裡面有一位 Rick Bradley 提出一個healthcare網站的實例,他們將 JAVA/Hibernate/JSP/pojos/beans/Struts/Ajax 的專案轉換到 Ruby on Rails 時,他發現所有程式碼比例: JAVA 比 Rails 高達 20853 : 823,這是 25 : 1 的可怕程式碼數量比例。(註3)

結論

開發時間很難去作 Benchmark ,不過程式碼行數就很赤裸裸了。我們可以發現到,Rails 在程式碼行數上面的優勢還是相當相當明顯的。程式碼的行數代表的意思不只是開發速度的快慢有絕對的正相關,維護程式的速度也會加快,重構等等議題也會簡單許多,這是一個 Ruby on Rails 巨大的優勢。


註解
  1. 根據原文,PHP行數裡面,沒有計算一個 close source 的 CMS ,用 Ruby on Rails 實做時,也沒有implement 一些他們後來認為不重要的功能。我將兩者造成的程式碼數量增減都視為抵銷
  2. 原始碼的比例是 3293:1164,設定檔的行數是 1161:113
  3. GMANE提出 25 : 1 的驚人實例的詳細數據列表
Java version:

10361 lines of Java code
1143 lines of JSP
8082 lines of XML
1267 lines of build configuration
-----------------------------------------------------------
20853 TOTAL lines of stuff

Rails version:

494 lines of code (386 "LOC" per rake stats)
254 lines of RHTML
75 lines of configuration (includes comments in routes.rb)
0 lines of build configuration
-----------------------------------------------------------
823 TOTAL lines of stuff

10/24/2006

map_by_method 在 irb 上面的 bug

剛剛發現的問題,我使用的 irbrc 是 強化你的 irb 這裡的設定。我發現到當我 Model 裡面這樣設定
class User < ActiveRecord::Base
has_many :habit_contents , :dependent => true
has_many :diaries , :dependent => true
end
而我在 ruby script/console 裡面使用時,發現到 habit_contents 可以正常使用,但是 diaries 卻會出現 Error 。
>> a = User.find(1)
......
>> a.habit_contents
.....
>> >> a.diaries
NameError: /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:100:in `const_missing': uninitialized constant Diari
我發現到所有 1:1 使用都沒問題,1:m 有時候有問題。像是 Diaries 無法正確判斷複數,Messages 也無法正確判斷單複數,他會出現 uninitialized constant Messag,很可能是在單複數判斷機制上面出現了 BUG 。

但是我在Web 環境上面,沒有任何的問題,很明顯的,不是我程式的問題,問題出現在 irb 裡面。我想到了上周我改寫了 irbrc ,所以就一行一行檢查。發現到當我將 map_by_method comment 起來,一切都恢復正常了。
require 'rubygems'
#require 'map_by_method'
require 'what_methods'
require 'wirble'
require 'irb/completion'
So,這明顯是 map_by_method 在 irb 上面的 bug,請暫時不要在 irb 上面使用 map_by_methods 。

大家一起來跳舞

左到右是 DHH ,Matz , Babie。來頭是 Rails 創作者 -> Ruby 創作者 -> RedHanded blogger。至於他們詭異的 pose ,是向上天祈求 Ruby 速度可以快個一倍嗎?非也非也,根據這篇照片出處的 comment
This pose is a funny and famous pose in Japan, that called ‘Boom’. This pose is expressed a mimicking air-plain flies, came from an Ascii Art on 2ch – a huge aggregate of BBSs, photogravure idol Shoko-tan spread this pose to Real world.
簡單講就是日本 BBS 的發明的另外一種姿勢,本來都只侷限於文字版本。由一個偶像中村翔子率先登高一呼,在自己 Blog 發表真人版,從此日本陷入了
二( ^ω^)二⊃
             |
          ( ヽ
這樣的世界。