Google Hackathon for OpenSocial in Kyoto に参加
先日、近所の京都リサーチパークでHackathonがあるということで参加してきた。
2/20(事前ミーティング/Ideathon), 2/21(Hackathon)という2日間。結果から言うと、前日のIdeathonでは見事優勝させて頂いたものの、本番では意外な所で躓き思ったように作業が進まず惨敗であった。
今回作ろうと考えたのは、MySpaceをターゲットにした、音を使ったFlashアプリケーションで、タイトルもまんま「Social Sounds」。簡単に説明すると、まず、ユーザの属性値をパラメータとしてユーザー毎に固有の「音」が生成されるようにする。誰かと友達になるとその友達の音を自分の音コレクションに追加出来るようになる。で、集めた音を使い、簡単なシーケンサーもどきでトラックを作って聴いて遊べる、というもの。
アプリケーションのコアな部分は全てFlashで実装し、jsでの実装部分はOpenSocialコンテナとFlashアプリ間のブリッジに徹するのが良さそうだ、ということで、基本的にFlashとjsに分担して作業することにした。二人チームで、相方となった方がCS使いということだったので、Flash制作は彼にお願いし、js/Flash間のインターフェースを取り決め、自分はFlexを使用してテスト用のアプリケーションを作成。Flex <-> js <-> コンテナがやりとりするコードを書いていった。
しかし自分がCSを使った事が無く、FlexとCSで如何にコラボレートしていくべきかのノウハウが全く無かったのが痛かった。結局、テスト用Flexアプリに対しては問題無く動作していたjsとのインターフェースが、CSで作成したFlashとは上手く動作しない部分があり、ハマりまくっているうちに時間切れ、と非常に中途半端な状態で終了。
まあ、元々そんなに作業が進むとは期待していなかったものの、流石にこのままでは悔しいし勿体ないので、今後も開発を継続していくことにした。ついでにFlash CS4 Professionalの試用版をダウンロードし、CSの使い方を探りはじめてみたり。
で、現状は、元々MySpaceのみを対象にしていたのを、Orkutの方が開発し易いかも?ということでOrkut上で色々試しはじめているのだけど、Flashの埋め込み方法に違いがあってハマったので以下にメモしておく。
MySpaceの場合
MySpaceの場合、普通に
<Content type="html"> <![CDATA[ <object id="flashobject" classid="clsid:1111111-2222-333-4444-xxxxxxxxxx" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0" width="550" height="400" align="middle"> <param name="movie" value="path_to_swf_file/foo.swf" /> <param name="quality" value="high" /> <param name="bgcolor" value="#ffffff" /> <param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="false" /> <embed name="flashobject" src="path_to_swf_file/foo.swf" width="550" height="400" align="middle" quality="high" bgcolor="#ffffff" allowScriptAccess="always" allowNetworking="all" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer_jp" /> </object> ]]> </Content>
Orkutの場合
まず、MySpaceと同じように
<div id="flashcontainer"></div> <script type="text/javascript"> gadgets.flash.embedFlash( "path_to_swf_file/foo.swf", "flashcontainer", { swf_version: 10, id: "flashobject", allowScriptAccess: "always", allowNetworking: "all", wmode: "transparent"}); </script>
ではどうするか?答はSWFObjectを使用すること。Download SWFObject 1.5を適当に配置し下記の様に記述すると、問題無く動作した。まだ試していないが、多分Orkut以外もこの方法で大丈夫なのではと思われる。
<div id="flashcontainer"></div> <script type="text/javascript" src="path_to_swfobject/swfobject.js"></script> <script type="text/javascript"> var so = new SWFObject( "path_to_swf_file/foo.swf", "flashobject", "550", "400", "10", "#ffffff"); so.addParam("allowScriptAccess","always"); so.addParam("allowNetworking","all"); so.addParam("wmode","transparent"); so.addParam("menu", "false"); so.write("flashcontainer"); </script>
という訳で、
Hackathonはとても楽しいイベントだった。Hackathonというと、コードばりばり書けるハッカーばかりが集まりそうな感じがして敷居が高そう(だし、実際ハッカー度の高いHackathonも多そう)だけど、Google Hackathonは色んなレベルの人が参加し易い良いイベントだと思う。このようなイベントを開催してくれたGoogleの方や京都のスタッフの方には感謝したい。懇親会で他の開発者の方々やGooglerの皆さんに色々と興味深い話が聞けるのも楽しいし、ハッカーが集まってコード書きまくるなんてなんか怖い、とか思ってる人がいるとしたら、それは誤解だと言っておきたい。
尚、今回参加の目的として、他の人の開発手法やどんな感じでコード書いてるのか等のテクニックを盗みたい、というのもあったが、とてもそんなことやってる場合じゃない、という位、自分の事で精一杯だったのは残念。
下駄記号
文字列同士の類似度を調べるコードを見ていて、下駄記号(〓)というのを初めて知った。へえ。
と、それはどうでもいいんだけど、仕事場の近くにふたば書房っていう極々普通の町の小さな書店があるのだが、久しぶりに立ち読みに入ったら、BGMが延々Animal Collectiveで異様だった上に、何故だか音楽やら建築やらのコーナーがやたらと増えている。一体何処へ向かってるんだ。小さい店は色々大変なんだろうな。
で、そのよく分からない頑張りに敬意を表し、見つけた「ナイロン100パーセント」を買ってみた。
この本、ページ左下がディスクガイドになっていて1ページに1タイトル紹介されているのだけど、これが僕が好きなのばっかで嬉しくなる。というか、ニューウェーブ、レコメン系、クラウトロック等なんだけど、そういうのを僕が好きだということの源流がここら辺にあるのだろうな・・・。殆ど持ってる(or 持っていた)ので、ここで紹介されていた中でまだ聴いてないやつは今後全て入手し聴くべきだ、と思った。
以上、ここへきて初めて音楽絡みのことを書いてみた。
ATOKのキーバインドをEmacs風にする
ATOKダイレクト API for Perl/Rubyが面白そうだったので、これを機会に初めてATOKを使ってみた。
で、早速ruby_evalを試してみら、確かに便利なんだけど、残念ながらATOKの変換後の文字列って、改行などを含むことが出来ないのね。便利さ半減。
それはそうと、ATOKって滅茶苦茶キーバインドをカスタマイズ出来るんだな。知らなかった・・・。即、以下のような感じでEmacs風にしてみたら、非常に日本語入力が快適になった!
変換/次候補 | Ctrl-N | 追加 |
変換/前候補 | Ctrl-P | 追加 |
全角無変換(後)変換 | Ctrl-Y | Ctrl-Pから変更 |
部分確定 | - | Ctrl-Nを削除 |
変換取消 | Ctrl-G | Ctrl-Hから変更 |
全文字削除 | Ctrl-G | 追加(入力中のみ) |
1文字削除 | Ctrl-D | Ctrl-Gから変更 |
カーソル前移動 | Ctrl-B | Ctrl-Kから変更 |
カーソル後移動 | Ctrl-F | Ctrl-Lから変更 |
カーソル先頭移動 | Ctrl-A | 追加 |
カーソル末尾移動 | Ctrl-E | 追加 |
文節区切り伸張 | Ctrl-V | Ctrl-Lから変更 |
文節区切り収縮 | Ctrl-6 | Ctrl-Kから変更 |
文節前移動 | Ctrl-B | 追加 |
文節後移動 | Ctrl-F | 追加 |
文節先頭移動 | Ctrl-A | 追加 |
文節最終移動 | Ctrl-E | 追加 |
惜しむらくは、修飾キーにAltが使えないことだな。それが出来たら、文節区切りの変更にM-F/M-Bを使うのだが。
ともかくこれでまた、矢印キーに指を伸ばさねばならない場面が減った。というか、今までMS-IME使い続けててかなり損してたかも。変換精度の良さとかはまだあまり体感出来てないけど、キーバインド変え放題というだけで使う価値大だな。本物のEmacs使いはSKK使うんだろうけど。
これで、残る大きな矢印キー問題は、eclipseのインテリセンスで候補を選択する時位になったな。
KOF 2008での「はてな流大規模データ処理」by id:naoyaさんの発表に関して質問したときのメモ
KOF 2008 の発表資料 - naoyaのはてなダイアリー
こちらの発表に関して、後日質問した時のメモ。
- 全文検索のインデックス生成等の為、MySQLのデータをバッチ処理でdumpしている、という点について。
- 全文検索について。Sedueを使っているが、既にLuceneなどもあるが?
- Senna -> 以前試したが、segvしてMySQLごと落ちたりして使えなかった。今は良くなってると思うけど。インデックスがでかくなってメモリに載せられなかったり。
- Lucene -> 試したが、目的の用途では遅くて使えなかった。融通も効きにくい。それにJavaだし。
- HyperEstraier -> 試してみていない模様?mixiとかでも使ってるのでいい感じなのかな、とのこと。
- Sedue -> Suffix Arrayならではの弱点もある。(具体的な話は忘れた。)
- 検索は自分で実装するのが良いよ!
- memcachedは結構使ってる?
- 勿論使ってるけど、そこまで使いまくってるというほどでもない。
- 格納するデータはケースバイケースで。あんまり凝ったことをしようとし過ぎるとまたバッドノウハウの世界に…。
- DBアクセスも、OSのキャッシュにデータがちゃんと載ってれば全然速い。
以上、特に公表してマズそうな内容も無さそうだし、もしかすると何か参考になる人がいるかも知れないので公開しておく。憶えている内容をそのまま書いただけで、間違ったことを書いている可能性もあるので注意。
ちなみに、id:kazuhookuさんのコメントで「〜OS のバッファの話からすると、myisam なん?」とあったが、例えばinnoDBだとOSのバッファが有効でないという事なんだろうか。実践ハイパフォーマンスMySQL読めば分るかな?
しかしJOINもトランザクションも要らないなら、もうそこはRDBMS捨てても良さそうな気もするんですが、本当に完全にJOIN使わないという訳でもなく、色々あってなかなか捨てられないもんなんですね。
PHP関西 勉強会に参加
11/7、KOF初日の帰りにPHPの勉強会に初参加。関西でPHPの勉強会というのは久しぶりらしい。とりあえず、全参加者のうち、かなりの割合がCakePHPを使っていて驚いた。以下感想。
PHPUnitのこんな機能知ってる? by id:kunitさん
PHPUnitというかSeleniumの話がメイン。なぜPHPUnitというタイトルかというと、話の中で、kunitさんが今作っている、SeleniumIDEの吐くテストのHTMLを、SeleniumRC使ったPHPUnitのコードに変換するツールについて触れようと思ってたら、Seleniumの話が面白くなってきてしまって、それメインになった、とのこと。
SeleniumIDEは便利だけど、HTMLだと柔軟性に欠けるので、結局プログラム書いてテストしたくなるよね?なのでそのHTMLをコードに変換し、それを改変してテスト書くと楽じゃないかという話。確かに便利そうだ。個人的にはSeleniumIDEの方は使ったこと無くて、最初からRCだけ使っていて十分便利だと思っているのだが。ちなみにそのRCも、テスト用に使ってるのではなく、ブラウザを自動操作するMechanize的なツールとして使っている。(Mechanizeでは上手く行かないところがあったので。)SeleniumRCはテスト用途で無くとも便利な道具だと思う。
PHPUnitについては、最近は単なるxUnit系のテスティングフレームワークという枠を超えて、RSpecみたいなBDDの要素を取り込んだりして面白い事になっているという(詳しくは今回の発表の範囲外)。自分は何故だったかSimpleTestをずっと使っている為PHPUnitは全然チェックしていなかったが、ちょっと気になった。
CakePHPとか by shin1x1さん
どの様な経緯でCakePHPを使うようになったかという話から、ちょっとしたTIPS、実際の構築例まで。自分はCakePHPは使っていないのだが、こういった経験談というのは参考になるし面白い。Cakeを選んだ理由として、PHP4/5対応、ドキュメント充実、などの点を挙げていたが、ここら辺が自分がsymfonyを選んだ理由と対照的で興味深かった。自分の場合、CakeはPHP4対応という所で、オブジェクト指向的にイマイチなんじゃないかと思ったのがまずあったし、またドキュメントという点では、当時の時点ではsymfonyの方がしっかりしていたように感じられた、ということもあるので。まあでも、今の時点で選ぶならCakeにするかもなーとか思ったりして。それにしても、発表途中で出てきたCake内のSetとかの配列関係のライブラリは、どんだけarray好きやねん!という感じでちょっと引いた。XPath的に多次元配列の要素にアクセスできるような機能とか、かなりキモい。便利そうだけど。
PHPScraping2008 by させざきさん
Web::Scraper的に使えるPHPライブラリ、Diggin_Scraperの作者であるid:sasezakiさんによる発表。Diggin_Scraper知らなかったけどなかなかいい感じ!sfBrowser使ったテストとかでDOMXPath->query()とかチマチマやってassertしたりしてたんだが、試しに使ってみて大丈夫そうなら今後はこれを使おうと思った。発表後半では、HTTPアクセスして結果をスクレイプするようなコードを書く時に、HTTPリクエスト周りの抽象化にZend_Http_Client使ってやると、テストするのにテストアダプタ使えていいよ、といった話もあり、Zendフレームワームのライブラリって完全にスルーしてたけど実は色々便利なものが埋もれてるのか、という気付きがあった。
懇親会
一度会ってみたかったkunitさんですが、やはり熱い方で、飲みまくりながら色々語ってくれました。少し前の設計勉強会絡みの話から、開発中だというMapleの次期バージョンの設計思想まで。既にsymfonyやらCakePHPなどが出ている中で、フルスタックのフレームワークを作る気はもう無いという話やら、レイヤーアーキテクチャ大好きだから勿論そういう作りになるのを意識したフレームワークであるとか、複数のリソースが複合的に表示されたビューに対応するRESTfulなインターフェースの話とか、色々と。最近RESTやテスト駆動に傾倒されているようなので、その辺を強力にサポートするフレームワークになるのだろう。テスト可能性を重視するならやはりDIコンテナがあると良いのだけど。個人的にsymfony2で入る予定だというDIコンテナにもかなり期待してるので。それにしてもkunitさんはこの懇親会後、次の日KOFで発表があるにも関わらず、更に朝4時まで飲んでいたという。凄い。
その他、ずっと不思議な存在として気になっているrhacoについて、やはり何だか凄いものなのだということが色んな人の話からちょっと分ったり。というか、作者のtokushimaさんという方が非常に面白い人のようだ。あんまり使ってもらうことを前提としてないとか、色々自由過ぎる話に衝撃を受けた。
WindowsでGitのGUIを使えるようにする
先日の日記に、よく調べもせずGitのeclipseプラグイン出ないかな、などと書いたら、親切にもid:secondlifeさんよりeclipseプラグインならありますよ。とのコメントをもらったので、試してみた。
教えてもらったEclipse Git、egit - 実用にビルドしたものが置いてあったのを、とりあえずそのままインストールしてみる。が、設定の「チーム」にGitの項目が増えたものの、特に何の操作も出来ず…。
残念。
まあeclipseプラグインは置いといて、Windows(XP)でGit使えるようにしとくか、と思い、msysgitをインストール。普通にインストールして、環境変数PATHに C:\Program Files\Git\bin を追加してやったら、コマンドラインからGitの各コマンドが使えるようになった。
インストールされたフォルダを見てみると、git-guiが一緒にインストールされているようだったので、これが動けばいいなあと、コマンドラインからsh.exe git-gui等と実行してみると、" *** Couldn't reserve space for cygwin's heap."というエラー。
cygwinもインストールしてあったので、何かが干渉しててダメなのかと暫くハマっていたが、ちょっと調べると、Gitのインストール時に入った C:\Program Files\Git\bin\msys-1.0.dll がダメな模様。WinAVR and Windows Vista - MadWizard.orgで、msys-rebased.zipを取ってきて解凍したもので上書きしたら、sh.exe自体は動くようになった。が、しかしgit-guiはやはり起動せず。正確には、wishのウィンドウが開くのだが、そこで固まる。
結局GUI使えないのか…と思いつつ、githubにGitのGUIのプロジェクトが登録されてないかと検索してみたら、git-colaなるものを発見したので、次はこれを試す。
基本的にREADMEに書いてある通りインストールしていけば良いようだが、リンク切れがあったり、使ったことのないpython回りのインストールを行ったので、その過程をメモっておく。ちなみに、cygwinもインストールしてある環境では、PATHを追加する際にcygwin関係よりも前に追加した方が良いと思われる。
- msysgitをインストール
先にインストール済み。
- pythonをインストール
Download Python SoftwareからPython2.5.2 Windows installerを取ってきて、C:\Python25にインストール。C:\Python25 をPATHに追加。
- PyQt4をインストール
Riverbank | Software | PyQt | PyQt4 Downloadより、PyQt-win-gpl-4.4.2.zipをダウンロードしてインストール。C:\Python25\Scripts;C:\Python25\PyQt4\bin をPATHに追加。
- MinGWをインストール
この後インストールするsimplejsonをコンパイルするのに必要なので、先にMinGW - Minimalist GNU for WindowsよりMinGW 5.1.4を C:\MinGW へインストール。C:\MinGW\bin をPATHに追加。
- simplejsonをインストール
Python Package Index : simplejson 1.9.2をダウンロードして解凍。コマンドラインより、解凍後のフォルダ内で
python setup.py build -c mingw32 install
としてインストール。(最初easy_installでインストールを試したが、上手く行かなかったので。)
- fileコマンドをインストール
File for Windowsから、バイナリを取ってきてインストール。解凍して C:\Program Files に入れ、C:\Program Files\file-4.25-1-bin\bin をPATHに追加。
- colaをインストール
git-colaから最新版をダウンロードし、C:\Python25\Lib\site-packages\cola に解凍。
- ショートカット作成
下記内容のファイルをcola.batとして保存。
C:\Python25\python.exe C:\Python25\Lib\site-packages\cola\bin\git-cola
作ったcola.batを実行すればGUIのウィンドウが開く…と思ったら、そう甘くは無く、既にgit-initなどで作成済みの既存レポジトリで実行しないとダメでした。なので、ショートカットをGitのレポジトリーにコピーしておく。これで一応使えるようになった。
使い方は、適当に弄ってみた限りでは、変更のあったファイルを一覧表示して、addするかどうかを指定し、ログを書いてコミットしたりといった作業が可能なようだ。まあ何も無いよりはいいかも知れない。
と、ここまでやってみたものの、Git入門 - Windows環境での利用によれば「Windowsのファイルシステムでは、Gitの特徴をうまく生かせず、Linux環境のようには高速に動作しないそうです。」といったこともあるようなので、結局Linux上でコマンドラインでのみ使用することになりそう。
第28回 Ruby/Rails勉強会@関西に参加
日本Rubyの会 公式Wiki - 第28回 Ruby/Rails勉強会@関西
http://jp.rubyist.net/?KansaiWorkshop28
自転車で京女の前の坂を汗だくになって登った上、ぎりぎりの到着でばたばたしてしまい、エラトステネスの嵐はちゃんと聴けなかった。
id:secondlifeさんのGitの解説は、これは使ってみたくなるなあ、という強力なもので、この日一気に信者を増やしたんじゃないだろうか。
branch切り替えとか異様に高速だったんですが、ファイルが大量にあってもやはり切り替え速いんだろうか。Subversionの場合、特にeclipseでSubclipse使って作業したりすると逐一時間がかかって苛々しまくるのだが。それからTrac+Subversionの連携は便利だけど、Trac+Gitも良い感じに組み合わせられるのかな?とか、windowsで問題なく使えるようにGUIクライアントとかeclipseプラグインとか出ないと辛いな、とか、良く考えるとGitをメインに使うには色々なハードルが。
ちなみにGitは「ギット」なのか「ジット」なのかどっちなんだ?と思っていたんだけど、皆さん普通にギットと発音していたのでそうなのか?なんとなくジットかと思っていたのだけど。
初心者レッスンはirbの説明。irbをカスタマイズして使おうとか考えたことがなかったので、なるほどという感じで楽しめた。okkezさんのブログのエントリに設定ファイルが載っているので、とりあえずこれをそのまま頂いて使おう。
演習では、irbの結果表示を消す方法が分からずに苦労してしまった。一応conf.return_format = ""で消えたけど、こんなんでいいのか。あとはirb_context.echo = falseとか。
以下は計算100もどきの回答。なんかこの前の石取りゲームと似た感じになってしまうので、ステートはlambda使ってみたりした。rubyだったら、State/Strategyといったパターンは、クラス使う替わりにlambdaでやるのも良いかも。コード長いけど、表示とロジックは分離してないと気持ち悪くて仕方ないので、個人的にはこれで良し。include Observableするだけで一発でObserverが書けるrubyは最高。
require "observer" QUESTION_NUMBER = 100 class UI def initialize(hundred_calc) @hundred_calc = hundred_calc @hundred_calc.add_observer(self) @response = Response.new(@hundred_calc) @hundred_calc.update(nil) end def update print @response.render exit if @hundred_calc.state.nil? @hundred_calc.update(gets.strip) end end class Response def initialize(hundred_calc) @calc = hundred_calc end def render send(@calc.result.to_s) end def greeting "計算 100 開始\n" + select_level end def select_level "難易度を選択してください。1:かんたん 2:むずかしい >" end def level (@calc.level == 1 ? "かんたん" : "むずかしい") + " を開始します。Press Enter >" end def first_question question end def correct_continue correct + question end def error_continue error + question end def correct_check_point correct + check_point + question end def error_check_point error + check_point + question end def correct_finish correct + summary end def error_finish error + summary end def correct "正解\n" end def error "不正解 (正解: #{@calc.answer(@calc.question_count - 1)})\n" end def question "#{@calc.question(@calc.question_count)} = " end def check_point "#{@calc.question_count} 問突破\n" end def summary m, s = (@calc.end_time.to_i - @calc.start_time.to_i).divmod(60) check_point + "#{QUESTION_NUMBER} 問終了しました\n" + "正解 : #{@calc.correct_count} 問\n" + "不正解 : #{@calc.error_count} 問\n" + "タイム : #{m} 分 #{s} 秒\n" end end class HundredCalc include Observable LEVELS = {1 => :easy, 2 => :difficult} attr_accessor :state, :input, :result, :level, :correct_count, :error_count, :question_count, :start_time, :end_time, :questions def initialize @level = nil @correct_count = 0 @error_count = 0 @question_count = 0 @start_time = nil @end_time = nil @questions = [] initialize_states @state = @initial_state end def update(input) @input = input @state.call changed notify_observers end def correct?(question_count, answer) @questions[question_count].correct?(answer) end def question(question_count) @questions[question_count].expression end def answer(question_count) @questions[question_count].answer end private def create_question(question_count) @questions[question_count] = Question.new end def initialize_states @initial_state = lambda do @result = :greeting @state = @select_level_state end @select_level_state = lambda do if LEVELS.include?(@input.to_i) @level = @input.to_i Question.send(LEVELS[@level].to_s) @result = :level @state = @start_state else @result = :select_level @state = @select_level_state end end @start_state = lambda do (0...QUESTION_NUMBER).each {|i| create_question(i) } @question_count = 0 @start_time = Time.now @result = :first_question @state = @question_state end @question_state = lambda do correct = @input =~ /\A-?\d+$/ && correct?(@question_count, @input.to_i) if correct @correct_count += 1 else @error_count += 1 end @question_count += 1 if QUESTION_NUMBER <= @question_count @end_time = Time.now @result = correct ? :correct_finish : :error_finish @state = nil else if @question_count % 10 == 0 @result = correct ? :correct_check_point : :error_check_point else @result = correct ? :correct_continue : :error_continue end @state = @question_state end end end end class Question @@expression_builder = nil def self.easy @@expression_builder = lambda do op = %w[+ - *][rand(3)] left = rand(10) right = op == "/" ? rand(9) + 1 : rand(10) "#{left} #{op} #{right}" end end def self.difficult @@expression_builder = lambda do op = %w[+ - * /][rand(4)] if op == "/" #除数が大きいとつまらないので right = rand < 0.9 ? rand(20) + 1 : rand(99) + 1 left = right * (rand(100 / right) + 1) else right = rand(100) left = rand(100) end "#{left} #{op} #{right}" end end attr_reader :expression, :answer def initialize @expression = @@expression_builder.call @answer = eval(@expression) end def correct?(answer) @answer == answer end end if $0 == __FILE__ UI.new(HundredCalc.new) end