akishin999の日記

調べた事などを書いて行きます。

CentOS 6.4 で Docker 0.7 を使ってみる

Docker がバージョン 0.7 から RedHat 系 OS に正式に対応した、という事なのでインストールして軽く触ってみました。

Requirements and Installation on Red Hat Enterprise Linux / CentOS - Docker Documentation
http://docs.docker.io/en/latest/installation/rhel/

試した OS は手元にあった CentOS 6.4 x86_64 です。

インストールといっても公式の手順にもある通り EPEL で入れられるようになったので、yum リポジトリを追加して入れるだけです。
まずは EPEL リポジトリを使えるようにします。

# rpm --import http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6
# yum install -y http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

EPEL が使えるようになったら yum で docker-io パッケージをインストール。

# yum install -y docker-io

これだけで他は特に何もしなくても Docker が使えるようになりました。
以前試した時は CentOS へ入れようとするとかなり大変だったので、大分楽になりましたね。
ありがたい事です。

せっかくインストールしたのでちょっと使ってみます。
インストールしたらまずはデーモンを起動。

# service docker start

取りあえず Redis をインストールする Dockerfile を作ってみました。

  • Dockerfile
FROM centos

RUN rpm --import http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6
RUN rpm --import http://rpms.famillecollet.com/RPM-GPG-KEY-remi
RUN yum install -y http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
RUN yum install -y http://rpms.famillecollet.com/enterprise/6/remi/x86_64/remi-release-6.4-1.el6.remi.noarch.rpm
RUN yum install -y --enablerepo=remi redis

RUN echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf
RUN /sbin/sysctl -p

CMD ["/usr/sbin/redis-server"]

保存したらビルドします。
Dockerfile と同じディレクトリで以下を実行。

# docker build -t akishin/redis .

初回は Docker のリポジトリから CentOS のイメージをダウンロードするのでちょっと時間がかかります。
実行が終わったら docker images を実行すると以下のようにイメージが作成された事が分かります。

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
akishin/redis       latest              24584d69487e        22 seconds ago      355.4 MB (virtual 1.11 GB)
centos              6.4                 539c0211cd76        8 months ago        300.6 MB (virtual 300.6 MB)
centos              latest              539c0211cd76        8 months ago        300.6 MB (virtual 300.6 MB)

作成したイメージを起動してみます。

# docker run -d akishin/redis

ちょっと冗長ですが、以下のコマンドを実行するとコンテナの IP アドレスが分かります。

# docker inspect $(docker ps -q) | grep IPAddress
        "IPAddress": "172.17.0.17",

Redis は Telnet で操作できるので、先ほど調べた IPアドレスで接続して試しに使ってみます。

# telnet 172.17.0.17 6379
Trying 172.17.0.17...
Connected to 172.17.0.17.
Escape character is '^]'.

SET foo bar
+OK
GET foo
$3
bar
quit
+OK
Connection closed by foreign host.

問題無さそうですね。

ただ、このままだと Docker を動かしているホストからしか Redis を使用することが出来ません。
他のマシンから接続するためには、コンテナのポートをホストのポートにマッピングする必要があります。

公式ドキュメントでは以下の辺り。

Port redirection - Docker Documentation
http://docs.docker.io/en/latest/use/port_redirection/

まずは先ほどのコンテナを停止します。

# docker stop $(docker ps -q)

今度は docker run 時に -p でポートを指定します。
以下の指定ではコンテナの 6379 ポートをホストの同じポートにマッピングしています。

# docker run -d -p 6379:6379 akishin/redis

netstat で見てみると今度はホスト側でもポート 6379 が LISTEN されていることが分かります。

# netstat -tln | grep 6379
tcp        0      0 :::6379                     :::*                        LISTEN

これで「ホストのIPアドレス:6379」で外部のマシンから接続できるはず・・・と思ったら以下のようなエラーとなってしまい、接続できません。

% telnet 192.0.2.3 6379
Trying 192.0.2.3...
telnet: Unable to connect to remote host: No route to host

どうやらこれはファイアウォールにブロックされてしまっている事が原因のようです。
学習用環境なので iptables を止めてしまえばいいかとも思ったのですが、docker では iptables に以下のように NAT 設定が追加されていました。

# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere             anywhere            ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16       !172.17.0.0/16

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere            !loopback/8          ADDRTYPE match dst-type LOCAL

