| @技術/プログラミング

昨日と一昨日の二日間、TDD Boot Camp 福岡に参加した。

参加しようと思ったわけ

勤めている会社にはテストがない。いや、テストはある。エクセルにテスト項目をたくさん書き出していって手動テストだ。テストコードがない。人力で三人くらいが夜なべしてテストする(だいたいスケジュールには遅延が発生する)。これはどうしたっておかしい。開発前に要件定義書、設計書を書いて開発して、開発が終わったらエクセルで長大なテストシートを作成し、手動テストを行う。そしてバグや思わぬ不具合が発見されるとプログラムに改修を加える。欠陥や不都合が発見される度に連動して設計書にも修正・変更が加えられ、Do Repeat Yourselfな感じになってる。毎日毎日ドキュメント作成などの開発以外のタスクに時間を割かれるので新しい技術に触れる機会がないし、遅くまで残って仕事してから帰宅するので趣味プログラミングで知見を広めることもできない(福岡に来てからのこのブログの更新状況を見ればおわかりいただけるかと)。この状況をなんとかしたいと思っていて、藁をもすがる思いでTDD Boot Campに参加した。

感想

一日目は『プログラマが知るべき97のこと』の監修やTDDで有名な @t_wada さんのレクチャーで、TDDとは何かが説明された。以下印象に残った点。

  • 良いテスト
    • 自動化されている
    • 徹底している
    • 何度でも実行可能
    • 独立している
    • プロのコード
    • テストコードもプロダクトコードと同じ品質であることが求められる(リファクタリング、DRY原則の貫徹など)
  • TDD三原則
    • 単体テストコードを書く前に製品コードを書いてはいけない
    • 適切に失敗する単体テストコードができるまで、次の単体テストコードを書いてはならない
    • 単体テストコードに対応する以上に製品コードを書かない
  • なぜTDDするのか
    • 自分が完璧ではないことがわかっているから
    • 最初から思い通りにコードを書けるほどに私たちは賢くない
    • 最初から思い通りに動作するほど対象は単純ではない
    • 素早く対象に近づき、フィードバックを得て方向修正をしながら開発を行う必要がある
  • テストの目的は健康
    • 変化に対応できるのは健康体のコードだけ
    • 変化に対応できるのは健康体のチームだけ
    • 毎日残業する、毎日レッドブル飲んだりしていてはダメ
  • TDDの導入効果
    • MSやIBMでTDD導入後、欠陥の割合が4割から9割削減された。
    • コード実装時間は15%から35%増加した。
    • しかし結果的にはバグが激減するので開発工数自体は減る。
  • TDDは才能ではなくスキル
    • 練習すれば習得可能
    • 量は質に転化する
    • 写経しましょう!
    • PCにGitをインストールし、ページをキープできるブックスタンドを買って、ケント・ベック本をひたすら写経。ビルドを実行する度にコミットする。

テストのみならず、自動化やバージョンコントロールの重要性が説かれた。

二日目には @bleis さんによるGitの効果的な利用方法やJenkinsを利用した継続的インテグレーションの実践例、 @akineko さんによるOMakeを利用した自動ビルド、自動テスト、自動コミットの話など。

ペアプログラミングを体験した

ペアプログラミングを生まれて体験した。 @mallowlabs さんとRubyでペアプログラミングをさせてもらった。講師陣が出題する課題を解いていくというもの。当然テスト駆動。テスティングフレームワークにはRSpecを利用した。

WEB + DB PressなどのRSpec特集を写経したことはあったけど、時間制限がある中で、他の人とペアを組んでプログラムを書いていく作業はかなりエキサイティングだった。

ただ自分のRubyスキルおよびVimのスキルが著しく劣っていたため、@mallowlabs さんの足を引っ張っていた感は否めない。正直申し訳なかったです。

全般的に、自分の知識のなさ、スキルのなさが実感できた

