Mitaka.rbでコラボレーションの真髄を垣間見た!

『WEBエンジニア meets デザイナー』と題した第9回Mitaka.rbに参加してきました。
今回はリトルスター・レストランに30名強のWEBエンジニアと数名のデザイナさんが集まり、プレゼンを聞き、フルボッコにされ、食事をし、楽しい時間を過ごしました。

最初に主催者の榊さん(@ysakaki)からMitaka.rbの紹介と進行の説明があり、すぐに前半のプレゼンが始まりました。

デザイナ様にフルボッコにされる

最初はあやさんによる『@ayaxxのお望みどおりみんなまとめてふるぼっこにしてやんよ』。
会場のエンジニアが事前に提出した名刺やネットショップ、ウェブサービスのデザインをあやさんがめった切りにしますw
指摘されるのは、不揃いなフォントや色使い、ポリシーのないマージン、意味不明な空白、など。
全て、デザイナでない人が見ても不自然に感じるはずなんですが、エンジニアはそれを言葉にできるほどはっきり認識できなかったり、認識しててもどう直していいのか分からないのかと思われます。
よってエンジニアとしては、自分のデザインが不自然だと感じたら放っておかず、観察して理由を考えたり、誰かにアドバイスを求めるのが大切でしょうね。

厳しいツッコミを入れるだけでなく、最後に改善案を見せてくれる@ayaxxさんからは愛情とプロフェッショナル魂が感じられました。

みんなで基礎を学ぶ

次は黒野明子さん(@crema)による『デザイナーでない人のためのレイアウト入門』。
課長からセミナーのチラシのデザインを強要された”ほげ山君”が、ただの情報の羅列からそれなりのデザインを完成させていくというストーリーで、レイアウトの基本である4つの法則(近接の法則、整列の法則、対比の法則、反復の法則)を説明してくださいました。
この辺は僕も勉強して、実践したこともあるのですが、実際デザイナ様がダメなレイアウトに法則を一つ適用するといきなり見違える様子は、未だに謎を含んでいますw
自分で実践してもあんなにはっきりビフォーアフターが違いません!

やはり法則は法則でしかなく、”法則に則って何を何ピクセル動かすか”は、熟練を必要とする別の技術なのだと思います。
あと分からなかったのは、反復の法則の目的とか、使うべきところ、使わないところ、具体的にどうすればいいのか…。今も言葉にできないもやもやが残っていますが、そこまで説明していたら時間が足りないでしょうね。
ノンデザイナーズ・デザインブック』という本を紹介していただいたので、勉強してみようと思います。

歓談、そして怒涛のラストへ

くれまさんの講義のあと乾杯があり、食事が始まりました。リトルスターさん、料理は美味しいです。人と話すのに忙しくてゆっくり味わえなかったのが残念です。

3番目のプレゼンは、貴田さんによる、Railsで作られた自治体向けCMSの紹介。白状すると、この辺は僕がぼんやりしていてよく覚えていません…ごめんなさい。
でもユーザ層が考慮され、動線に配慮した作りになっていることは分かりました。

最後は橋本さん(@hsmt)による『僕的グループ・ジーニアスについて』。ATNDを見てタイトルを書き入れてますが、本当にこのタイトルだったか覚えていません。
というか、内容は想像を絶するものでした。
まず橋本さんという人の迫力がすごい。エネルギーに満ちあふれているっ!すごいパワーと信念を感じる人です。
昔は舞台俳優さんだったそうで、またクラブでDJもやるそうで、見ているだけで雰囲気に巻き込まれてしまいます。
そしてその橋本さんがちょっと出来上がっていて、いい具合にブレーキが滑っているようでしたw
内容については、まだ僕のなかで消化できていないので上手く書けないのですが、”コラボレーション”の概念を1から考え直させられるものでした
2人の人がコラボレーションするとき、それは「1+1」なのか?
エンジニアとデザイナがウェブサイトを作るとき、「エンジニア+デザイナ」なのか?
コラボレーションで最終的に作るものは何なのか。
探求するきっかけをいただけました。
他にも特筆すべき演出があったのですが、僕の文章力と理解度ではチープになってしまうので控えます。
とにかく印象的なプレゼンでした。

最後に、主催者の榊さん、発表者のみなさん、サポートとリトルスターのスタッフのみなさん、楽しい時間をありがとうございました。

Windows上でRubyからMySQL 5.1を使う