Chain DOCKER (2 references)
target     prot opt source               destination
DNAT       tcp  --  anywhere             anywhere            tcp dpt:6379 to:172.17.0.18:6379

これだと iptables は止めちゃ駄目っぽいですね。
という訳で、FORWARD チェインに以下のようなルールを追加してみます。

# iptables -I FORWARD -p tcp --dport 6379 -j ACCEPT

再度別のマシンからアクセス。

% telnet 192.0.2.3 6379
Trying 192.0.2.3...
Connected to 192.0.2.3.
Escape character is '^]'.
set hoge fuga
+OK
get hoge
$4
fuga
quit
+OK
Connection closed by foreign host.

今度は上手く行きました!
これで開発時に Redis サーバが欲しくなったらイメージからサクッと起動できますね。

普段開発で使っている Ubuntu がまだ 32bit という事もあって Docker は使ってなかったのですが、CentOS でこれだけ簡単に使えるようになると今後は活用していきたいところ。
取りあえずドキュメント読みながら触ってるところですが、いまいち EXPOSE の使いどころがよく分からず・・・。

CentOS 6.5 へアップグレード

CentOS 6.5 が出たみたいです。

RHEL 6.5互換となるLinuxディストリビューションCentOS 6.5」リリース | SourceForge.JP Magazine
http://sourceforge.jp/magazine/13/12/04/143000

Manuals/ReleaseNotes/CentOS6.5 - CentOS Wiki
http://wiki.centos.org/Manuals/ReleaseNotes/CentOS6.5#head-d5e02cc120fd27486bb7812c28802604b89e1943

というわけで手元の環境をアップグレードしました。
いつもの通り重要なファイルのみバックアップしてからこんな感じで。

# yum clean all
# yum -y upgrade

しばらく待って、終わったら再起動。

# reboot

バージョンを確認。

# cat /etc/redhat-release
CentOS release 6.5 (Final)
# uname -a
Linux exp01 2.6.32-358.el6.x86_64 #1 SMP Fri Feb 22 00:31:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

これで 6.5 になりました。

RPM で直接新しいパッケージを入れている場合、yum upgrade の所でエラーになる事があるみたいです。
自分の場合は取りあえず /etc/yum.conf で exclude したらアップデートできました。

参考

CentOS 6.5 Has Been Officially Released! | Unixmen
http://www.unixmen.com/centos-6-5-officially-released/

Upgrade to CentOS 6.5 from CentOS 6.4/6.3/6.2/6.1/6.0
http://www.if-not-true-then-false.com/2011/upgrade-centos-6/

ファイルやディレクトリのパーミッションを8進数表記で確認する

ファイルやディレクトリのパーミッションを確認したい場合、ls コマンドではアルファベットでしか確認できませんが、stat コマンドを使う事で8進数表記でパーミッションを確認することが出来ます。

% stat -c %a /home/akishin
755
% stat -c %a /tmp
1777

自分も serverspec の結果を見ていて最近知りました。
パーミッションを数値で指定する必要がある場合など、とっさに出て来なかったりする事がわりとよくあるので嬉しいです。

ちなみに、パーミッション繋がりという事で、以下の「Unix Permissions Calculator」というサービスも結構便利に使わせて頂いてます。

Unix Permissions by Project Fondue
http://permissions-calculator.org/

本当はこれくらいちゃんと覚えてパパッと暗算できるようになればいいんでしょうけど・・・。

後置の rescue では例外クラスを指定できない

タイトルの通りなんですが、知らなくてちょっと悩んだので忘れないようにメモ。

require 時の LoadError を無視したくて以下のようにしたところ、何度試しても rescue 出来ませんでした。

require 'hoge' rescue LoadError
#=> cannot load such file -- hoge (LoadError)

当たり前ですが以下のようにすれば問題ありません。

begin
  require 'hoge'
rescue LoadError
end

しばらく試行錯誤した末、ドキュメントをよくよく探してみると以下のように書いてあるのを発見。

捕捉する例外クラスを指定することはできません。 (つまり、StandardError 例外クラスのサブクラスだけしか捕捉できません)

http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fcontrol.html#begin

なるほど。
Ruby の rescue の後置記法(rescue 修飾子)って例外クラスを指定できないんですね。
以下のような書き方が特にエラーにならなかったので、普通に例外クラスは指定できているものだとばかり思っていましたが、どうやら間違いだったようです。

