T

Ferret

このブログの検索は全部懐かしのpubsubhubbub経由でFerretのインデックスが更新されるようになっているのだけれど。

いまみたら全部で2.1MBしかなかった。

DataMapperを教えておじいさん

Ruby初心者です。

DataMapperとやらを使ってみているわけですが。

とにかく、情報が少なくて困ってしまいます。例えば、複数の LIKE 条件をつなぐときとか、どうすればいいの?

class Repo #例えばこんなクラスがあるとする
  include DataMapper::Resource
  property :id, Serial
  property :body, String
end

普通のLIKE節の作成の仕方はこうなる。

Repo.all('body.like' => '%test%')
#=> SELECT * FROM repos WHERE body LIKE '%test%';

または、シンボルで書くこともできる。

Repo.all(:body.like => '%here%')
#=> SELECT * FROM repos WHERE body LIKE '%here%';

でも、複数のLIKE節をつなげる方法がいまいちわからない。そこで試してみた。

まずは配列。

Repo.all('body.like' => ['%test%', '%here%'])
#=> SELECT * FROM repos WHERE body LIKE '%test%%here%';

失敗。

じゃあ羅列。

Repo.all('body.like' => '%test%', 'body.like' => '%here%')
#=> SELECT * FROM repos WHERE body LIKE '%here%';

これも失敗。

ところが、こんな風にチェーンさせるとうまくいく。

Repo.all('body.like' => '%test%') 
+ Repo.all('body.like' => '%here%')
#=> SELECT * FROM repos WHERE body LIKE '%test%' OR body LIKE '%here%';

でも、確かにうまくいくのだけれど、プログラムに組み込むのはどうすればいいんだろう。「+」なんか使われたら、文字列の連結になっちゃうのでエラーになりそうだ。思いつく方法で検索フォームから検索語が入力されたらLIKE検索で結果を返すプログラムを作成してみる。

search_options = []
params[:keyword].gsub(' ', '').strip.split([\s]).each do |keyword|
  search_options << "Post.all('body.like' => '%#{keyword}%')"
end
eval_str = search_options.join(' + ') #まさか!?
@repos = eval eval_str #な、なんと!!

よし、エスケープも用意するか。

