Posted by (2) Comment
如同Title,懶得看我發牢騷的就跳過吧,一不小心就扯遠了:p
上回寫有關ActiveRecord的文章其實到現在都一直提不起勁來寫,一來是我認為基礎性的教學,網路上或書店裡都有很多參考資料,似乎輪不到我來寫;二來是進階的教學我又不夠力。但站在推廣的角度來說,關於Ruby on Rails的文章總是多多益善。
還記得以前我跟老師、同學們分享Ruby on Rails這個嶄新的框架時,快速在短短幾分鐘內依賴ActiveRecord及Dynamic Scaffold所搭建起來的簡單網站,讓在場的人都留下了滿深刻的印象。然而,對老師來說,他認為MVC pattern或ORM的實作雖然可以快速、有效地提高生產力,但這個框架的能耐有多大是他最關心的。
說實在,Ruby on Rails的能耐我也無從說起,因為Ruby on Rails能做到的,其他語言也都能做到,甚至其他語言、框架、技術有著更悠久的歷史、更成熟的架構,甚至更有力的後台,例如.NET, JAVA頗受企業青睞,出問題時即使自己公司內部的技術人員無法搞定,還可以求救於其他公司甚至原廠。
當時老師問我一句:「Ruby on Rails拿來做電子商務、處理金流、交易系統ok嗎?」事實上我當然認為是ok的,但我也很清楚要做這些事情"好像"不該拿RoR來做,總之,當下的我實在難以回答老師的問題,頓時我覺得自己目光如豆,我的視野大概就只停留在Web Development,所謂企業級的解決方案,我可說是個門外漢。(事實上我們那們課修的是Web 2.0研究,我報告純Web相關的應該沒錯啊XD如果是要拿Web做金流、電子商務,那RoR理所當然也是個選擇嘛,要加密、要搞Web Service、要連結不同的資料庫也都沒問題啊~:p)
老師似乎沒有太大興趣,那同學們呢?大概是我沒有表現出Rails的優美,同學們大概也只是當作我在報告給老師聽、而不認為我是在推廣-.-,總之後來我只成功讓一位同學繼續走上學習Ruby on Rails的道路,而他也成功讓一位女同學在某一門課的期末專題使用Ruby on Rails開發網頁。現在回頭想想,當初真是辛苦他們了orz
在那之後,如同日前我剛換上的新版面所言,我的工作、專案主要使用.NET Framework開發,Ruby on Rails是我閒暇時拿來娛樂的工具之一。主要原因有二,一是我認為除了網頁開發之外,我還想學習更多的技術;二是因為人在江湖、身不由己,我的工作、我的專案都是不得已要使用.NET的orz...
不過,學習Ruby on Rails的過程著實讓我成長不少, 寫Rails有時候是一種享受:D
這篇本來是想寫這一陣子開發Rails網頁,有關使用Helper的心得,不過既然已經扯遠了,那就下次再說吧XD
Posted by (0) Comment
ericksk說:「既然找不到別人寫的,那就自己寫吧!」,因此他寫了一套jq4r,全名叫jQuery helpers for Rails。真是頗吸引我的,之前用過一陣子的jQuery,感覺還滿好用的,最近不但推出新版還搞了一套jQuery UI,之前在某個日文網站看到的評論稱之為「jQuery's Scriptaculous」。
anyway,ericsk的文章在此:jq4r - jQuery helpers for Rails
他另外開了一個blog及google code project專門來放jq4r:
Posted by (0) Comment
最近在ptt的Ruby板,有板友在討論刪除資料時如何進行驗證,原po的問題大致上是說倘若某一筆資料跟其他的資料有所關聯,該如何顯示錯誤訊息給使用者、告知因為有資料的關聯參照存在所以無法刪除。
看到這個問題,我心中第一個想到的是「就用CASCADE的方式把資料一口氣刪光啊!」XD不過隨即想想,大多數的資料可不是說想刪就能隨便刪的,例如在Ruby板上的討論串就有人提到要刪除某個分類名稱,但在刪除前要先將該分類底下的文章都指定為未分類。
總之,好奇心驅使之下,我猜想ActiveRecord裡面一定有針對關聯性特別設計一套機制來處理,果不其然其實在has_one, has_many底下就有:dependent來處理互相參照的資料。
:dependent有三個選項:(在此假設User has_many Posts, Post has_many Comments)
class User < ActiveRecord::Base has_many :posts, :dependent => :destroy end
至於要在物件刪除之前,若要做驗證動作,就如同ptt Ruby的板主godfat分享的一樣,使用"before_destroy"可以在呼叫destroy之前先做其他動作。例如在這個網頁裡面看到的sample code(before_destroy是寫在Model裡,destroy的動作則是由controller中的action來呼叫):
class Group < ActiveRecord::Base acts_as_treerder => "name" has_and_belongs_to_many => :users before_destroy :validates_no_dependents def validates_no_dependents if children.size > 0 || users.size > 0 errors.add :base, "Cannot delete this group, as it has sub-groups and/or users" end end end
在上一篇文章裡稍微提到了ActiveRecord能讓你不需寫SQL語法便能快速地存取資料,(事實上我只提到了讀取的部份,寫入其實也相當簡單,依樣畫葫蘆,只是使用的method不同罷了)這篇便來談談在ActiveRecord裡,是如何處理Model與Model之間的關係。
平常我們在描述兩個資料表之間的關係,通常可能會說兩張table是一對一、一對多、或是多對多,而在ActiveRecord裡,兩個Model之間的關係主要以上述的三種方式來定義。
延續上一篇文章中我所建立的三個model: User, Video, and Comment,這三個model之間的關係如下:
看這樣的描述,如果是尚未接觸過Ruby on Rails或是ActiveRecord的讀者一定會感到疑惑,根本聽不懂我在說什麼嘛!請看下列三段程式碼,分別是上述三個Model的程式:
#Model: rails_app/app/models/user.rb class User < ActiveRecord::Base has_many :videos has_many :comments end
#Model: rails_app/app/models/video.rb class Video < ActiveRecord::Base has_many :comments belongs_to :user end
#Model: rails_app/app/models/comment.rb class Comment < ActiveRecord::Base belongs_to :user belongs_to :video end
看出什麼了嗎?只要使用很直觀的has_many, belongs_to這樣的method便可以輕鬆定義Model之間的關係。
接著,我們還必須仰賴migration替我們新增對應的資料表或欄位,在上一篇文章中已經完成了User migration的程式:
#Migration: rails_app/app/db/migrate/001_create_users.rb
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :name, :string, :null => false
t.column :email, :string, :null => false
t.column :password, :string, :null => false
t.column :nickname, :string, :null => false
t.column :memo, :text
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :users
end
end
在上一篇文章中,除了self.up、self.down兩個method之外,我並沒有交代其他程式碼所代表的意義,這邊稍微補充一下
接下來我們要繼續完成與Video、Comment這兩個model相關的migration。migration檔案通常是由generator在建立model的同時,就會自動新增一個migration的檔案,而generator也可以單獨產生一個migration file讓你對資料庫的schema進行改變。
#Migration: rails_app/app/db/migrate/002_create_videos.rb
class CreateVideos < ActiveRecord::Migration
def self.up
create_table :videos do |t|
t.column :title, :string, :null => false
t.column :description, :text
t.column :user_id, :integer
t.column :created_at, :datetime
end
end
def self.down
drop_table :videos
end
end
#Migration: rails_app/app/db/migrate/003_create_comments.rb
class CreateComments < ActiveRecord::Migration
def self.up
create_table :comments do |t|
t.column :content, :text
t.column :user_id, :integer
t.column :video_id, :integer
t.column :created_at, :datetime
end
end
def self.down
drop_table :comments
end
end
從上面的Migration可以發現下列兩點:
從上一篇文章透過generate指令來產生model、migration files,到本篇文章我們實際去修改上述的檔案內容,如果熟練大概三分鐘就可以完成,接下來便可以開始感受到ActiveRecord所帶來的高生產力與享受:D
做完上述動作之後,我們便要將定義好的table schema寫入資料庫裡面,在Rails的目錄結構裡面,config/database.yml便是在定義資料庫的相關設定值。設定資料庫的動作一般是在建立好Rails Project便該完成的,這算是開發Rails project的前置動作之一,那為甚麼我到現在才說咧?其實只是我忘了先講怎麼建立專案、怎麼設定資料庫罷了.....以後如果要把這個系列彙整起來給Rails新鮮人看,再寫一篇好了XD
關於Rails Project的前置動作可以參考午夜盧比人Winson寫的「My Rails Way」,裡面提到的技巧都相當實用,像是第一點提到的
為了節省時間,我通常會維護一個叫做sample的Rails project,裡面把簡易的帳號、密碼、登入做起來,以及設計一個model,也把他的tag、comment功能作起來,並把相關的測試案例寫好、常用的外掛裝起來,然後整包壓成一個sample.zip檔。
倘若資料庫已經設定好,接下來會使用到「rake」指令來將我們規劃好的schema寫入資料庫裡面,指令如下:
rake db:migrate
通常會得到下面的結果:
deduce@nccu:~/test$ rake db:migrate (in /home/deduce/test) == CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0170s == CreateUsers: migrated (0.0174s) ============================= == CreateVideos: migrating ===================================== -- create_table(:videos) -> 0.0090s == CreateVideos: migrated (0.0094s) ============================ == CreateComments: migrating =================================== -- create_table(:comments) -> 0.0089s == CreateComments: migrated (0.0093s) ==========================
如果沒有出現錯誤訊息就代表資料表皆成功建立,接下來我們便可以開始使用ActiveRecord來對資料庫進行各種關聯式的存取動作!
註:實在是有點拖稿,不過我寫這些文章是希望可以盡量淺白,所以盡量詳細、盡量簡單,一樣,有任何錯誤或建議請多指教。
首先,Ruby on Rails對我而言,最大的衝擊便是Rails中的ActiveRecord實作了ORM (Object-relational mapping),讓程式對資料庫進行存取的動作時,程式設計師不需要寫任何一行SQL語法,就可以存取到特定的資料表並找到自己想要的資料,沒接觸過Rails或是ActiveRecord的人,會認為:「真的有這麼神奇嗎?」
我可以告訴你,是的,真的就這麼神奇!但,90%的一般性需求不需要寫SQL語法,而剩下的10%特殊需求,Rails又提供給你充足的彈性讓你自訂SQL語法對資料庫進行存取。
如此的作法正符合了Convention Over Configuration的精神,RoR裡面使用大量的慣例來方便程式設計師進行開發,當慣例無法滿足需求時,她同時間會提供足夠的空間讓你自訂想要的功能。
舉例來說,假設要開發一個類似Youtube的網站,資料庫中起碼會有這三個資料表:
在Rails中的作法,通常會使用下列的語法來建立與資料表相對應的三個Model:
ruby script/generate model user
ruby script/generate model video
ruby script/generate model comment
"generate"指令除了會產生上述的三個Model,同時還會產生三個對應的migration,migration中文通常翻作遷移,概念是資料庫的版本演進由migration來控制,包含新增、刪除資料表,或是新增欄位、刪除欄位、欄位類型變更、欄位名稱變更、欄位屬性變更等等,都可以在migration裡面完成。
由migration來進行資料表的管理,優點是無須在意後端使用的資料庫系統為何,migration會自動幫你產生對應到不同資料庫的資料型態及語法,因此無論後端是MySQL、MSSQL Server、DB2、Oracle,ActiveRecord的Migration都可以替你完成資料表的建置與管理。(事實上可能不是每個資料庫系統都有100%的支援。)
由generate指令所產生的migration檔案通常如下:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
end
end
def self.down
drop_table :users
end
end
上述的程式碼會對資料庫進行新增table的動作,可以發現其中定義了self.up及self.down兩個method,這代表當程式設計者對於目前對資料庫進行的修改感到不滿意時,可以馬上回到前一個版本,此時migration便會自動執行self.down的程式;換句話說,migration裡面定義的是兩個反向的動作,例如:新增table、移除table;新增欄位、移除欄位。
接下來我將這個CreateUsers的migration修改如下:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :name, :string, :null => false
t.column :email, :string, :null => false
t.column :password, :string, :null => false
t.column :nickname, :string, :null => false
t.column :memo, :text
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :users
end
end
我定義了七個欄位給users這張table,如此的定義方式,在接下來的開發過程將會有幾個重要的效果:
<% user = User.find(1) %> 帳號:<%= user.name %> 電子郵件:<%= user.email %> 暱稱:<%= user.nickname %> 何時註冊?<%= user.created_at %>
如何?是不是非常簡單而且非常直觀:)
<% users = User.find(:all) %> <% users.each do |user| %> User序號:<= user.id %> 帳號:<%= user.name %> 電子郵件:<%= user.email %> 暱稱:<%= user.nickname %> 何時註冊?<%= user.created_at %> <% end %>
(Ruby也可以用 for user in users的寫法,不過我比較習慣用.each)
事實上,要深入談ActiveRecord的話,是可以寫成一本書的,最近才剛出版的一本新書「Pro ActiveRecord: Database with Ruby on Rails」內容便是在探討ActiveRecord的深入技巧,Amazon上的Book Description中有一段話是這麼說的:
Because ActiveRecord is configured with default assumptions that mesh perfectly with the Rails framework, Rails developers often find they hardly need think about it at all. However, if you are developing in Ruby without Rails, or are deploying against legacy databases designed without Rails in mind, or you just want to take advantage of database-specific features such as large objects and stored procedures, you need the in-depth knowledge of ActiveRecord found in this book.
大致上是說,身為一個Rails developer,由於Rails跟ActiveRecord有著許多的慣例(configured with defaul assumptions),因此我們通常不太需要想太多,反正用就對了。然而如果身為一個Rails developer,或是你想單獨拿ActiveRecord來開發其他與資料庫有關的project,就需要有更多深入的瞭解,這也是這本書的主要目的。
我想這或許會是一本值得購入的好書吧?:D
這篇文章簡單地介紹了ActiveRecord如何讓Rails developer輕鬆地存取資料,下一篇再來談Model與Model之間要如何運作,文中有任何錯誤或對本文有任何指教請留言或來信告知(deduce_at_gmail_dot_com),感激不盡!