settingslogicを使った環境毎の定義と、rspecでの値の一時的な書き換え

2014.06.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Ruby on Railsの開発においてsettingslogicを使うと、develop・production・testと環境ごとに異なる環境変数を定義するのに便利であるのと、rspecにてエラー時のテストを実行するのに便利だったので、紹介したいと思います。

settingslogicの使い方とサンプルソース

サンプルソースについて

まず、サンプルとして作成したアプリの動きについて説明します。リクエストを受け付けると、ローカルにあるファイルを読み込み、内容をjson形式で返却するAPIです。読み込むファイルの名称と、格納先フォルダを、settingslogicを利用して定義します。

インストールと定義の作成

1.インストール

Gemfileに以下を記述して、bundle install します。

gem 'settingslogic'

2.定義ファイルの作成

任意のファイル名の定義ファイルを用意し、環境ごとに異なる変数を定義します。今回は「application.yml」というファイル名で、configフォルダ内に配置しました。

defaults: &defaults
  file:
    folder: <%= Rails.root %>/lib/files
    name: default.txt

development:
  <<: *defaults

test:
  <<: *defaults
  file:
    folder: <%= Rails.root %>/spec/files
    name: test.txt

production:
  <<: *defaults
  file:
    folder: <%= Rails.root %>/lib/files
    name: production.txt

yml形式なので、直感的に分かると思います。一番上で全環境共通のデフォルトの定義を行い、その下に環境毎の定義で継承しています。デフォルトと異なる定義については、環境毎の定義にてオーバーライドしています。今回は「file」という項目内にネストして「folder」「name」を定義しています。注意点としては、オーラーライドする場合には「folder」「name」の両方を記述する必要がある点です(片方だけ定義し直そうとしたら読み込まれなかった)。例えば「production:」では、「folder」の定義値はデフォルトと同じですが、重複する形で記述しています。

3.アプリケーションへの設定

上記で用意した定義ファイルをアプリで読み込むため、config\initializers内に「settings.rb」を作成し、以下のように記述します。

class Settings < Settingslogic
  source "#{Rails.root}/config/application.yml"
  namespace Rails.env
end
[/ruby]
<h3>サンプルソースと動作確認</h3>
<p>
上記で作成した環境毎の定義を使用するサンプルです。
</p>
<h4>1.コントローラ</h4>
<p>
定義ファイルに記述したファイルを読み込み、json形式で返却するためのコントローラです。処理に失敗した場合、HTTPステータスとして500を返却します。
</p>
<h5>files_controller.rb</h5>

class FilesController < ApplicationController
  def read
    folder = Settings.file.folder
    filename = Settings.file.name
    @content = File.open(File.join(folder, filename)).read
  rescue => e
    render :status => 500
  end
end

2.JSON

JBuilderを使用してjsonを返却するため、jsonのフォーマットファイルをap/views/files内に作成します。

read.json.jbuilder
json.content @content

3.読み込むファイル

上記の定義ファイル内に記述した、アプリが読み込むファイルを用意します。上記に書いたように、develop・test・productionの各環境毎に異なるファイルを用意します。

lib\files\default.txt
This is a default.
lib\files\production.txt
This is a production.
spec\files\test.txt
This is a test.

4.RSpec

上記で作成したControllerなどをテストするrspecは以下のようになります。

spec\controllers\files_controller_spec.rb
require 'rails_helper'

RSpec.describe FilesController, :type => :controller do

  describe "GET 'read'" do
    it "returns http success" do
      get 'read', { :format => 'json' }

      expect(response).to be_success
      expect(response.status).to eq(200)

      json = JSON.parse(response.body)
      expect(json['content']).to eq('This is a test.')
    end

    it "file not found." do
      allow(Settings.file).to receive(:name) { 'error.txt' }
      get 'read', { :format => 'json' }
      
      expect(response.status).to eq(500)
    end
  end

end

6~14行目は正常系のテストで、ファイルを読み込み、内容が返却されてくることをテストしています。返却される値は「3.読み込むファイル」で定義したtest環境用のファイルの内容です。

16~22行目がエラーの場合のテストです。17行目で、mockを使ってsettingslogicで定義した値を一時的に変更し、存在しないファイル名を指定しています。これでエラーが発生することが期待できます。20行目で、エラー時にはHTTPステータスとして500が返ってくることを確認します。

このRSpecを実行すると以下のように表示され、エラーのケースも含めて無事に実行できることが確認できました。(Pendingはありますが)

$ spring rspec spec/
(中略)
4 examples, 0 failures, 2 pending

develop・productionの動作確認

最後に、development・production環境で正しく動くかを確認してみます。まずはdevelopmentからです。コンソールよりRailsを起動し、curlを使い確認します。

rails s
$ curl -X GET http://localhost:3000/files/read
{"content":"This is a default."}

「3.読み込むファイル」で定義したdevelopment環境用のファイルの中身が返却されました。次にproduction環境の確認です。

rails s -e production
$ curl -X GET http://localhost:3000/files/read
{"content":"This is a production."}

こちらもproduction環境用のファイルの中身が返却されました。

まとめ

settingslogicを使うと、環境毎の定義が簡単にできることや、その値をmockで書き換えることでエラー時のテストが可能であることが伝われば幸いです。