Rails Config 私人偏方
在一個 Rails 專案,常需要使用一些設定值。最常見的就是各種 API 使用的 key。通常我們會把這些設定獨立起來放在一個 yaml 檔案裡面,避免紀錄在 git 的版本管理系統裡面。個人使用了 Settingslogic ,覺得十分合用,不過在一些細節上我想要推廣一些撇步:
讓設定檔成為能獨立運作的 class
Rails 的啟動是十分花時間的,在 Ruby 1.9 下面通常要等個幾十秒。有時候有些可以獨立運作的套件為了使用一個 Rails 下面的設定值,就得花時間啟動整個 Rails 才能取得設定。要避免這種情形,Settingslogic 我是這樣寫的:
# config/app_config.rb 檔案
require 'settingslogic'
class AppConfig < Settingslogic
source File.dirname(__FILE__) << "/app_config.yml"
end
首先是手動 require Settingslogic,然後其 yaml 檔案則是使用相對路徑作參照(與 app_config.rb
同一個目錄)。這樣子在其他環境就能簡單 require "./path/to/app_config"
讀取設定了。
我是把我的 app_config
放在 config 資料夾下面,然後在 config/initializers/ 下開個 app_config.rb
檔案 require 我的 app_config
。
我的 config/app_config.yml 長這樣:
exception_notification:
recipients:
- lulz@example.com
- lols@example.com
sendgrid:
user_name: 'foobar'
password: '2000'
rollbar:
access_token: dghgkalt4k5439fkgio43pb2
當然這個檔案不會放進版本控制。會放進版本控制的是 config/app_config.yml.example 檔案,裡面只有放 key 沒有值,讓大家知道需要設定什麼。我也沒有用 namespace 之類的東西,反正一個地方就一個設定,依照 exmaple 照樣畫葫蘆即可。
把網域資訊存進設定檔中
一個網站的網域資訊是很多設定不可或缺的一部分。比如說我使用 capistrano_nginx
就常常需要像是 .example.com
的設定。網域設定又會因為環境的不同而改變。 production 跟 staging 各會有不同的設定。所以我覺得把網域寫在 config 中是最恰當的。
# app_config.rb 中
# returns domain string
# pass in false for segment key to hide that segment, e.g. `protocol:false`
def self.domain(params = {})
params = {protocol:'http'}.merge(params)
params = self.get('domain_setting').to_hash.merge(params)
url = ''
url << params[:protocol].clone << '://' if params[:protocol]
url << params[:subdomain].clone << '.' if params[:subdomain]
url << params[:domain]
url << ':' << params[:port].to_s if params[:port]
url
end
# app_config.yml 中
# 注意我的 key 是 symbol 喔
domain_setting:
:domain: 'lvh.me'
:port: 3000
這樣子,我在就可以呼叫下列指令來取得不同的網域資訊:
# irb 中
AppConfig.domain #=> "http://lvh.me:3000"
AppConfig.domain(protocol:false) #=> "lvh.me:3000"
AppConfig.domain(protocol:false, subdomain:'admin') #=> "admin.lvh.me:3000"
這裡使用的參數跟 url_for
類似。
我在 production.rb 跟 staging.rb 當然也就這樣改了:
config.asset_host = AppConfig.domain(subdomain:'assets')
config.action_mailer.default_url_options = { :host => AppConfig.domain(protocol:false) }
像是 staging/development 這種地方,把可能變動的網域給寫死在 rb 檔然後存進去 git 中本來就怪怪的,所以我都用設定檔來作設定。另一個好處是,想要修改網域就只要修改一個地方,徹底貫徹DRY懶人精神!
最後,記得也要準備一份只有 key 沒有 value 的 app_config.yml.example
,放入版本管理中,給以後的人作參考喔。