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

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として動かすと遅い。パフォーマンスは逆に落ちてるかも知れないけど、所詮自己満足なので問題なし。

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

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

福岡工業大学で行われた、オープンソースカンファレンス福岡2010に行った。

usp labのシェルスクリプトで業務用基幹システムを開発する話が面白かった。一般的なプログラミング言語は使わずに、シェルで生のLinuxを使って作業を行わせる。DBは使わずデータはTextで持つ。HTTP関連のあれこれもシェルスクリプトで行う。このためセキュリティのことは考えないといけないらしいんだけど(フロントエンドとバックエンドがシェルスクリプトでつながってるため)、大手のシステム会社が100人月とかみたいな見積を出す案件を10人月程度でやったそう。具体的には無印良品のシステムとか。遅めに会場に入ったので資料は売り切れで貰えず、細かい数字は自信がないんだけど、無印のやつは一年間に10億円かかってたシステム関連の費用が2億円程度に抑えられるようになったそう。mknodとかsedコマンドを駆使した並列処理のデモは本当に面白かった。へー、シェルスクリプトだけでこんなにできるんだー、っていう感じ。1億行のテキストデータ処理を53秒で終わらせたりとか。「Linuxはなるべく生で使いましょう」という言葉がとても深く印象に残っている。Railsみたいな便利なフレームワークを使った開発とはまったく異なるけど、UNIXの「小さいものは美しい」という理念のもと、とんでもなく効率的に開発を行っている様がとてつもなくクールだった。

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

Ruby 1.9、Rails 3、RSpec 2で開発してる。いままでRubyとRailsになれるのに一杯一杯でテスト駆動開発できてなかったので、きちんとテストファーストでやることにした。

そんで環境を整えてたんだけど、RSpecはRails 3に合わせてVersion 2が出てるみたい。しかも結構変わってる。

$ spec -c spec/*

とかやってもエラーが出る。 which spec すると /usr/bin/spec と出ちゃう。どうやら spec で発動されるのはRSpec 1.3のものみたい。

RSpec 2からはコマンド名が変更されており、テストコードを実行したいときは以下のように書くみたい。

$ rspec -c spec/*

これできちんとテストコードが実行された。かと思いきやFailureが。

Ruby 1.9で動かしてるから、コードの中に日本語を書くときは一行目に

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

と書いてテキストエンコーディングを明示してあげないといけない。

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

動かしてるサイトのCakePHPのバージョンを最新版の1.3.4に上げようと思って、Media PluginもCakePHP 1.3対応バージョンの1.3alphaにアップデートしようとした。

とりあえず本体を git clone http://github.com/cakephp/cakephp.git し、Media Pluginを git clone http://github.com/davidpersson/media.git してみた。

Media Plugin 1.3での設定方法とか調べてみようと思って、GitHub上のWikiのページを探すんだけど見つからない。なんと、作者サマはVer 1.3からWikiを消しちゃったみたい! そんまま動かしてみたところではMedia Plugin動いてないみたい。Mediumヘルパーがないというエラーが出る。プロジェクトの中の docs ディレクトリにドキュメントが格納されていたのを発見したので(docs at 1.3 from davidpersson's media - GitHub)そこを参考にしながらMedia Plugin 0.6から1.3へのMigration作業をやったんだけど、とうとうできなかった。

まず第一に、クラス名が変わってる。Media.Medium だったのが Media.Media になってる。Viewファイル内での変数も $medium ではなく、 $media になってる。そしてメソッドとかもHTML5対応とかで結構変わってるみたい。

さらに、media processing 関連のクラスが分割されて別のライブラリとしてMedia Pluginの中に含まれてる(davidpersson's mm at master - GitHub)。これが結構わかりにくい。なんかImagick必要ぽくて、本番環境じゃインストール権限ないので使えないし、結局ここで諦めてしまった。

Media PluginはVer 0.6のWikiがあった頃もなんかドキュメンが見づらかったし、1.3になってドキュメントはただのテキストファイルになり、しかもメソッドについての解説がないので結局本体のコードを読むしかない。

RubyのライブラリはたいていRdocとかついててドキュメントが充実してるので、あれに慣れるとドキュメントがわかりにくいライブラリやプラグインは億劫に感じてしまう。

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

いやまぁテキストエディターにはいろいろあるわけでして、皆さんEmacsとかVimで日夜しこしこコードを書いておられると思うんですけど、僕はGUIしか使えない情報弱者なので主にTextMateを使ってます。

TextMateで便利なのが "Run" っていう機能です。Rubyのコードを書いていて、 + R でさくっと実行結果を確認できます。いちいちTerminal開いて

$ ruby hogehoge.rb

とか面倒くさいことをやらずにすみます。

で、これからが本題なんですけど、先月Ruby 1.9.2がリリースされて、さらに gem update でRails 3が入るようになってしまったので、お試しでRuby 1.9.2とRails 3を使ってみることにしました。しかし1.8系を完全に捨てることは恐ろしいので、RVMを使って複数のバージョンのRubyを切り替えながらしばらく過ごしてみることにしたわけです。

上に書いたとおり僕ちゃんは情報弱者なのでTextMateに依存したコーディングライフを送っており、 + R で動くRubyもRVMのRubyにしたいと思ったのですが、これが分からなかった。RVMのサイトを見たらいろいろごちゃごちゃやり方が書いてあるんだけど(RVM: Ruby Version Manager - Textmate Integration with RVM)、結局この通りにやってもうまくいかず。

しかし先ほどなにげなく

$ rvm 1.9.2 --default

としてあげたところ、TextMateでもRVMのRubyが走るようになりました。

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

みなさん、クリッカブルマップしてますか? あんなくそみたいなもの使って地図をつくるなんて発狂しそうなこと、ここを読みそうな人はやってないと思いますが、僕の職場のお客さんとか上司は「Google Mapsには心がこもってない」とか言うので、クソみたいイラストをクリッカブルマップにして地図を作ってるんですよ。まじでかわいそうな僕ちゃん。

ご存じない方のためにクリッカブルマップの作り方を説明しておくと、まず画像を作り、それをHTMLに貼り付け、HTMLオーサリングツール(うちの職場ではDreamWeaver)のGUIエディタでちまちま画像上のクリッカブルにしたい位置を選択するという、血尿が出そうなくらい面倒くさい作業を強いられます。

最悪なことにこのクリッカブルマップのあるページ、頻繁に更新依頼が来るのですよね。依頼が来る度に就業時間中の快適なネットサーフィンが妨げられるので、一発JavaScriptを書いて画像上の座標を取得することにしました。いちいちクソみたいに重いAdobe DreamWeaverとか立ち上げてられるか。

コードはこんな感じ。

<html>
  <head>
    <script>
      function getPosition(){
        var x, y;
        var image = document.getElementById('image');
        image.onclick = function(e) {
          x = e.layerX;
          y = e.layerY;
          document.getElementById("pointX").value = x;
          document.getElementById("pointY").value = y;
        }
      }

      window.onload = getPosition;
    </script>
  </head>
  <body>
    <div>
      <img src="path/to/image" id="image" style="position: absolute; top: auto; left: auto; width: 500px; height: 332px" />
    </div>
    <form action="/" method="post">
      <input type="text" id="pointX" name="pointX" value="" />
      <input type="text" id="pointY" name="pointY" value="" />
      <input type="submit" value="発射!" >
    </form>
  </body>
</html>

Firefoxでしか動作確認してないけど多分IEでは動かないと思います。ここのサイトを参考にさせてもらいました。