nil.hello rescue NoMethodError

どうしても一行で書きたい場合は以下のようにするしかないようです。

begin; require 'hoge'; rescue LoadError; end

同じキーワードなので動作も同じと勝手に思い込んでしまっていました。
こういう事もあるんですね。
気をつけねば。

公式の yum リポジトリから MySQL をインストール

MySQL yum repositories | MySQL Server Blog
http://mysqlserverteam.com/mysql-yum-repositories/

こちらの記事で公式の yum リポジトリが公開されたのを見かけたので早速使ってみました。
OS は CentOS 6.4 x86_64 です。

リポジトリの追加

まずは yum リポジトリを追加します。

以下から環境に合わせた rpm をダウンロード。

MySQL :: Download MySQL Yum Repository
http://dev.mysql.com/downloads/repo/

Red Hat/Oracle Enterprise Linux 6 用、Fedora 18 用、Fedora 19 用のリンクがありました。
ここでは直接 yum に URL を指定してインストール。

# yum install -y http://repo.mysql.com/mysql-community-release-el6-3.noarch.rpm

以下のリポジトリ設定が追加されました。

# cat /etc/yum.repos.d/mysql-community.repo
[mysql-community]
name=MySQL Community Server
baseurl=http://repo.mysql.com/yum/mysql-community/el/6/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

[mysql-community-src]
name=MySQL Community Server - Source
baseurl=http://repo.mysql.com/yum/mysql-community/el/6/SRPMS
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

インストール

それでは早速インストール。
パッケージ名は「mysql-community-server」を指定します。

# yum install mysql-community-server

mysql-community-server をインストールすると以下の mysql* パッケージがインストールされました。

chkconfig で確認すると service 名は mysqld となっています。

# chkconfig --list | grep mysql
mysqld          0:off   1:off   2:off   3:off   4:off   5:off   6:off

起動してみます。

# service mysqld start

初回のみいろいろと表示されますが取りあえず問題は無さそう。
無事起動できたので接続してみます。
ちなみに root / パスワード無し がデフォルトの接続情報でした。

# mysql -uroot -p

バージョンを確認。

mysql> \s
--------------
mysql  Ver 14.14 Distrib 5.6.14, for Linux (x86_64) using  EditLine wrapper

Connection id:          2
Current database:
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.6.14 MySQL Community Server (GPL)
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 2 min 1 sec

Threads: 1  Questions: 7  Slow queries: 0  Opens: 67  Flush tables: 1  Open tables: 60  Queries per second avg: 0.057
--------------

5.6.14 がインストールされたようです。

以前 RPM で入れた時と比べると、yum を使う事で依存関係を自動的に解決してくれるので、 少しは簡単になった、かな?

個人的にはマジ環境だと検証用にどうせ構築時点の RPM ファイルを保存しておいたりするので、RPM でもあまり変わらないような気がします。
むしろ client だけ入れたり libs だけ入れたり出来るから RPM のが便利かも。

でもまぁささっと環境作りたい時はやっぱり yum のが便利かなー。

Vim で英語を書く時に便利なプラグインいくつか

情けない事にいつまで経っても英語は苦手なままなのですが、プログラマたるもの、自分のプログラム内できちんとした名前を付けるためには避けて通る事は出来ません。
とはいえ、毎回毎回 Web で検索するのにも疲れてきたので、 Vim で英語を書いたり調べたりするのに便利そうなプラグインを探してインストール、設定してみました。

ref.vim

まずは Web 上の辞書サイトなどを Vim から引けるようにするため、ref.vim を導入します。

thinca/vim-ref
https://github.com/thinca/vim-ref

Web 上の辞書を引くためには ref.vim に含まれる ref-webdict を使うのですが、そのためにはテキストブラウザが必要になります。
ref-webdict では以下のテキストブラウザに対応しているようです。

ここでは lynx をインストールしました。
OS は Ubuntu なので以下を実行。

% sudo apt-get install lynx

インストールが完了したら設定ファイルを編集します。

% sudo vi /etc/lynx-cur/lynx.cfg

日本語環境で使うので、以下のように変更します。

・
・
・
#CHARACTER_SET:iso-8859-1
CHARACTER_SET:utf-8
・
・
・
#PREFERRED_LANGUAGE:en
PREFERRED_LANGUAGE:ja
・
・
・

