2014-12-31 14:09:41 +0900
2014年に読んだ本の中で、とくに面白かったもの。
2014年に読んだ本の中で、とくに面白かったもの。
Rails 4.2以降でストリーム(STDINなど)への出力を抑止・キャプチャする話。
Rails 4.1までに含まれるActiveSupportでは、Kernel#captureやそのエイリアスであるsilenceメソッドを利用することで、標準出力などへの出力内容を取得したり、出力を抑止することができていました。
これは、例えば「Herokuのdrainを監視する各種アドオン(TreasureDataやFlyDataなど)に対して、アプリがSTDOUTにJSONを出力することでデータを受け渡す」ような実装をしている場合に、テスト実行時などに出力を抑止したり出力内容を検証するようなケースで重宝していました。
Rails 4.2ではそれらのメソッドがスレッドセーフではないことを理由にDeprecatedとなり、将来的には削除されることとなりました。
今後は、上記のIssueでコメントされているRubyTapasで紹介されたコードなどをspec/supportに置いて参照するのが妥当かな、というのが現時点での認識です。
rspec-mocksのSpyという機能を使うと、メソッドが想定した形で呼び出されたことの検証を、自然な順序で記述できるようになる。
ここでいう自然な順序とは、「検証用のコードを実行してから、テストコードを実行する」のではなく「テスト対象コードを実行した後で、検証用のコードを実行する」という形で記述できるようになる、ということを指す。
例えば、コントローラーのアクション内でサービスクラスのメソッドが呼び出されることを以下のようにテストしている場合、
というようになる。expectの記述が、SUT(テスト対象システム)であるget :showの呼び出しよりも前になくてはならない。
ここでSpyを利用すると、以下のようにテスト対象コードを実行した後でexpectによる検証を記述することができる。
テストに必要な前処理と検証用コードを明確に分離できるため、テストの意図がより伝わりやすくなると思う。
Rails 4.0でrespond_to利用時にUnknownFormatが発生しないようにする方法。
コントローラーのアクションが以下のようなコードになっている場合、
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
respond_to do |format|
format.html
format.xml { render xml: @user }
format.json { render json: @user }
end
end
end
/users/1.textのようなパスにリクエストがあるとActionController::UnknownFormatが発生する。
通常はその挙動のままで構わないと思うが、要件によっては「不明なフォーマットが指定された場合はすべてtext/htmlを返したい」ということもありうる。そのような場合は、formatのanyメソッドを使うことでデフォルトの挙動を指定することができる。
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
respond_to do |format|
format.any
format.xml { render xml: @user }
format.json { render json: @user }
end
end
end
anyメソッドは、any(:xml, :json)のように受け入れるフォーマットを引数で指定することもできる。
format.any(:xml, :json) { render request.format.to_sym => @user }
また、anyメソッドにはallというエイリアスも設定されているので、format.allという指定でもよい。
参考:
Herokuアプリにデプロイされている一番新しいコミットについての情報を取得する必要があったため、Platform APIのReleaseを利用した。
Platform APIでの認証はAuthorizationヘッダにAPIキーを指定することで行い、結果のソート条件やページングについてはRangeヘッダで指定する。
以下の例のRangeヘッダの場合は、Releaseリソースについて「versionを降順に1件だけ取得する」という指定になる。
$ curl -n -X GET https://api.heroku.com/apps/Herokuアプリ名/releases \
-H "Accept: application/vnd.heroku+json; version=3" \
-H "Authorization: Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" \
-H "Range: version ..; order=desc,max=1"