Misoca開発チームの黒曜(@kokuyouwind)です。
先日秋葉原UDXで行われた技術書典2にサークル参加して情報ガールを頒布してきました。*1
本気で原稿を落としそうになりましたが、なんとか入稿できてよかったです。
「原稿は早めに書こう」という教訓を得た結果、この開発ブログはそこそこ早めに書き始めています。*2
🔧 環境設定について
今回は設定ファイルの話をします。
MisocaではSettingsLogicを使い、RAILS_ENVに応じて設定を切り替えられるようにしています。*3
しかしアプリケーションの成長に伴い設定ファイルが肥大化し、以下のような設定ファイルができあがっていました。
default: &default application: &default_application host_with_port: "localhost:3000" protocol: http service_a: host: "<%= ENV['SERVICE_A_HOST']%>" token: "<%= ENV['SERVICE_A_TOKEN']%>" user_path: /api/v1/user issue_path: /api/v2/issue # こんな感じで設定が100行くらい続く development: <<: *defaults service_b: host: dev.example.com application_id: misoca_dev secret: secret_dev_b service_c: type: mock production: <<: *defaults application: <<: *defaults_application host_with_port: "<%= ENV['APPLICATION_HOST_WITH_PORT']%>" protocol: https service_b: host: prod.example.com application_id: misoca secret: secret_prod_b service_c: type: api application_id: misoca secret: secret_prod_c # こんな感じで設定が50行くらい続く
こうなると、設定を追加するのも確認するのも大変になってきます。
特にしんどいのは外部サービスの設定で、
RAILS_ENV
に関わらず、環境変数で設定を変える(service_a
)RAILS_ENV
で切り替える(service_b
)- 開発環境ではモックする(
service_c
)
など設定方法がそれぞれで変わってしまい、設定の見通しを更に悪くしています。
🔪 設定の分割
設定ファイルの肥大化を解決するため、外部サービスの設定は別ファイルに切り出していくことにしました。
service_b
の設定であれば、以下のように専用の設定ファイルを読み込むコードを書いておきます。
class ServiceBSettings < Settingslogic source "#{Rails.root}/config/service_b.yml" namespace Rails.env end
こうすると、service_b.yml
は下記のようにとてもシンプルになります。
development: host: dev.example.com application_id: misoca_dev secret: secret_dev_b production: host: prod.example.com application_id: misoca secret: secret_prod_b
同様に、service_c
の設定は以下のようになります。
development: type: mock production: type: api application_id: misoca secret: secret_prod_c
元の設定と比べて、環境ごとの設定がわかりやすくなりましたね。
🚩 設定切り替え条件の変更
ステージングサーバと本番サーバで外部サービスの向き先を変えたい場合は、service_a
のように環境変数でサーバを切り替えることになります。*4
このような設定は、SettingsLogicのnamespace
を利用して、1つの環境変数で設定項目をまとめて切り替えるようにしました。
class ServiceASettings < Settingslogic source "#{Rails.root}/config/service_a.yml" namespace ENV['SERVICE_A_ENV'] || 'development' end
設定ファイルであるservice_a.yml
は以下のようになります。
default: &default user_path: /api/v1/user issue_path: /api/v2/issue development: <<: *defaults host: dev.servicea.example.com token: token_dev production: <<: *defaults host: servicea.example.com token: token_prod
こうすることで環境変数の設定が簡潔になり、各環境での設定内容をきちんと設定ファイルに記述できるようになりました。
🏗 URLの構築
上述の設定ファイルのようにホスト名とパスをそれぞれ定義した場合、URLを使う箇所で"https://#{ServiceASettings.host}#{ServiceASettings.user_path}"
のようにURLを組み立てる必要が出てきます。
これは使いづらかったので、以下のように設定クラスを拡張し、*_url
に対応する設定が見つからない場合には*_path
からURLを生成して返すようにしました。
class ServiceASettings < Settingslogic source "#{Rails.root}/config/service_a.yml" namespace ENV['SERVICE_A_ENV'] || 'development' # *_urlが見つからず、対応する*_pathがある場合はhostを含めたフルURLを生成して返す def method_missing(name) if name =~ /_url\Z/ && host path_key = name.to_s.gsub(/_url\Z/, '_path').to_sym url = URI.join("https://#{host}/", send(path_key)).to_s create_accessor_for(name, url) url else super end end end
これで、ServiceASettings.user_url
のようにアクセスすることができるようになります。
設定ファイルを切り分けることで、こういった各設定ごとのヘルパーも生やすことができるようになり、なかなか便利です。*5
📢 宣伝
Misocaでは設定ファイルを綺麗にしたいエンジニアを募集しています。