RSpec2で特定のテストを実行する

Relishより、次のようにすることで特定のdescribe/context/itのみを実行することができる。

spec_helper.rbのRSpec.configureにfilter_runの設定を追加:

RSpec.configure do |c|
  c.filter_run :focus => true
end

テストしたいdescribe/context/itに:focusオプションを追加:

describe User do
  describe "#say", :focus => true do
    it "should return a String" do
      :
    end
  end
end

こうすることで:focus => trueを指定したテストのみが実行され、それ以外は無視される。複数個指定すればそれらすべてが実行されるし、ひとつも指定しなければ何もテストされない。テストコードが多い中で新規にテストを追加する場合などに便利だと思う。

上記のRelishのページに書かれている run_all_when_everything_filtered を設定すれば、:focusが1つも指定されていない場合に全テストを実行するというもの。


yard-sinatraでSinatraのドキュメントにYARDを使う

yardのプラグインであるyard-sinatraを使うとSinatraのアクションごとに記述されたyardocからHTMLドキュメントを生成できる。

インストールと設定

yard-sinatraをインストールすれば必要に応じてyardもインストールされる。 ただしyardはデフォルトではプラグインを読み込まないので、読み込むように設定してやる必要がある。

$ gem install yard-sinatra
$ mkdir ~/.yardoc
$ yard config load_plugins true

ドキュメントの生成

$ cd /path/to/sinatra/project
$ yardoc
$ open doc/index.html # OSXでのみ有効

Class Listからアクションが実装されているClassを選択すると、HTTPメソッド順にアクションが並んでいる。


display:boxとbox-ordinal-group

CSS3にはdisplay:boxとbox-ordinal-groupという便利な記法があって、マルチカラムレイアウトっぽいことが手軽にできる。詳しくはCSS3の数行で作る簡単な3カラムレイアウトを参照。

で、サンプルを書いてみた。

Google Chrome で表示すると次のようになる。

floatを使う必要がないのが嬉しい。

追記

CSS3の数行で作る簡単な3カラムレイアウトを見ると、複数ブラウザに対応する場合にはbox-sizingも考慮する必要があるみたい。


µ-optparseを使って.rvmrcを生成

optparseのラッパーであるµ-optparseを使って.rvmrcを生成するスクリプトを書いてみた。

オプションは下記の通り。–helpでも見れるし、コードを読むだけでもわかりやすい。

-r, –ruby
Rubyのバージョン。デフォルトはこのスクリプトを実行しているRuby
-g, –gemset
gemset名。デフォルトはカレントディレクトリ名
-f, –force
指定された場合、.rvmrcが存在しようがしまいが書きだす

たとえば次のコマンドでは”rvm use 1.9.2@rails31″を.rvmrcに書きだす。

$ rvmrc -r 1.9.2 -g rails31 -f

µ-optparseは宣言していない引数が指定された場合や、引数の値が条件(正規表現、特定の値、もしくは任意の判定条件を)にマッチしない場合にエラー文を出してくれたりと結構便利。–helpや–versionにも勝手に対応してくれるので、ちょこっとしたスクリプトを書くのに向いている。

まぁこんなコマンドを叩くぐらいなら直接echoした場合のほうが手軽だろうけど、どちらかというとオプション指定ナシで現在の環境の.rvmrcを吐くのが目的なので、実際問題オプションは必要なかったりする…かも


git-svnでリモートのブランチを作成・コミット

git svn branch <BRANCH>で<BRANCH>というリモートブランチがbranches/<BRANCH>に作られる。 これはgit branch -rで確認できる。

$ git svn branch foo
$ git branch -r

git checkout <BRANCH1> remotes/<BRANCH2>でコミット先がbranches/<BRANCH2>を向いたローカルブランチ<BRANCH1>が作成される。 これはgit svn infoで確認できる。

$ git checkout foo remotes/bar
$ git svn info

このローカルブランチに対して変更を加えていき、最終的にgit svn dcommitでリモートブランチに反映する。

$ git svn dcommit

コミット先のURLをgit checkoutでしか変更できないものなのかな?既存のローカルブランチのコミット先を変更する方法がある気がするんだけど、そこまで調べきれなかった。

そろそろgitの本が欲しい。


リモートホストとの差分

リモートホストとの差分を見る場合、次のようにsshでcatした結果をdiffに流しこめば良い(元ネタ):

$ ssh {remote_host} cat {remote_file} | diff {local_file} -

リモートホストとローカルホストが同じ構成の場合(開発環境とステージング環境など)には、次のようなスクリプトを組むともっと便利になる。

上記のコードをパスの通る場所にrdとして保存して実行権限を付加して、次のように叩くとローカルホストとremote_hostとで/path/to/somewhere/foo/bar.rbの差分を比較できる。

$ cd /path/to/somewhere
$ rd foo/bar.rb remote_host

個人的な好みにより-uオプションをつけてる。Usageとか書くべきなんだろうけど、とりあえず。

いやまぁ直接じゃなくてgitとかでバージョン管理して見ろよって話ではあるんだけど、そこはまぁほら。