以下、初日に行ったFizzBuzz問題と主に二日目に取り組んだTwitterのタイムラインをカテゴリ分けするという課題の成果物。

TDD Boot Camp 福岡 — Gist TDDBC でペアプロした結果です — Gist

TDDをいかにして根付かせるか

勉強会に参加していきなりコードが書けるようになるわけでは当然ないので、継続的な勉強が必要だと感じた。いっぱい本を紹介してもらったので積ん読本を何冊か片付けたら『レガシー・コード改善ガイド』と『テスト駆動開発入門』を買おうと思った。

| @技術/プログラミング

気がついたらだいぶ時間が経ってる…。2月26日、福岡県コンテンツ・Ruby産業振興センターで行われたスマートフォン開発環境セミナー(仮)に行った。

仕事でスマートフォン開発してないけど、いろいろ面白い話が聞けるかなと思って行ってみた。

発起人のきしださんからiOSとAndroidのスマートフォン開発環境について概要が説明された。スマートフォン向けにアプリケーションを提供するには大まかに二つあって、Objective-CやJavaによるネイティブアプリケーションか、HTML5 + CSS3 + JavaScriptによるウェブアプリケーションか。WebアプリはAppleの審査がないし開発・配布が容易だが(iOS用、Android用にソースを分ける必要がない)、動作が遅いし使いにくい。かといってネイティブアプリにもデメリットはあって、Objective-CやAndroid Javaなどを身につけねばならず、Web系でLLばかりやってる人には取っつきにくい。特にObjective-Cはガベージコレクションがないのでメモリ管理が大変。さらにiOSとAndroidに対応させようとすると両方を学ぶ必要があり、学習コストが高い。ではどうすればいいのか? といった感じでセミナーが始まった。

ゲストスピーカーで最初に話をしたのがTitanium Mobileの開発元、Appceleratorの masuidrive さん。正直なところすっかり洗脳されてしまった。Titanium MobileはiOS・Android両対応のスマートフォン開発環境を提供で、JavaScriptで記述する。位置情報やソーシャルサービスとの連携など、ネイティブアプリにできることのほぼすべてができる。画像操作や高速な処理が必要となるゲーム開発には向かないし、アプリ内課金にいまのところは対応していないが、PC向けWebアプリケーションのモバイルクライアントとしての役割は十分にTitanium Mobileでこなすことができるそう。どうしてもスピードや高度な画像処理が必要なときはそこだけC言語なりJavaなりで記述すればいい。Titanium Mobile上で開発すると、UIのこともあまり考えずに済む。OSごとのUI指針に沿ったUIを自動的に提供してくれるため、ごちゃごちゃぐちゃぐちゃになりがちなAndroidのUIにも共通感・統一感をもたらす。すでに『もぐスナップ』など、実際にTitanium Mobileで作成されたiOSアプリがApp Storeで発売されており、活況を呈している。エトセトラ。

すっかりこのmasuidriveさんのスピーチに魅了されてしまった。

Node.jsなど、嫌がおうにもこれからはJavaScriptの時代がやってくることを印象づけられる勉強会だった。最近はあまりJavaScriptを書いていなかったが、Publickeyとかに書いてあるみたいに、本当にJavaScriptの時代がやってくると感じた。

| @技術/プログラミング

昨日の続きのようなもの。LokkaをUbuntu上で動かそうといろいろやってます。passenger入れて、 passenger-install-nginx-module を実行しようとしたらエラーが出た。お前はrootじゃないからnginxインストールできひんわ、みたいなメッセージが出る。sudoったりrootになってインストールしようとすると今度はpassengerなんていうgemはないわ、って怒られる。sudoしたときには.rvm内のgemパッケージではなくシステムのgemパッケージを見ようとするっぽい。またrootになるとRVMではなくシステムのRubyが実行されるので同じくpassengerなんてgemないわ、って怒られる。システムのRubyは1.8.7だし、なるべくなら使いたくない。RVMのRubyを使ってpassenger + nginxな環境作れないのかよ、とググってたら以下のような記事を発見した。