現在Windows上のRubyMySQLを使う場合、安定性や速度の面で最適なバインディングmysql-win 2.8.1だと思われますが、リリースのアナウンスにも書かれている通り、このgemが対応するMySQLのバージョンは5.0.83です。
しかし機能や安定性など、様々な理由でMySQL 5.1を使いたい場合もあるはずです。
そこで、mysql gemを最新のソースツリーからビルドすることでMySQL 5.1に対応する方法を説明します。

必要なもの

Ruby

Ruby Installer for Windowsから適当なものをダウンロードしてインストールしてください。私は諸事情からrubyinstaller-1.8.6-p383-rc1.exeを使いました。もちろんpikで好きなバージョンをインストールしても構いません(多分)。

Development Kit

ここで言うDevelopment Kitは、ネイティブモジュールを含むgemをWindows上でビルドするためのツールです。前述のRuby Installer for Windowsにあります。
7-Zipで展開したら、中にあるINSTALL.txtに従って以下の作業をします。

  • binフォルダとdevkitフォルダをRubyのインストールフォルダへ移動
  • devkit/msys/1.0.11/etc/fstabを修正

fstabは下のような内容になっているので、「C:/Ruby」の部分を実際のRubyのインストールパスに直してやります。

C:/Ruby/devkit/gcc/3.4.5 /mingw
C:/Ruby/devkit/msys/1.0.11/usr/local /usr/local
msysgit

msysgitWindows用のGitです(Gitは分散リポジトリ型のバージョン管理ツールです)。プロジェクトのホームページから最新版をダウンロード、インストールしてください。今ならGit-1.7.0.2-preview20100309.exeです。

mysql-gemのソース

今回元にするソースは、mysql-gemという名前でLuis Lavena氏が管理、開発しているバージョンです(mysql-win 2.8.1をリードしたのも同氏です)。
gitが使えるようになったらリポジトリから取り寄せましょう。

git clone http://github.com/luislavena/mysql-gem.git

gemを作る

mysql-gemは以下のgemに依存しているので、先にインストールします。

gem install hoe rake-compiler rubyforge

rubyforge gemはセットアップが必要なので、下のように実行してやります。

rubyforge setup

セットアップが終わると”~/.rubyforge/user-config.ymlを編集しろ”というようなメッセージが出ますが、mysql-gemをビルドするだけなら必要ありません。
準備ができたら、ソースのルートへ移動して、gemを作ります。

cd mysql-gem
rake gem

pkg/mysql-2.8.2.gemができるはずです。

インストール

インストールのプロセスは、ネイティブモジュールをコンパイルしてMySQLのライブラリをリンクします。そのためにヘッダファイルとライブラリのパスを指定してやる必要があるのですが、空白を含んだパスを指定するとコンパイルは失敗します。もしMySQLをスペースを含んだパス(C:\Program Files\MySQL\MySQL Server 5.1など)にインストールしていたら、下のようにsubstコマンドを使って空白のないパスにマッピングしてやるとよいでしょう。

subst m: "C:\Program Files\MySQL\MySQL Server 5.1"

上の場合、ヘッダファイルはm:\include、ライブラリはm:\lib\optで参照できるようになります。
インストールのコマンドラインは次のようになります(includeとlib\optは適切なパスに置き換えてください)。

gem install pkg\mysql-2.8.2.gem -- --with-mysql-include=m:\include --with-mysql-lib=m:\lib\opt

動作確認

下のようなコマンドでconnectできることを確認しましょう(MySQLサーバのホスト名、ユーザ名、パスワードは適宜差し替えてください)。

ruby -rubygems -e "require 'mysql'; puts Mysql.connect('localhost', 'root', 'password').server_info"

Rails 2.3.5のウェブアプリをWindowsのexeファイルにする

ローカルアプリケーションを作るときでも、ウェブインターフェースを付けたいことがあります。しかも、データベースも使いたい。そういう場合にはRailsを使えば開発が簡単になります。

しかし、作ったローカルアプリケーションを配布しようとすると問題が起きます。Railsアプリケーションは配布するようには作られていないからです。特に、Windowsで動作させるとなるとハードルは一気に高くなります。

ここではRails 2.3.5で作成したウェブアプリケーションWindows用のexeファイルにする方法を紹介します。

Ovewview

Rubyプログラムをexeにするツールはいくつかありますが、ここではRubyScript2Exeを使います。このエントリではRubyScript2Exeの動作環境の構築から実行までの手順、躓きやすい点について説明します。