これで ref-webdict を使用する準備が出来たので、いよいよ vim プラグインの方を導入します。
NeoBundle を使っているので、以下のように .vimrc に記述しました。

NeoBundle 'thinca/vim-ref'
NeoBundle 'mfumi/ref-dicts-en'
NeoBundle 'tyru/vim-altercmd'

英英辞書を検索するための ref-dicts-en と Vim コマンドに短いエイリアスを付けられる vim-altercmd も併せて導入しています。
保存したら再読み込みして :NeoBundleInstall を実行すればインストールは完了。
インストールが終わったら ref-webdict 用に辞書サイトの設定を行います。

ここでは .vimrc に以下のような設定を入れました。
辞書定義は karakaram-blog さんの記事で紹介されていたものをそのまま使わせて頂きました。

" vim-ref のバッファを q で閉じられるようにする
autocmd FileType ref-* nnoremap <buffer> <silent> q :<C-u>close<CR>

" 辞書定義
let g:ref_source_webdict_sites = {
\   'je': {
\     'url': 'http://dictionary.infoseek.ne.jp/jeword/%s',
\   },
\   'ej': {
\     'url': 'http://dictionary.infoseek.ne.jp/ejword/%s',
\   },
\ }

" デフォルトサイト
let g:ref_source_webdict_sites.default = 'ej'

" 出力に対するフィルタ
" 最初の数行を削除
function! g:ref_source_webdict_sites.je.filter(output)
  return join(split(a:output, "\n")[15 :], "\n")
endfunction

function! g:ref_source_webdict_sites.ej.filter(output)
  return join(split(a:output, "\n")[15 :], "\n")
endfunction

このままでも便利なのですが、 webdict は使用する時に「:Ref webdict ej 英単語」のようにタイプしなければならないのでちょっと面倒くさいですね。
という事でもう少し短いコマンドで使用できるようにするために以下のような vim-altercmd の設定を入れています。

call altercmd#load()
CAlterCommand ej Ref webdict ej
CAlterCommand je Ref webdict je

これでコマンドモードで「:ej 英単語」のように入力すれば「:Ref webdict ej 英単語」に展開され、Infoseekマルチ辞書を検索できるようになりました。
同様に webdict の設定を追加することで、他の辞書サイトを追加出来ます。

但し、alc のように Web ブラウザからの利用以外を禁止しているサイトもあるようなので、利用規約には十分注意して下さい。

英辞郎 on the WEB の利用規約とか - 永遠に未完成
http://d.hatena.ne.jp/thinca/20120515/1337017232

また、同時にインストールした ref ソース「mfumi/ref-dicts-en」により、以下の英英辞書なども検索できるようになっています。

これらは ref ソースとして追加されているので、「:Ref answers 英単語」のようにして検索できます。
タイプ量が気になる場合は、これらのソースについても vim-altercmd の設定を追加すると良いと思います。

excitetranslate-vim

excitetranslate-vim は、バッファ上もしくはビジュアルモードで選択した範囲の文字列を Excite翻訳を使って翻訳する事ができるようになる Vim プラグインです。

mattn/excitetranslate-vim
https://github.com/mattn/excitetranslate-vim

以下を .vimrc に記述して :NeoBundleInstall 実行。

NeoBundle 'mattn/webapi-vim'
NeoBundle 'mattn/excitetranslate-vim'

これで :ExciteTranslate を実行するとバッファ上のテキストが Excite 翻訳で翻訳されるようになりました。
ちょっとタイプ量が多いのでキーマップを定義するか、先ほどと同様 vim-altercmd の設定を入れると便利です。

また、実行するとウインドウが分割されるので、以下のように q で開いたバッファを閉じれるように設定しています。

" 開いたバッファを q で閉じれるようにする
autocmd BufEnter ==Translate==\ Excite nnoremap <buffer> <silent> q :<C-u>close<CR>

英和、和英の判定を自動で行ってくれたりと非常に便利です。

neco-look

neco-look は neocomplcache または neocomplete のプラグインで、英単語を入力した際に look コマンドの結果で補完を表示してくれます。
(neocomplcache または neocomplete はインストール、設定されているものとします。)

ujihisa/neco-look
https://github.com/ujihisa/neco-look

