「Ruby 語法放大鏡」系列短文主要是針對在大家學習 Ruby 或 Rails 時看到一些神奇但不知道用途的語法介紹,希望可以藉由這一系列的短文幫大家更容易的了解到底 Ruby 或 Rails 是怎麼回事。
在開發 Ruby on Rails 專案的過程中,一定都看過或用過 rake db:migrate 這個指令,大部份的教學資料可能只會跟你說「只要照著打這個指令就行了」,沒有太多詳細的介紹。
其實這個 rake 指令,是一位已過世但我非常景仰的大師 Jim Weirich 所開發的。如果各位曾經聽說過 Make 這個工具,Rake 就像是 Ruby 版的 Make,你可以用 Ruby 語法來編寫 makefile。
動手練習
隨便建一個目錄來練習一下:
$ cd /tmp
$ mkdir rake_demo
$ cd rake_demo
$ rake
rake aborted!
No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)
/Users/user/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `eval'
/Users/user/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `<main>'
(See full trace by running task with --trace)'
執行 rake 指令後,它會預期在這個目錄有 rakefile, Rakefile, rakefile.rb, Rakefile.rb 這四個檔案任何一個檔案,但因為什麼都沒有所以出現上面的錯誤訊息。既然沒有,就做一個給它:
$ touch Rakefile
$ rake
rake aborted!
Don't know how to build task 'default' (see --tasks)
/Users/user/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `eval'
/Users/user/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `<main>'
(See full trace by running task with --trace)'
咦? 有 Rakefile 但還是有錯誤訊息,那是因為我們什麼都還沒寫,所以先打開 Rakefile 來寫一個簡單的寄信任務:
1 2 3 4 5 6 7 | |
task 方法可以定義任務的名字,後面接的 block 就是它實作的內容(實作功能就留給大家了,這裡僅先使用 puts 方法把結果印出來)。最上面的 desc 則僅是這個任務的描述,如果有描述的話,在 rake -T 指令列出任務清單的時候就看得到說明了:
$ rake -T
rake send_email # 寄發會員通知信
因為我在上面定義了一個叫做 send_email 的任務,如果要執行它的話,就是這樣下指令:
$ rake send_email
Email Sent!
又,因為這邊我在最後一行把預設的任務設定成 send_email,所以即使我只輸入 rake 指令,它也會執行 rake send_email 任務。
另外,如果任務沒有在 rake -T 的清單上不表示任務不存在,它可能只是 desc 沒寫而已,但一樣是可以執行的喔。
那 rake db:migrate 中間的冒號?
了解 rake 的基本操作後,回來看看 Rails 裡常用的那個 rake db:migrate 是怎麼做的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
使用 namespace 方法,然後把任務包進去,這樣一來,任務的名字就會長得像這樣:
$ rake -T
rake db:migrate # Migrate the database
rake send_email # 寄發會員通知信
而且可以正常執行:
$ rake db:migrate
migrating database!
大概就是這樣囉! 關於更多 Rake 的使用方式,請參閱 Rake 的 Github 網站說明 https://github.com/ruby/rake
Rails 裡也是這樣嗎?
隨便開一個 Rails 專案,在根目錄應該可以看到一個 Rakefile,它的內容長這樣:
1 2 3 4 5 6 | |
咦? 怎麼都空空的? 讓我們直接挖 Rails 的原始檔出來看看(以 Rails 5.0.0 beta 4 版本為例):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
這段程式碼的意思就是一口氣載入一堆放在 railties/lib/rails/tasks/ 目錄的一些檔案(.rake 檔),這些都是 Rails 預設會載入的任務。而那個 rake db:migrate 任務內容就藏在(這裡)。
要在 Rails 專案加上自己的 rake 指令?
在剛剛看到根目錄的 Rakefile 的最後一行寫到:
1
| |
再繼續練習挖一下原始碼,可以翻到這個檔案,這個檔案大概 700 行左右,我只列出相關的程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 | |
看得出來 Rails 除了載入內建的任務外,其中這個 load_tasks 方法會把在專案裡 lib/tasks 目錄裡的任務檔也都一併讀進來。
除了挖原始碼看得出來之外,其實在 Rails 專案根目錄的 Rakefile 一打開的那兩行說明:
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
如果你想要加自己的 task 的話,就是寫在專案的 lib/tasks/ 目錄裡。寫法跟上面寫 Rakefile 沒什麼兩樣,更多細節可參閱 Rails Guide 上的 Command Line 的 Custom Rake Tasks 章節。
Rails 5 的變化
提醒一下,在 Rails 5 之後,原本那些 rake 指令,例如:
$ rake db:migrate
$ rake routes
也都搬一份到 rails 指令底下囉,像是這樣:
$ rails db:migrate
$ rails routes
下次看到這樣的指令,不要以為是打錯字囉 :)