より新しいツールとしてOCRAがありますが、Railsアプリケーションの場合は正常に動作しませんでした。

動作環境の構築

msysGit

RubyScript2Exeはgithubで公開されているバージョンを使いますので、msysGit (Git on Windows)をインストールしておきます。msysGitのサイトからダウンロード、インストールしてください。

Ruby

Windows用のRubyも幾つかありますが、今回動作確認したのはRubyInstallerの1.8.6-26です(RubyInstallerのファイルリストの一番下)。最新版の1.9.1-p378では、Rubyのプロセスがクラッシュすることがありました。

RubyInstallerはその名の通りインストーラになっていますので、そのままダウンロードしてインストールしてください。

Rubygemsの更新

Rails 2.3.5はRubygems 1.3.2以降に依存していますが、RubyInstallerでインストールされるRubygemsはバージョン0.9.4なので更新する必要があります。コマンドプロンプトを起動して以下のコマンドを実行します。(0.9.4から1.3.5への更新にgem update --systemは使用できません。)

> gem install rubygems-update
> update_rubygems
Railsのインストール

この辺はMacLinuxと変わりません。

> gem install rails -v 2.3.5

ついでにSQLite3アダプタもインストールしておきます。

> gem install sqlite3-ruby

アプリケーションの作成/展開

ここまででRailsアプリケーションを動作させる環境ができましたので、新しいアプリケーションを作成するなり、MacLinuxから持ってくるなりします。

ここでは例として「hi!」と表示するアプリケーション(?)、fooを作ります。

> rails foo
> ruby script\generate controller foos

app\controllers\foos_controller.rbの内容は下の通りです。

class FoosController < ApplicationController
  def hi
    render :text => 'hi!'
  end
end

これでアプリケーションを起動し、

> ruby script\server

http://127.0.0.1:3000/foos/hiを開けば「hi!」と表示されます。

exe化

RubyScript2ExeはRubyスクリプトをexe化するツールなので、Railsアプリケーションのように複数のファイルが含まれるものには、そのままでは適用できません。そこで、同じ作者によるTar2RubyScriptという、フォルダを自己解凍Rubyスクリプト(以下アーカイブ)に変換するツールを使います。

Tar2RubyScript

オリジナルのTar2RubyScriptRubygems 1.3.1までにしか対応していないため、Ryan Bookerによって修正されたバージョンを使います。まずgitを使ってインストールします。

> git clone git://github.com/ryanbooker/tar2rubyscript.git

Windows用のtarが同梱されているので、パスの通ったフォルダへコピーします。

> copy tar2rubyscript\tar.exe C:\WINDOWS

そしてTar2RubyScript自身をアーカイブにします。

> ruby tar2rubyscript\init.rb tar2rubyscript

これでtar2rubyscript.rbというスクリプトができます。このスクリプトを使うことで他のフォルダをアーカイブできます。

ただし、フォルダをアーカイブ化する際には、そのフォルダの中で最初に実行する.rbファイルが明確になっていなければなりません。Tar2RubyScriptは、フォルダの直下にあるinit.rbを最初に実行するようになっています。つまり、Railsアプリケーションをアーカイブ化するには、いわゆるRAILS_ROOTにscript/serverを実行するinit.rbを入れておかなければなりません。

例えば、以下のようなinit.rbを入れておけばRailsアプリケーションをアーカイブ化できます(at_exitについては後述します)。

at_exit do
  require "irb"
  require "drb/acl"
  require "sqlite3"
end

load "script/server"

準備ができましたので先程のfooアプリをアーカイブ化します。

> ruby tar2rubyscript.rb foo

foo.rbができるはずです。これを実行するとシステムの一時フォルダに展開されてWebrickが起動する様子が見られます。

> ruby foo.rb

もちろんhttp://127.0.0.1:3000/foos/hiにもアクセスできます。また、-eオプションなどを渡すこともできます。

> ruby foo.rb -e production
RubyScript2Exe

オリジナルのRubyScript2ExeRubygems 1.3.1にしか対応していないので、修正されたバージョンをインストールします。

> git clone git://github.com/ryanbooker/rubyscript2exe.git

そしてアーカイブ化しておきます。

> ruby tar2rubyscript.rb rubyscript2exe

rubyscript2exe.rbができるので、foo.rbを食わせます。この時、下のようにRUBYOPTを設定します。

> set RUBYOPT=
> ruby rubyscript2exe.rb foo.rb