Herokuの手始め

毎回Herokuを使ってみようと思うたびに手順を忘れるのでメモ。

アプリケーションの準備

hello_herokuというRailsアプリケーションを作ることにする。 Herokuの標準のDBがPostgreSQLなので今回もそれを指定する。 Test::UnitとPrototype.jsは要らないので-JTする。

$ rails -v #=> 3.0.9
$ rails new hello_heroku -JT -d postgresql
$ cd hello_heroku

いつもどおりgitリポジトリを作る

$ git init
$ git add .
$ git commit -am "initial commit"

専用のRVM gemsetも作っておく。

$ rvm use 1.9.2@hello_heroku --create
$ echo "rvm use 1.9.2@hello_heroku" > .rvmrc

Gemfileにpgとherokuを追加してインストール。 (heroku gemは:require => falseでもいいのかな?)

$ vim Gemfile
+ gem 'pg'
+ gem 'heroku'
$ bundle
$ git add .
$ git commit -am "add pg and heroku gems"

念のためGitHubに上げておく。 (hubコマンドが便利なので、alias git=hubしてある。)

$ git create hello_heroku
$ git push -u origin master

リポジトリはa2ikm/hello_herokuにある。

Herokuにアップロードする

とりあえずアプリケーションができたので、Herokuにアプリケーションを新規に登録してアップロードする。

$ heroku create hello-heroku
$ git push -u heroku master

アプリケーション名が重複していなければ、これで http://hello-heroku.heroku.com で動いていることが確認できる。

あとはいつもどおり機能を実装して、git pushしてアップロードすればok。

まとめ

使いたいと思いつつも結局使っていないHeroku。SSLが無料だったりするので、うーん、なんか作りたい。


varnishはQUERY_STRINGも含めてキャッシュする

今動いているサービスが

(INTERNET)-[varnish]-[Starman/Catalyst]

というような構成でvarnishでCSSや画像なんかの静的ファイルをキャッシュしているんだけど、 現状デプロイしたけどキャッシュが更新されなくて見えてる画像が新しくなってないよって現象がある。 一応varnishを再起動すればキャッシュはクリアされるけど、 一つのvarnishの下に複数のサービスがぶら下がっているので、 一つのサービスのデプロイのためにそれ以外の全てのサービスのキャッシュをクリアするのも大変微妙。

で、そういえば、先日Railsのimage_tagを真似て作ってみたMyApp::View::Plugin::Tsを使って画像のURLなどに更新時刻を付加すれば、 varnishがそこをみて新しくキャッシュしてくれるようになるんじゃないか?と思った。

そのためにはvarnishがパスだけでなくてQUERY_STRINGも含めて (例えばfoo.jpgとfoo.jpg?123456を別物と見て)キャッシュしてくれている必要がある。

というわけで、今回はそれを確認してみた。

varnishのインストールと設定、起動

インストールはbrewで一発。8/10現在、3.0.0がインストールされた。

$ brew install varnish

今回の設定ファイルを作成:

$ vim /usr/local/Cellar/varnish/3.0.0/etc/varnish/cache_test.vcl
backend cache_test {
  .host = "127.0.0.1";
  .port = "3000";
}

# リクエストを受け付けると必ず走る処理
sub vcl_recv {
  if (req.request != "GET" && req.request != "HEAD") {
    return(pipe); # このトランザクションではこれ以降キャッシュを利用せずにAppサーバに繋ぎに行く
  }
  if (req.http.Cache-Control ~ "no-cache") {
    return(pass); # キャッシュを利用せずにAppサーバに繋ぎに行く
  }
  return(lookup); # キャッシュの中から利用できるものがあれば利用する
}

# Appサーバに繋ぎに行くときに走る処理
sub vcl_fetch {
  if (beresp.status == 500) {
    set beresp.saintmode = 10s;
    return(restart);
  }
  set beresp.grace = 5m;
  if (req.url ~ "\.(png|gif|jpg|css|js|ico)$") {
    unset beresp.http.set-cookie;
    set beresp.ttl = 3600s;
  }
}

上記の設定ファイルを読みこむよう設定に追記:

# 設定ファイルの実体は /usr/local/Cellar/varnish/3.0.0/etc/varnish/default.vcl
$ vim /usr/local/etc/varnish/default.vcl
include '/usr/local/Cellar/varnish/3.0.0/etc/varnish/cache_test.vcl';

varnishを起動する。

$ varnishd -a 127.0.0.1:8000 -f /usr/local/Cellar/varnish/3.0.0/etc/varnish/cache_test.vcl

これで http://127.0.0.1:8000 にアクセスするとvarnishを経由して http://127.0.0.1:3000 にアクセスするようになった。

実験

適当なファイルを配信するサーバを立てる。 ここで使ってるwebrickコマンドは、適当に書いたwebrickサーバを立てるスクリプト。 HTTPサーバであればなんでもいい。

$ mkdir -p ~/dev/sample/varnish
$ cd ~/dev/sample/varnish
$ cp ~/Pictures/foo.jpg .
$ webrick -p 3000