Ubuntu であれば恐らく最初から look コマンドと辞書ファイルがインストールされています。
というか、私の環境では気付いたら入っていました。
パッケージ的には bsdmainutils のようです。

インストールされている場合、ターミナルで実行すると以下のような結果が得られると思います。

% look program                                                                                                                      (git)-[master] -
program
program's
programed
programer
programer's
programers
programing
programmable
programmable's
programmables
programmed
programmer
programmer's
programmers
programming
programming's
programs

こんな感じで look が使えれば準備は OK。
以下を .vimrc に記述して :NeoBundleInstall を実行します。

NeoBundle 'ujihisa/neco-look'

これだけで適当な英単語をタイプすると以下のように補完候補が表示されるようになります。


他にもあるかもですが、取りあえず調べた範囲で便利そうなプラグインを一通り入れてみました。
Vim にはスペルチェック機能もありますし、これで変数名やメソッド名を考えるのも大分楽になりますね(?)。

参考サイト

vim-refを導入する [2012年版] | karakaram-blog
http://www.karakaram.com/20120726-vim-ref

vim-refのwebdictで英和・和英辞書を参照する | karakaram-blog
http://www.karakaram.com/ref-webdict

ref.vimから英英を引く - mfumiの日記
http://d.hatena.ne.jp/mFumi/20111229/1325145325

vimでスペルを確認する - まる-おぶ-ざ-でい
http://d.hatena.ne.jp/wiredool/20121212/1355315368

Vimで英文をうちこむときにはかどりそうなプラグインと設定 - Qiita [キータ]
http://qiita.com/hatchinee/items/960c3222dc0c5605a169

FakeFS でファイルアクセスをテストする

ファイルシステムの操作に関連するクラスのモックオブジェクトを提供するライブラリ「FakeFS」を使ってみたので、使い方をメモしておきます。

defunkt/fakefs
https://github.com/defunkt/fakefs

インストール

% gem install fakefs --no-rdoc --no-ri

Rails を使う場合は Gemfile に以下のように追加します。

group :test do
  gem 'fakefs', :require => "fakefs/safe"
end

使ってみる

FakeFS を require すると以下のクラスが FakeFS が提供するクラスに差替えられます。

  • File
  • FileTest
  • FileUtils
  • Dir
  • Pathname

そのため、これらのクラスを使用してファイルシステムにアクセスするコードを、実際のファイルシステムに変更を加える事なくテストする事ができます。

まずは「fakefs」を require して使ってみます。

# -*- coding: utf-8 -*-

require 'fakefs'

TEST_DIR = '/temp/example'

# ディレクトリの作成
# 全てのパスがモック化されているため、中間パスが実在する場合でも「Errno::ENOENT」になる
# そのためフルパスで指定する場合は mkdir_p などの中間パスも作成してくれるメソッドを使う
p FileUtils.mkdir_p TEST_DIR
#=> ["/temp/example"]

# ディレクトリの削除
# この行が無くても実際のパスにディレクトリは作成されない
# また、削除自体もエラーにはならず成功する
p Dir.rmdir TEST_DIR
#=> true

「require 'fakefs'」した場合、FakeFS はその時点でファイルシステム関連のクラスを差替えてしまいます。
使用しているコード内(ライブラリ内も含む)でのあらゆるファイルアクセスをモック化して問題ない場合以外では、この挙動は少々微妙かも知れません。

そういった場合には「fakefs/safe」の方を require します。
「fakefs/safe」を require した場合のサンプルコードは以下のようになります。

# -*- coding: utf-8 -*-

require 'fakefs/safe'

TEST_DIR  = '/temp/'
TEST_FILE = '/temp/example.txt'

# FakeFS 有効
FakeFS.activate!

# ファイルシステムを操作する処理
Dir::mkdir TEST_DIR
File.open(TEST_FILE, 'w') { |f| f.write 'Hello, World.' }

# FakeFS 無効
FakeFS.deactivate!

上記のように、明示的に FakeFS.activate! を呼ばない限り FakeFS が有効になりません。
また、 FakeFS.deactivate! を呼ぶ事で通常のファイルアクセスが可能になるよう、挙動を戻す事も出来ます。

他にも、以下のようにブロックを渡すと、ブロック内でのみ FakeFS が有効になります。

# -*- coding: utf-8 -*-

require 'fakefs/safe'