def escape_shell_cmd keyword
  keyword.gsub!(/(["'#&;`|*?~<>^()\[\]{}$\\\x0A\xFF])/) { "\\" + $1 }
end

うーん、どんどん間違った方向に進んでしまう。なんか、一般的なやり方っていうのがあって、こちらがそれを知らないだけなんだと思うんだけど、これ以上進むのは危険すぎるので調べることにする。

Lokka用Akismetプラグイン

update: Akismetを間違ってAkismentと綴っていました

Lokka用Akismetプラグインを作成しました。本体にコメントの評価機能を追加したので、このコミット以降のLokkaではAkismetを利用することができます。これで迷惑なコメントとはすっぱりお別れです。大まかな仕様は

  • ログイン中の人のコメントは自動承認
  • 未ログイン状態ではAkismetキーを登録していたらSPAM判定、なければ未承認状態にセット

です。そろそろ案件で使いたいなあ、と思っていたところなので必須機能だろうと追加しました。

動作確認ですが、未ログイン状態でコメントの名前欄に「viagra-test-123」と記入してください。必ずSPAM判定されます(Akismet APIが用意しているテスト機能です)。ログイン状態で同じコメントを投稿したら即時に自動承認されます。

lokka.png

コメントの管理画面にステータスが表示されます。今のところ編集ボタンからステータスを変更する方法しかありません。もうちょっと管理が楽になるように変更したいです。

Win32APIのメモ

dllが用意されているWindows XP embeded入りのハードウェアでFelicaリーダを開発することになり、面白そうなのでRubyで実装してみた。

Win32API経由でdllを叩く際に、渡したポインタに値を入れてもらう関数を実行するやり方がいまいちドキュメントを読んでも不明だったのでメモしておく。

例えば、my_dll.dllというのが用意されていて、初期化関数(void型)とポーリング用関数(INT型、渡したポインタにFelicaのIDmとPMmが入る * どっちも8BYTE)が呼ばれるケース。メモだし例なのでエラー処理は豪快に飛ばす。

require 'Win32API'

# void型の実行
felica = Win32API.new('my_dll', 'felicainit', %w(nil), 'V')
felica.call(0)

ptr_idm = [" " * 256].pack('A8')
ptr_pmm = [" " * 256].pack('A8')

# ポーリング開始
loop dp
  # ポーリング用関数はカードを認識したら0を返す
  polling = Win32API.new('my_dll', 'felicapolling', %(P P), 'i')
  status = polling.call(ptr_idm, ptr_pmm)
  if status == 0
    idm = ptr_idm.unpack('H*')[0]
    pmm = ptr_pmm.unpack(H*)[0]
    # 取得したカード情報を使って素敵なサムシング
  end
end

わからなかったのはポインタの受け渡し方法だったのだけれど、まず引数として2つのポインタを渡すのを明示するには

polling = Win32API.new('my_dll', 'felicapolling', %(P P), 'i')

こんな感じに書ける。「P」はポインタですよという意味で、配列で2つで十分ですよと伝えておく。それから実行時には pack で変数を初期化して

ptr_idm = [" " * 256].pack('A8')
ptr_pmm = [" " * 256].pack('A8')

用意ができたら

status = polling.call(ptr_idm, ptr_pmm)

2つ渡してあげる。ポインタに入った値はエンディアンに気をつけて

idm = ptr_idm.unpack('H*')[0]

こんな感じにunpackしてあげる。以上。

さくらのVPSでLokka(コピペでインストール)

デフォルトのCentOS release 5.5だとRuby 1.8.5になってしまうので、えいやとソースからインストール。Lokkaで使うsqlite-develとWEBrick用にopenssl-devel、gemで使うのでreadlineとzlibのヘッダファイルも。

* ここからroot権限で。

# yum install readline-devel zlib-devel sqlite-devel openssl-devel
# mkdir -p /usr/local/src/ruby
# cd /usr/local/src/ruby
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p302.tar.bz2
# tar jxfv ruby-1.8.7-p302.tar.bz2
# cd ruby-1.8.7-p302
# ./configure --prefix=/usr
# make && make test
# make install

rubygemsを用意。

# mkdir -p /usr/local/src/rubygems
# cd /usr/local/src/rubygems
# wget http://rubyforge.org/frs/download.php/70696/rubygems-1.3.7.tgz
# tar zxfv rubygems-1.3.7.tgz 
# cd rubygems-1.3.7
# ruby ./setup.rb 
# gem list
# gem install bundler

いよいよLokka。

*ここからは一般ユーザで。

$ mkdir lokka
$ cd lokka
$ wget --no-check-certificate https://github.com/downloads/komagata/lokka/lokka-v0.1.0.zip
$ unzip lokka-v0.1.0.zip 
$ cd komagata-lokka-50a9c78/
$ bundle install --path bundle --without production test
$ bundle exec rake db:set
$ bundle exec rackup

これで起動する。

Lokka

WordPressを憎む根性曲がりな人のための密やかな楽しみ、Lokkaをいじってみた。というかgithubにあるので華麗にフォークした。

ざっと眺めて、最初に必要なのはプラグインの自動ロードだろうなあ、と思っていたのだけれど、迷うところが2点。ひとつは、まあルールにしてしまえばいいんだけど、自動ロードするからには何らかの命名規則に従ってもらう必要がある。ちょうどサンプルにhelloプラグインがあったので、これ幸いと

plugin/hoge/lib/lokka/hoge.rbがあったらHogeクラスをregisterする

ということにしてしまった。これはSinatraでいうところのregister Hogeと同等なのだが、じゃあSinatra拡張モジュールの二大巨頭であるところのhelpersはどうするんだという問題がある。するとHelperの方を拡張するやつを書いている人が見つかったので、

plugin/hoge/lib/lokka/hoge.rbの中でHelperを再オープンしてくれたら活きになる

ということにした。それでhelpers Lokka.Helpersが呼び出されるより前にLokka.Helpersを拡張できるようにしておけばいいじゃない、ということでさっそく変更を施してコミットしておいた。呼び出される順番ってのもあるわけだが、それはRuby側の問題だってことにしておけばいいじゃないか、と心の中の悪魔が言うので従っておいた。以上がプラグイン作成のお作法、英語でいうところのconvention over configurationである。

ちょっと仕事が狂ったように忙しいので、ほとぼりが冷めたらプッシュしておこう。

参考文献:Pyhaの大体の仕組み

update: マージされたよ!これでみんなAkismetとAll in one SEO packとHowdyを作ればいいよ!

Tokyo Rubyist Meetup

Tokyo Rubyis Meetupに行ってきた。 前職ではなかなかこういう場に顔を出すような時間が取れなかったので、これからはもっと積極的に技術者間の交流に参加したい。

外国からやって来て日本で働く人たちが、日本人を交えてなんでもざっくばらんに話せるのは面白い試みだと思う。逆に考えれば、たとえばシリコンバレーで働く日本人のエンジニアが日本人同士でいろいろ話し合える場所があると、とても便利なはずだ。

次の予定はまだ決まっていないが、もっと続いて発展してほしいと思う。公用語は英語になるけど、みんな日本に住んでいるんだから日本人が英会話が得意じゃないことくらい十分わかっているので、気楽に参加できる。

もっとも、気づいたらワイクル株式会社(宣伝したよ)の @kdmsnr (児玉サヌールと読みます)さんに小さな会社のぼやきとかを聞いてもらうだけの簡単な仕事ばかりしていたような感じもする。あとRuby歴13年のPHPプログラマだった高橋会長は自分の自己紹介の番の直前に電話がかかってきたふりをして逃亡して繰り上げで順番をこちらにまわすという見事な攻撃だった。新しい高橋メソッドが完成したのかもしれない。自己紹介は何も考えていなかったので何を話したのかも覚えていないが、きっと何も話していない。

会場のビルの屋上に出たら満月がちょっとだけ観られた。そこでまたいろんな人たちと話すことができたが、間違ってとなりのビルに入った人が自分の他にもいたので安心した。でもこちらは自転車置き場から忍び込んでちゃんと40階まで上がったので勝負としては勝ったと思う。

ちょうどWordpress上の開発に辟易して検索していたらpyhaというプロジェクトを見つけたところだったので、居合わせた開発者の @komagata さん(噂通り写真より太っていた)と話したりして面白かった。とりあえずWordpressの人気上位50位までのプラグインとテーマを全部移植しよう。あと、Wordpressを公然と憎むのは悪いことではないと思う。そりゃ世の中にはWordpressが好きだったりナチスに投票したり児童虐待する人だっているわけだけど、みんなの意見に耳を傾けても実りはない。「お前だって使ってるだろ?」と親切な方がおっしゃる前に、私としては、単純に、Wordpressは開発プラットフォームとしては最悪なのに有名になってしまったばかりに案件が増えてしまうから早くなんとかしないと世界中のプログラマが酷い責め苦に遭うよ、といいたいのだと弁明しておく。ああ、まるで俗なPHPとRubyの話のようだ。

そうそう、asakusa.rbって何なんだろうと思っていた人は少なからずいて、asakusa.rbっぽい人がいたので、何回同じこと聞かれんだよと文句をいわれながらもちゃっかり質問したら、ようするにasakusa.rbっていうのは「開発者が集まって、なんか好き勝手なことをしているんだけれど、Rubyでわからないことがあればそこにいる人に聞いてみて、もしわからなければそんなことをわかる人なんかこの世にいない」という集まりだそうな。おわかり?

あっという間だったので、こういうイベントは継続することが大事なんだな、と思った。あと、普段から開発者同士でやり取りする場をもっと増やしたいので、Rubyによるサービス構築の話なんかができたりする集まりがあれば参加しよう。nerima.rbがあるともっといいんだけれど、ひとりでやっても仕方がないしなあ。

heroku事始め

herokuはコマンドラインユーティリティを用意してくれている。普通は

$ sudo gem install heroku

でインストールすればいい。

忘れがちなこと!

のだけれど、もしRubyがreadline無しでビルドされているなら、herokuコマンドのインストールに成功しても動作しないのでリビルドしておくこと。readlineの準備は、yum install readline-develとか、環境に合わせて適当に。

では最初にheroku上で開発するアプリケーションの作成を。

$ heroku create my-great-app

Created http://my-great-app.heroku.com/
git@heroku.com:my-great-app.git

これで準備が完了する。情報を閲覧してみよう。

$ heroku info --app my-great-app
ずらずらずら

ではいつものようにRailsのアプリケーション開発を始める。

$ rails my-great-app
$ cd my-great-app
$ git init
$ git add ./
ずらずらずら

忘れがちなこと!

その後、heroku側にデプロイするためgitでpushして同期する。手元のgitでcommitしていないと当然ながらheroku側と同期はできないんだけれど、忘れがち。

$ git commit -a -m "initial release"

ここまで進むと、./.git/refs/heads/masterが作成されていると思うので、

$ git remote add heroku git@heroku.com:my-great-app.git
$ git push heroku master

これでheroku側との同期が完了する。

RhodesとiPhone SDK4

Rhodesが出力するbuild.xmlの

iphone:
configuration: Debug
sdk: iphonesimulator3.0

ここを

iphone:
configuration: Debug
sdk: iphonesimulator4.0

にするだけで動作した。

shotgunで自分の足を撃つ

このサイトの検索ferretで動いているのだけれど、shotgun + sinatraで作っている簡単なシステムがさっきまで接続できなくなっていた。フロントエンドのリバースプロクシが500番エラーを吐いているので、サーバインスタンスが落ちているかと思ったら起動はしている。でもリモートからtelnetしても接続できない。

netstatしてみると、確かにlocalhostしかLISTENしていない。

$ netstat -atn|grep LISTEN
tcp        0      0 127.0.0.1:3306                0.0.0.0:*                   LISTEN      
tcp        0      0 127.0.0.1:4567                0.0.0.0:*                   LISTEN      
tcp        0      0 :::80                       :::*                        LISTEN      
................

何が変わったわけでもなかろうに、困ったものだ。この環境はshotgunで動いているので、これまで下のような起動スクリプトになっていたのを

 shotgun -p 4567 $HOME/ruby/ferret/index.rb > $HOME/ruby/ferret/logs/ferret_log 2>&1 &

この下のように変更した。WAN側のIPアドレスは持っていない環境なのでとりあえず全部受け付けておいた。「-o」オプションでLISTENする先を指定できる。

 shotgun -o 0.0.0.0 -p 4567 $HOME/ruby/ferret/index.rb > $HOME/ruby/ferret/logs/ferret_log 2>&1 &

で、問題なく動作しているのだが、なんでこうなったのかは不明。正常動作はこのリンクで確認できる。