するとfooアプリケーションが起動します。実はRubyScript2Exeはターゲットのスクリプトを実際に実行し、ロードしたファイルを記録しておいてexeに同梱することで(出来上がるexeは自己解凍ファイルです)gemなどの外部のファイルを利用可能にします。よってここでは一度http://127.0.0.1:3000/foos/hiにアクセスするなどして、アプリケーションの実際の動作をさせておくとファイルの取りこぼしがなくなります。

あるいは、起動しただけでは読み込まれないファイルがわかっている場合には、上のinit.rbに書いたようにat_exitの中でrequireするなどの方法で読み込ませることができます。

Ctrl-Cでfooを停止するとfoo.exeが作成されます。このfoo.exeにはRuby本体からfooアプリケーション、必要なgemまで全てが含まれていますので、そのまま他のマシンへコピーして実行できます。

autotestからRailsが分離された

6月3日のことですが、ZenTest 4.1.0のリリースノートによると、autotestからRailsサポートが分離され、autotest-railsというGemになりました。

つまり、ZenTest 4.1.0以降をインストールすると、Railsのルートディレクトリで

$ autotest

としてもテストが実行されないということです(-railsを付けてもダメ)。

Railsの自動テストをするには

$ sudo gem install autotest-rails

などとして、autotest-railsをインストールしなければなりません。

ところがAutotestのRDocにはRailsサポートの分離について何も書かれておらず、autotest not recognizing tests... WHY? - Rails Forumのように混乱を招いているもよう。

ZenTestのフィードをチェックしてれば分かるだろう、というスタンスなのでしょうか…。ちょっと残念です。

foo ||= {} は foo = foo || {} じゃないってば

&&と||の自己代入式は例外です。

foo ||= {}

foo || foo = {}

です。これは、左辺が属性参照だったときに大きな違いになる可能性があります。つまり

foo.bar = foo.bar || {}

だと、foo.bar=というメソッドが常に呼ばれるのに対し、

foo.bar || foo.bar = {}

だとfoo.bar=が呼ばれない可能性があるということです。

Railsで賞を取るような方でも間違えているので気をつけましょう。

RSpecよりShoulda、fixturesよりヘルパーとMocha

RailsでBDDと言ったらRSpecが圧倒的にポピュラーですね。でもRSpecに馴染めないという人はいませんか?私はx.should == yという書き方からKernelを拡張する実装まで、何もかも独自路線でいく個性の強さについていけません。

しかし同時にTest::Unitの垢抜けなさにもうんざりしていて、何かいいフレームワークはないかと思っていた時にShouldaに出会いました。contextを入れ子にできる柔軟性や、既存のTest::Unitインスタンスの中に書けるとっつきやすさはとても魅力的に見えました。そしてその日からShouldaでテストを書き始めました。

このエントリでは、Shouldaを中心にヘルパーやMochaを使った気持ちのいい開発手法を紹介します。

Shouldaは柔軟でとっつきやすい

RSpecの最大の魅力はその書き方にあると思います。しかしその書き方を利用するために、RSpecが開発者に多くのルールを課すのも事実です。いくつか挙げてみると、まず当然ながらそのシンタックスそのもの。ファイル構成(*_spec.rb)とコマンド(spec)、難解なRSpec自身の実装、独自の実行結果のフォーマット(x examples, y failures)。これらはRSpec導入の敷居を高くし、他のツールとの親和性を損なっていると思われます。

ShouldaはBDDのための最低限の文法を提供し、その他には何も変更しません。下のコードを見てください。

class UserTest < Test::Unit::TestCase
  def setup
    # Normal setup code
  end

  def test_can_have_normal_tests
    # Normal test here
  end

  context "a User instance" do
    setup do
      @user = User.find(:first)
    end

    should "return its full name" do
      assert_equal 'John Doe', @user.full_name
    end
  end
end

:: GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS :: Introducing the Shoulda Testing Pluginより。)
前半は古きよき?Test::Unitそのものだということが分かるでしょう。そしてこのコードは「test: a User instance should return its full name」という名前のテストを生成し、「rake」で実行でき、「2 tests, 2 assertions, 0 failures, 0 errors」という、標準的な結果表示をしてくれます。

さらに、Shouldaのcontextは入れ子にできます。

class UserTest << Test::Unit 
  context "A User instance" do
    setup do
      @user = User.find(:first)
    end

    should "return its full name" do
      assert_equal 'John Doe', @user.full_name
    end

    context "with a profile" do
      setup do
        @user.profile = Profile.find(:first)
      end

      should "return true when sent #has_profile?" do
        assert @user.has_profile?
      end
    end
  end