TEST_DIR  = '/temp/'
TEST_FILE = '/temp/example.txt'

FakeFS do
  Dir::mkdir TEST_DIR
  File.open(TEST_FILE, 'w') { |f| f.write 'Hello, World.' }
end

こんな感じでとても簡単に使用する事ができるので、既存のテストコードに導入するのも容易だと思います。

RSpec で使う

FakeFS では RSpec と一緒に使うための spec_helpers.rb というファイルも提供されています。
簡単に試してみるため、まずは RSpec を使ったサンプルプロジェクトを用意してみました。

ファイル配置は以下のような感じ。

exampleapp
 ├── Rakefile
 ├── example.rb
 └── spec
     ├── example_spec.rb
     └── spec_helper.rb

Rakefile と spec_helper.rb は以下のような内容で用意しました。

# -*- coding: utf-8 -*-
require "rspec/core/rake_task"

desc 'rake spec'
task :default => [:spec]

RSpec::Core::RakeTask.new(:spec) do |spec|
  spec.pattern = 'spec/*_spec.rb'
  spec.rspec_opts = ['-cfd --backtrace']
end
  • spec_helper.rb
# -*- coding: utf-8 -*-
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'rspec'

RSpec.configure do |config|
end

次にテスト対象クラスです。
若干無理矢理感溢れてますが、適当なものを思いつかなかった為、テスト対象とする Example クラスは以下のような内容とします。

  • example.rb
# -*- coding: utf-8 -*-

class Example
  LOG_FILE = 'example.log'

  def self.create_dir(dir)
    FileUtils::mkdir_p dir
  end

  def self.log(message)
    File.open(LOG_FILE, 'w') { |f| f.write message }
  end
end

spec はこんな感じで適当に。

  • example_spec.rb
# -*- coding: utf-8 -*-
require_relative 'spec_helper'
require_relative '../example'

describe Example do
  describe ".create_dir" do
    subject { Example }
    before  { 
      subject.create_dir("testdir")
    }

    it "ディレクトリが作成されること" do
      expect(File.exists? "testdir").to be_true
    end
  end

  describe ".log" do
    subject { Example }
    before  { 
      subject.log("Hello, World.")
    }

    it "ログファイルが作成されること" do
      expect(File.exists? Example::LOG_FILE).to be_true
    end
  end
end

ここで試しに rake spec してみると、カレントディレクトリ内に「testdir」ディレクトリと「example.log」というファイルが作成されてしまいます。
テストの度にこういったファイルやディレクトリが作成されてしまうのは煩わしいですよね。

という訳で、毎回後始末をするのも面倒なので FakeFS の出番になります。
example_spec.rb に以下のように二行追加します。

  • example_spec.rb
# -*- coding: utf-8 -*-
require_relative 'spec_helper'
require 'fakefs/spec_helpers'    # <= 追加
require_relative '../example'

describe Example do
  include FakeFS::SpecHelpers    # <= 追加
  describe ".create_dir" do
    subject { Example }
    before  { 
      subject.create_dir("testdir")
    }

    it "ディレクトリが作成されること" do
      expect(File.exists? "testdir").to be_true
    end
  end

  describe ".log" do
    subject { Example }
    before  { 
      subject.log("Hello, World.")
    }

    it "ログファイルが作成されること" do
      expect(File.exists? Example::LOG_FILE).to be_true
    end
  end
end

これでこの spec 内で FakeFS が有効になります。
今度はテストを実行してもディレクトリやファイルが作成されません。

ただ、もし複数の spec ファイルでファイルシステムを扱っているような場合、全部のファイルで上の二行を追加するのは面倒です。
そういった場合は、以下のように spec_helper.rb に記述する事で、全ての spec ファイルで FakeFS を有効にすることができます。

  • spec_helper.rb
# -*- coding: utf-8 -*-
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'rspec'
require 'fakefs/spec_helpers'           # <= 追加

RSpec.configure do |config|
  config.include FakeFS::SpecHelpers    # <= 追加
end

ファイルシステムを操作するようなクラスのテストは結構面倒だと思っていたのですが、FakeFS を使うと余り色々な事を気にせずにテストが書けるのでいい感じですね。

参考

Ruby - FakefsでTest-Drivenな運用スクリプト - Qiita [キータ]
http://qiita.com/sawanoboly/items/de2b14779796179ef0d1