そして、このwebrickサーバで配信している foo.jpg にアクセスしたときにvarnishがキャッシュするのか、 つまりwebrickサーバにアクセスが行くのかどうかを確認してみた。

# webrick 直でアクセスするとログは流れる
$ open http://localhost:3000/foo.jpg

# varnish 経由でアクセス
# 画像はキャッシュされていないのでwebrickのログが流れる
$ open http://localhost:8000/foo.jpg

# varnish 経由でアクセス、2回目
# 画像はキャッシュされているのでwebrickのログは流れない
$ open http://localhost:8000/foo.jpg

# varnish 経由でアクセス、QUERY STRING付き
# 先ほど/foo.jpgはキャッシュされたけど、/foo.jpg?123456はキャッシュされていないのでwebrickのログは流れる
$ open http://localhost:8000/foo.jpg?123456

# varnish 経由でアクセス、QUERY STRING付き、2回目
# 直前のアクセスで/foo.jpg?123456はキャッシュされたのでwebrickのログは流れない
$ open http://localhost:8000/foo.jpg?123456

まとめ

varnishはQUERY_STRING付きでキャッシュしてくれているので、 画像やCSSなどの静的ファイルは更新日時などを付加してリンクすれば適宜キャッシュしなおされる。 端末側も新しくキャッシュしてくれる(はずな)ので常に最新のファイルを読むようになる。

でも、-M演算子やFile.mtimeのコストってどうなんだろう? Appサーバにリクエストが行かなくなるだけマシになるのかな?

参考資料


Template::Toolkitでファイルのタイムスタンプを付加

Railsだとimage_tagを使えば画像ファイルの更新日時をくっつけて

<img src="foo.jpg?1234567890" />

みたいにしてくれて、画像が更新されればブラウザは画像をキャッシュしていても新しくリクエストしてくれたりする。で、Catalyst/Template::Toolkitだとどうやるんだろう?ってことで書いてみた。

これをlib/MyApp/Views/Pluginに置いて、MyApp::View::TTとかに書かれているPACKAGE->configに

PLUGIN_BASE => 'TclandSp::View::Plugin',

を追加して各テンプレートで

[% USE Ts %]

すれば、例えば

[% Ts.ts('/static/images/logo.jpg') %]

/static/images/logo.jpg?1234567890

になる。これをimgタグと組み合わせて

<img src="[% Ts.ts('/static/images/logo.jpg') %]" />

とかするといい。

以下メモ:

  • -M演算子はperlのインタプリタが起動してからの相対日数を返すので、606024=86400をかけて、さらにインタプリタの起動した時刻$^Tを足し合わせることで絶対時間が得られる
  • MyAppが入っちゃうのが残念。うまく回避したい。
  • imgタグやlinkタグもまとめて出力してくれるメソッドも定義したけど、altやwidth、heightなんかも指定したくなったときに不便だなと思ってもっぱらTs.tsだけ使ってる。
  • 複数テンプレートで共通で使うプラグインはPRE_PROCESSで呼んでるテンプレート中でまとめてUSEすると便利。

やっぱりフルスタックのRailsはデカいけど便利だ。


ActiveRecordとMongoidは共存できる

第5回 MongoDB.jp 勉強会 in Tokyoに行ってきた! メモとかはまたのちほどアップするとして(そういえばRubyKaigi最終日のメモもアップできていない)、@yuki24 さんの発表で「ActiveRecordとMongoidは共存」できるという話を聞いて、ちょっと試してみた。

その過程で適当に作ったコードをgithubに上げておく。Todoを作成・更新した際にその内容をTodoLogに吐き出すというモノ。

mongoidのインストール

これは普通に。

$ gem install bson_ext mongoid
$ rails g mongoid:config

Mongoidのモデルの作成

mongoidをインストールするとmodelジェネレータがmongoidのモデルファイルを生成してくれる。

$ rails g model todo_log title:string done:boolean 
      invoke  mongoid
      create    app/models/todo_log.rb

ActiveRecordのモデルの作成

modelジェネレータがmongoidに割り振られたので、代わりにactive_record:modelジェネレータを使う。

$ rails g active_record:model todo title:string done:boolean
      create  db/migrate/20110730071022_create_todos.rb
      create  app/models/todo.rb

これはrails gコマンドの出力を見て初めて気づいた。

アプリでやってること

Todoのafter_create、after_updateコールバックにTodoLog.createを追加すれば、TodoLが作成・更新された際にTodoLogが作成される。

最後に

やってみれば当たり前なんだけどちゃんと動く。 mongoid入れただけでmodelがそれ用のジェネレータになっていたのでできないものだと思ってた。 調べてみるってとても大事。

あとスキーマレスは便利。ついさっき、「TodoLogなんだからtodo_id入れなきゃな」と気づいたのでコードをちょっとだけ修正したのだけれど、それだけでtodo_idが保存されるようになった。凄く手軽。 もちろん後から追加したんだから、それ以前のレコードにはtodo_idは保存されていないんだけど。