なんと、RVMには rvmsudo というコマンドがあるらしい! 試しに

$ rvmsudo passenger-install-nginx-module

を実行してみたところ、お前はroot権限がないんじゃ〜とエラーが出ていたところも無事通過してnginxをインストールできてしまった。以前、sudoで無理矢理nginxをインストールしたときは、 nginx.conf 内で、

passenger_root /home/morygonzalez/.rvm/gems/ruby-1.9.2-p136/gems/passenger-3.0.2;
passenger_ruby /usr/bin/ruby1.8;

となっていて大変気持ち悪かったし、Passengerも「PassengerとRubyの本体でバージョンの不整合があるだろヴォケ」みたいな警告出してた。それが rvmsudo のおかげで nginx.conf に書き込まれる値も以下の通りとなるので、とりあえずPassenger(RubyGems)とRubyのバージョンが異なるというような問題は回避できる。

passenger_root /home/morygonzalez/.rvm/gems/ruby-1.9.2-p136/gems/passenger-3.0.2;
passenger_ruby /home/morygonzalez/.rvm/wrappers/ruby-1.9.2-p136/ruby;

ただ、なんでか分からないのだけどnginxがうまく動いてくれなくて、無事Webサーバーを起動できてない感じです。

それにしてもRVMは、本当にきもいと思う。readlineとかまで.rvm内にインストールできるできるし、いったい何考えてるんでしょうかね。きもすぎて便利です。

| @技術/プログラミング

RVMを使ってます。RVMで入れたRubyがshebangに #!/usr/bin/env ruby と書いたときに呼び出されて欲しいと思ってます。シェルから

#!/usr/bin/env ruby

puts RUBY_DESCRIPTION

なファイル(hoge.rb)があったとします。これを

$ ruby hoge.rb

とシェルから呼び出したときにはちゃんと

$ ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]

と表示されます。しかしTextMateでRunしたときや、Apacheから同じshebangで呼び出したときには、systemのRubyが呼ばれます。

これ、もう半年くらい解決方法探してる気がする。

TextMateでRunしたときにsystemのRubyが呼ばれるのはともかくとして(テストするときはシェルから呼び出すのでRVMのRubyで実行できる)、Apacheで新しめのRubyを使いたいときはどうすればよいのでしょう。まさかshebangに ~/.rvm/bin/ruby-1.9.2-p136 とか書くんじゃろか。

cxxさんに質問したら RVM: Ruby Version Manager - Installing RVM System Wide というURLを教えてもらった。今日は眠いのでまた今度試す。

| @技術/プログラミング

ブログをリニューアルするにあたり、自分でRailsで簡単なブログを作ろうかとも思ったけど、いろいろ面倒くさいので今のところLokkaを使うつもりでいます。デフォルトデザインがお洒落だし、さくっとデプロイできそうなのもいい。LokkaはSinatraベースなので、気にはなっていたものの何も触れなかったSinatraの勉強にもなるでしょう。開発者の komagata さんのこのブログ記事が印象に残ってる。

Railsを追いきれる自信が無かったから。Rails文化に引っ張られてアプリが一生完成しない気がしたから。あとアプリとしては問題無いのにベースのRailsのバージョンが低いだけで残念っぽくなってるアプリ(Redmineとか)を見たから。

半年やってみてSinatra面倒クセー!っていっぱいあったけど、(Sinatra本体の)ソースが短いので完全把握できる掌握感は独自のOSS作る上で心強かった。

そう、Railsはバージョンアップが頻繁なため、仕事でRailsを使えないサンデー開発者にはバージョンアップを追いかけるのが大変すぎる。Railsのマイナーアップデートでアプリケーションが依存するgemが動作しなくなりアプリケーション崩壊みたいのを何度か経験した。それが毎週末続くと、「Railsめんどくせぇ」ってなる。 ~/Sites に作りかけのRailsアプリケーションがいくつもあります。