end

thoughtbot: Shoulda testing pluginより)
このフィーチャーは自然な形でコードをDRY UPしてくれます。

RSpecが様々なspecを定義するのに便利なシンタックスを提供してくれるように、Shouldaにも独自のマクロがあります。またShouldaはその簡潔な実装ゆえに新しいマクロを実装しやすくなっています。詳しくはShouldaのサイトを見てください。

fixturesは避ける。しかしモックにも欠点が

フィクスチャが欠点だらけということは広く認識されていると思いますが、改めて書くと、

  • 書きにくい
  • 分かりにくい(特にassociation)
  • 遅い

同じテスト用データをマクロ1発でロードできるのでDRYではありますが、とにかく素のRailsで(プラグインの助けを借りずに)フィクスチャでテストしようと思ったら茨の道であることは間違いありません。

そこで多くの人はモックとスタブを使いますが、モック/スタブにも次のような欠点があると思います。

製品コードの柔軟性が失われやすい。

つまり製品コードが使用するインターフェースをモック/スタブが決めてしまい、他の書き方をできなくなってしまうという現象です。

たとえばPeopleモデルのレコード数を数える場合、ただ数えるだけならPeople.countを使いますね。そこでPeople.countをモックするわけですが、数えるだけでなく実際に全てのレコードを使うなら、People.find(:all)して得られたArrayのsizeを使えばSQLの発行は1回で済みます。ではPeople.findもモックするのか?実際の製品コードではどちらでも書けるようにしておけば、リファクタリングのための自由度を残せます。つまりモック/スタブを使わず、DBにアクセスさせる方が開発しやすい場合があるということです。

ヘルパーでセットアップ

ではフィクスチャの欠点を避けつつ、モック/スタブの罠にもかからないようにDBを使うにはどうすればいいでしょうか。要件は次の通りです。

  • 書きやすい
  • 読みやすい
  • fixturesより速い
  • DRYである
  • 製品コードに自由度を残す

答えはテスト用データをセットアップするためのヘルパー群です。製品コードに自由度を残すためにDBは使いますがfixturesは使わないので、セットアップの段階で必要なデータをDBに注入してやる必要があります。しかし毎回People.create :name => ...とやっていたのではDRYでなくなってしまうので、ヘルパーを作るのです。例えば次のようなコードです。

def a_person
  People.create! :name => random_name, :email => random_email
end

なおrandom_nameとrandom_emailは、文字通りランダムな名前とメールアドレスを生成するヘルパーです。ランダムでいいのか疑問を持たれる方もいらっしゃると思いますが、assertionの段階で参照さえできれば、ほとんどのテストはランダムな値で十分です。どうしても特定の値を持たせたければヘルパーを使わずにPeople.createを直接呼べばよいだけです。ヘルパーをdef a_person(name, email)などと定義して柔軟性を持たせるよりも、何度も書くヘルパーを短くする方が重要です。

このようなヘルパーを使ってどのようなテストを書けるか、以下のまとめで紹介します。

まとめ

それではShouldaとヘルパーを使ったコードの実例です。setupの中のヘルパーで汎用的なテスト用データを生成し、それらをテストの中で使用しています。

class CommentObserverTest < Test::Unit::TestCase
  context 'With an issue, adding a comment' do
    setup do
      @issue, @client = an_issue_of_a_client
      emails.clear
    end

    should 'send a notification' do
      a_comment(@issue, an_account(@client), :private)
      assert_equal 1, emails.size
    end

    should 'not notify 3rd party members a private comment' do
      a_comment(@issue, an_account(a_client), :private)
      assert_equal 0, emails.size
    end
  end
end

このようにShouldaと独自のヘルパーを使うことで、書きやすく、読みやすく、とっつきやすいテストを書くことができました。

最後にShouldaの欠点とMochaについて言及しておきます。Shouldaは個々のテストを「should ...」で書き始めるため、主語をcontextの方に書く必要があります。ところがcontextにつけるフレーズは主語として適切でないことがしばしばあるのです(上の例のように)。そのためcontextのフレーズと実際のsetupの中に書かれている内容がずれてしまうことがあるのが悩みです。

Mochaはモックとスタブを提供するプラグインなのですが、RSpecが独自にモックとスタブを提供するのに対しShouldaはそうでないので、モックやスタブが必要な時は適当なプラグインだと思います。