Lokkaを利用して、途中で挫折しないようにブログ移行をしてみようと思います。

| @技術/プログラミング

VPSが安くなってきてるということなので使ってみることにした。自宅サーバーも一瞬考えはしたが、夏の発熱や騒音のことを考えるとVPSで十分かなという気がした。いま借りてるレンタルサーバーのDreamhostにはおおむね満足してるんだけど(ディスク容量・転送量無制限)、rootになれるかどうかはやっぱり大きい。あとDreamhostはsshでシェルにログインしたときのわずかなタイムラグが地味に気になる(zshでTab補完するときとか)。さくらVPSは速い回線から接続すればローカルのシェルと同じくらい速い。

OSはデフォルトだとCentOSなので、Ubuntuに入れ替えた。HDD容量は20GBだけど、普通にホームページ作ったり小さなプログラムを動かすくらいだったら十分かなと思う。

| @技術/プログラミング

ポータルシットのコメント一覧ページで、マウスオーバーして全文を取得するやつをかっこよくした。

これまではjQueryでスクレイピングして、記事のパーマリンクからコメントを取得していた。しかしこれは無駄なクエリが発生する。記事本文を取得するクエリとコメントを取得するクエリだ。

これはださいと思っていたので、Rubyでコメントを取得してJSONで返すCGIを書いた。

#! /usr/bin/env ruby
#-*- coding: utf-8 -*-
start_time = Time.now

require "rubygems"
require "active_record"
require "mysql"
require "kconv"
require "cgi"

ActiveRecord::Base.establish_connection(
  :adapter  => "mysql",
  :database => "database_name",
  :host     => "mysql.host.name",
  :username => "mysql_username",
  :password => "mysql_password",
  :socket   => "/path/to/mysqld.sock",
  :encoding => "utf8"
)

class Comment < ActiveRecord::Base
  set_table_name 'p_forum'
  
  def self.some_comment(n)
    find(n)
  rescue ActiveRecord::RecordNotFound
    return false
  end
end

cgi = CGI.new
comment_id = cgi.params['id'][0]
@comment = Comment.some_comment(comment_id)
redirect_url = '/var/index.php?id=error404'
print cgi.header({ 'status' => 'REDIRECT', 'Location' => redirect_url }) unless @comment
cgi.out("application/json") {
  {:title => @comment.title, :user_name => @comment.user_name, :user_uri => @comment.user_uri, :date => @comment.date, :mod => @comment.mod, :comment => @comment.comment, :generation => Time.now - start_time}.to_json
}

ActiveRecordのおかげでさくっとできる。このCGIにjQueryで $.getJSON() してJSONをページ内に読み込む。各コメントはMarkdownに似た形式で記述されているので、Showdown.js を使って Markdown->HTML して表示している。参考までにコードはこんな感じ。

// load entire comment by JSON
$(document).ready(function() {
  $("#recent-comment-list a.taggedlink").one("mouseover", function() {
    var targetUrl = $(this).attr("href");
    targetUrl.match(/#c(d+)$/);
    var commentId = RegExp.$1;
    var commentLoadDiv = 'div#c' + commentId;
    var commentAuthorParagraph = 'div#c' + commentId + ' + p.auth';
    $.getJSON(
      'comments.cgi',
      { "id": commentId },
      function(data) {
        var converter = new Showdown.converter();
        var comment = converter.makeHtml(data.comment);
        $(commentAuthorParagraph).css("clear", "both");
        $(commentLoadDiv).html(comment).slideDown("slow");
        $(commentLoadDiv).wrap('<div class="comment-content"></div>');
      }
    );
  });
});

問い合わせのクエリはコメントを持ってる p_forum というテーブルへの一回だけになったが、RubyをCGIとして動かすと遅い。パフォーマンスは逆に落ちてるかも知れないけど、所詮自己満足なので問題なし。

追記: なんか脆弱性ありそうな気がしてきた…