Ruby on Rails の youRoom と Apache Shindg を連動させる

先日のブログに続きまして、youRoom Extension(Apps)の実装についてです。youRoom Extensionとは、次世代youRoom APIで、youRoom上でOpenSocialガジェットを動かして拡張できるようにするという想定です。詳しくは、前回のブログを。
http://d.hatena.ne.jp/mat_aki/20101129/1291026469

まずは、Ruby on RailsのyouRoom本体とApache Shindigを連携させるところからです。javaPHPShindigのどちらを使うか検討したのですが、Apacheだけで簡単に動かせるPHP版を利用しようと思っています。RailsがすでにApacheで動作しているので、運用も楽にできるだろうという観点からです。

まずは、Shindig本体をCheckoutします。もちろん SVN の Trank から。

svn co http://svn.apache.org/repos/asf/shindig/trunk/

と。。。構築方法よりもメインの連動方法をしっかり書きたいので、以下のブログとメモを参考にとだけ書いておきます。
http://gurimusan.blog93.fc2.com/blog-entry-75.html
http://shindig.apache.org/developers/php/build.html

MacMacportsを利用して構築メモ

sudo port -v install php5 +apache2 +pear +universal

sudo /opt/local/apache2/bin/apxs -a -e -n "php5" /opt/local/apache2/modules/libphp5.so

sudo port install php5-curl +universal
sudo port install php5-gd +universal
sudo port install php5-mcrypt +universal
sudo port install php5-mysql +universal
sudo port install php5-openssl +universal

sudo cp -p /opt/local/etc/php5/php.ini-development /opt/local/etc/php5/php.ini

Apacheの設定を行って、以下のURLにアクセスして動作確認。これが動作していればShindigのセットアップは完了です。
http://localhost/gadgets/ifr?url=http://www.labpixies.com/campaigns/todo/todo.xml

連動の1stステップはyouRoom内でガジェットを表示することを目標としてみます。

いろいろドキュメントを見てみたのですが、最初はほとんど的確なドキュメントが見つかりませんでした。はじめは、どこを探したら良いのかが本当に分からないのです。。。コンテナの情報すくない。。。

なので参考になったものをまずは紹介します。

shindigWiki https://cwiki.apache.org/confluence/display/SHINDIG/Display+a+gadget+in+a+web+page
shindigのサンプル #{svn co した shindig}/content/container/sample*.html

iFrameで表示する方法とjavascriptAPIから表示する方法の2通りがあるみたいなんですが、Shindigのサンプルはjavascriptでやっていたのでそちらで実装することにしました。

youRoom(コンテナ)側でやらないといけないことは3つ

です。上から見ていきましょう。

まずは、Shindigのコンテナ用のjavascriptをyouRoom側で表示するときに読み込まないといけません。

http://shindig-host/gadgets/js/core:rpc:pubsub:shindig-container.js?c=1&debug=1

というjsを読み込みましょう。":"で繋がっている部分で利用するライブラリを選択できるようです。Shindigで結合・圧縮してjavascriptを返してくれます。debugパラメータを付けると圧縮しないようになります。上のURLはおそらく全部のせ。

実際のyouRoom側の実装は、こんな感じ。

= javascript_include_tag gadget_server_url("/gadgets/js/core:rpc:pubsub:shindig-container.js?c=1&debug=1")

ヘルパーメソッドとして、ShindigサーバのURLをこのように返すメソッドを定義しています。

  def gadget_server_url(path)
    "http://ext.youroom.local:3333" + path
  end

次に、javascriptでガジェットをレンダリングします。変数があるのでRailsのViewで記述しています。

var gadget = shindig.container.createGadget({specUrl: "#{xml_url}"});
gadget.setServerBase('#{gadget_server_url("/gadgets/")}');
shindig.container.addGadget(gadget);
shindig.container.layoutManager.setGadgetChromeIds(['gadgets-gadget-chrome-#{gadget_mod_id}']);
shindig.container.renderGadget(gadget);

1行目でOpenSocialガジェットのURLを渡してガジェットオブジェクトを生成します。2行目でShindigサーバのURLを指定しています。3行目でガジェットを操作するコンテナオブジェクトに登録し、4行目でどの場所に配置するのかをlayoutManagerというクラスを利用して設定しています。引数はガジェットを配置するDIVのidを配列で渡します。最後に5行目でレンダリングしています。この一連の処理を行うことでjavascriptからガジェットがレンダリングできます。

最後に、metadataのAPIを定義します。このmetadataですが、実はまだちょっと分かっていません。ユーザの情報などを返すべきなのですが一旦はSampleのデータを参考にモックで実装したところ動いています。URLは "/gadgets/metadata" で、jsonでデータを返すという仕様のようです。以下、routesの定義部分とmetadataのアクションのモック部分です。

api.gadget_metadata "/gadgets/metadata", :controller => "gadgets", :action => "metadata", :conditions => { :method => :post }
  def metadata
    # FIXME: mock
    render :text => '{"gadgets":[{"iframeUrl":"http:\/\/ext.youroom.local:3333\/gadgets\/ifr?container=default&v=ba5a1d09f63dbf63ee3efa2a04bcccbf&lang=default&country=default&view=&url=http%3A%2F%2Fext.youroom.local%3A3333%2Fsamplecontainer%2Fexamples%2FSocialHelloWorld.xml","features":["globals","core.config","core.util","shindig.auth","core.json","core.io","core.log","core.prefs","core.legacy","core","core.none","rpc","locked-domain","auth-refresh","security-token","osapi","settitle","dynamic-height.util","dynamic-height"],"links":[],"icons":null,"views":{"":{"view":"","type":"HTML","href":"","preferedHeight":"","preferedWidth":"","quirks":"","authz":"","oauthServiceName":"","oauthTokenName":"","oauthRequestToken":"","oauthRequestTokenSecret":"","signOwner":"","signViewer":"","refreshInterval":"","dataPipelining":[]}},"author":"","authorEmail":"","description":"The Social Hello World Application Displays multilingual hello messages","directoryTitle":"","screenshot":"","thumbnail":"http:\/\/localhost:8080\/","title":"Social Hello World","titleUrl":"","authorAffiliation":"","authorLocation":"","authorPhoto":"","authorAboutme":"","authorQuote":"","authorLink":"","showInDirectory":"","showStats":"","width":"","height":"","categories":["",""],"singleton":"","scaling":"","scrolling":"","moduleId":1,"url":"http:\/\/ext.youroom.local:3333\/samplecontainer\/examples\/SocialHelloWorld.xml","userPrefs":[],"oauth":[]}]}'
+  end

以上、3つを行うとガジェットがyouRoom上で動作することが確認できました。以下は cacoo opensocial gadget をyouRoom内に表示したところです。

いい感じに動作しています。このGadgetはOAuthやSocial APIなどを利用していないため、現在の実装でもうまく動作するようです。ここまでで1stステップの目標としていたガジェットをyouRoom内に表示するができました!
次回は、トピックの投稿時にガジェットのデータを一緒に保存することを目標にやってみます。

[youRoom] 次世代 youRoom API の開発を始めます!

現在、youRoomではAPIを公開して、外部の開発者の方に連携アプリケーションを開発できるようにしています。
しかし、現状はTwitterと同じようなAPIを提供しているだけなので、youRoom本体からリンクを張って別アプリに遷移して操作するという風なアプリケーションしか作成できません。

やはり、youRoom本体で操作できるようなアプリのほうがユーザにとって非常に使いやすいものになるであろうと考え、そのような拡張を開発できるようなプラットホームにしようと検討中です。

yammerでは、似た様なことがもう実現可能になっているようです。イメージは似ています。
http://blog.yammer.com/blog/2010/09/the-new-yammer-is-here.html

yammerでは、まだ公開されていないようですが、独自の拡張方法があるようです。youRoomではOpenSocialを利用して実現しようと現在調査を進めています。OpenSocialのコンテナの情報は、非常に少なく現在僕も困っているのですが、今後OpenSocialのコンテナを開発する人がでてきた際に参考になるようにこのブログで記事を残そうと思います。さらに、 Ruby on RailsApache Shindig との連動の記事は全く見たことがないので、そのような方にも参考になるかと。また、「この設計ではうまくいかないよ」などご意見も貰えたらと思っています。また、実装は Apache Shindig の最新版を利用しているので、そのあたりも面白いと思います!!!

まずは軽く現在のイメージ。

トピック作成時に、どんなアプリを添付するのか選択でき、その中で必要な項目があれば入力する感じ。イメージでは、アンケートアプリを作成している雰囲気にしてみました。添付が完了すると、他のユーザがアンケートに答えるのですが、ここまでyouRoom上で実施できるのが一番のメリットですね。イメージは、アンケートに答えている感じです。

こんな雰囲気の拡張ができるようにyouRoomでは、OpenSocial(Apache Shindig)を利用して実現しようと思っています。

まずは、ここまで。次回は「Apache ShindigRailsから利用するには」くらいを書こうと思います。

[Rails][発表] RailsDevConで発表してきました!

遅くなりましたが、ブログを。そして、ブログも久々。。。

先日、RailsDevConという国内初のRailsのカンファレンスで発表してきました。
id:moro さんより「SKIP, youRoomまわりで何かしゃべってみませんか」とお声をかけていただき発表する機会をいただきました。

資料はこちら。

言いたかったとことは、
この記事や http://www.atmarkit.co.jp/news/201011/25/rails.html

このつぶやき

積極的に外のサービスを使って楽して、本質的なところに思いっきり注力する、と。素晴らしい。自分のとこは仕事のための仕事みたいの多いからなぁ。 #railsdevconless than a minute ago via HootSuite

でなんと一言でまとめられています!

具体例を知りたい人は、資料などを見ていただければと。

初めての一人での長めの発表でした。発表資料の流れをレビューしていただいた id:moro さんや @littlestarling さんに感謝。そして、参観にきてくれた id:kuranuki に感謝です。ありがとうございます。そして、つたない発表でしたが聞いていただいた皆さんありがとうございました。さらに、発表の機会をいただいた運営の皆さんありがとうございました!すばらしい運営でした。

発表中にありますが、今期は有名になるようにもっと動いてみようと思っています。僕でよければ何か話したり、書いたりしようと思っていますのでお声掛けいただければ嬉しいです!!

RailsDevCon自体ですが、さまざまな発表がありとても刺激的でした!参加されていないかたは資料やUSTなどをご覧になるとよいと思います。

SearchLogic と Rails 2.3.10 を利用する際はご用心

先日のyouRoomで発生した間違いメール送信の不具合について調査しましたので、公開いたします。

発端は、Rails2.3.9のセキュリティーアップデートでした。youRoomでは、Rails2.3.9を利用していたためRails2.3.10へのアップデートを行ないました。基本的な動作確認はとれたのでリリースしたのですが、上記のような不具合が残ってしまいました。

原因は、SearchLogicの実装とRails2.3.9からRails2.3.10までのあるコミット(セキュリティー対応以外)にありました。

http://github.com/rails/rails/commit/9b78af95be01534b3f1a4187024d803313c0ee1f

実際には、このような現象が発生しました。

# Group -has_many-> Participation
# Group.joined           ==> defined named_scope
# Group.user_id_not_null ==> genarated named_scope by searchlogic

g = Group.find(1)
# SQL) SELECT * FROM `groups` WHERE (`groups`.`id` = 1)

g.participations
# SQL) SELECT * FROM `participations` WHERE (`participations`.group_id = 1)

g.participations.joined.user_id_not_null
# Acctual SQL) SELECT * FROM `participations` WHERE ((participations.user_id IS NOT NULL) AND (participations.status = 'ACTIVE'))
# Collect SQL) SELECT * FROM `participations` WHERE ((participations.user_id IS NOT NULL) AND ((participations.status = 'ACTIVE') AND (`participations`.group_id = 1)))

g.participations.joined.user_id_not_null  #  Redo
# SQL) SELECT * FROM `participations` WHERE (`participations`.group_id = 1) AND ((participations.user_id IS NOT NULL) AND (participations.status = 'ACTIVE'))

本来は、Acctual SQLの部分でCollect SQLのようにgroup_idで絞ったSQLが発行されるはずですが、実際にはSQLが抜けた状態になりgroupで絞ることができませんでした。このため、正しくない送信先が選ばれることになり、昨日の間違いメールが発生してしまいました。

このような実装で発行されるSQLが上記の様になってしまうのは、Railsを利用している人にとっては非常に恐ろしいところです。さらに、2度目に実行すると正しい結果になるので、非常に見つけにくい現象であると思います。

なぜ、このような現象が発生したのかソースを追って解説します。

g.participations.joined.user_id_not_null
                       ^^^^^^^^^^^^^^^^^

この部分で、本来定義されていないメソッドなので、SearchLogicのmethod_missingに処理が渡ります。

http://github.com/binarylogic/searchlogic/blob/master/lib/searchlogic/active_record/association_proxy.rb

proxy_reflection.klass.send(method, *args)

を解釈すると

Participaion.send('user_id_not_null')

となります。このようにnamed_scopeのChainから一旦抜けたような感じになります。

ここから次にまだメソッドは存在しないので

http://github.com/binarylogic/searchlogic/blob/master/lib/searchlogic/named_scopes/conditions.rb

この辺りで、named_scopeが定義されます。そして、そのメソッドが send で呼び出されます。Line 75 ですね。この時、以下のような呼び出しになります。

Participation.user_id_not_null

ここで、Rails2.3.10の差分が効いてきます。

http://github.com/rails/rails/commit/9b78af95be01534b3f1a4187024d803313c0ee1f
http://github.com/rails/rails/blob/9b78af95be01534b3f1a4187024d803313c0ee1f/activerecord/lib/active_record/named_scope.rb

Rails2.3.10: unless (Scope === proxy_scope || ActiveRecord::Associations::AssociationCollection === proxy_scope)
Rails2.3.9:  unless Scope === proxy_scope

これの差分は、一般的に proxy_scope が AssociationCollection の場合は、同じ Where 区が登録されるのを防ぐために変更されました。

しかし、SearchLogicで動的に生成した場合も上記の様に呼び出した時 proxy_scope が AssociationCollection となります。これまで、group.id の Where区の制限が、with_scope で引き継がれていましたが、引き継がれなくなってしまいます。

もう少し具体的に書くと、正しい場合はnamed_scope.rbのL182あたりで以下の様に呼ばれるのに対して、正しくない場合は一つ上のif文でfalseとなりwith_scopeで引き継がれずに実行されてしまいます。

if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
  with_scope current_scoped_methods_when_defined do # ここのwith_scopeで group_id の制限がかかる {:conditions => {:group_id => 1} } という感じ
    proxy_scope.send(method, *args, &block)         # Participation.find ...
  end
else
  proxy_scope.send(method, *args, &block)
 end

そのような流れで、以上なデータが取得され今回の現象が発生してしまったようです。最後の方は、非常に複雑で文章で表現するのは難しく、理解しがたいかと思いますが、現象としては理解いただけるかと思います。

youRoom側の対応としては、Rails2.3.9のセキュリティーの問題も今回のSearchLogicの問題も致命的ですので、一旦、Rails2.3.10の必要なパッチをRails2.3.9に当てる形で対応しています。
http://gist.github.com/632068

この問題は、SearchLogicとRails2.3.10を併用したときのみ発生するようですが、非常に重大ですのでSearchLogicへも連絡しようと思っています。

[イベント参加] ANTENNAに参加してきました

福岡で開催された ANTENNA と言うイベントに参加して来ました。

スタートアップのwebサービスのデモと言うことで youRoom を紹介させていただきました。
直前に @pastaonly さんが作ってくれたムービーが大好評でどうやって作ったんですか?いくらで作ったんですか?と質問の嵐でした。

他にデモしていたサービスは Gemediar という動画をマルチプラットホームで再生するためのサービス、エンジンでした。YouTubeだと著作権の問題があるので独自で動画のマルチプラットホーム配信を行いたい時に使うとよいそうです。

もう一つは、福岡で大人気のサービス cacoo でした。いや、国内を代表するサービスですね!まさに正式サービス公開を当日に合わせて発表されていました。実際のユーザ数の数字などを含めてプレゼンしていただいたので非常に参考になりました。

個人的に福岡に行くのが初めてでしたが、イベント参加者の皆さんがとてもフレンドリーで福岡を愛している雰囲気があってとてもよかったです。また、社長やフリーランスの方がとてもおおく刺激的な話がたくさん聞けました。

今回のANTENNA は vol.0 でしたが、是非今後も福岡で続けて行って欲しいなと思います。また参加したいです!

[PivotalTracker] gitはもちろんPivotalTrackerをもっと楽しく!

先日、下のエントリを参考に git でコミットすると、「いぇーい!」っと喝采してくれるように自分のmacに設定を行ないました。
http://collectiveidea.com/blog/archives/2010/08/03/happy-git-commits/

それを一人で楽しんでいたところ、うちのチームの @chihajirou さんに「楽しそうでよいですね」っと言われました。なので、みんなにもこの喝采を浴びてもらいたいなぁっと思って、何かが終わったタイミングで喝采の音を出してあげようと考えました。

そこで、ちょうど良かったのは PivotalTracker でした。最近、SonicGardenでは進捗管理に PivotalTracker を利用しています。あるタスクが完了すると "Finish" のボタンを押して仕事を完了にします。その時に喝采を浴びせてやれば、嬉しくなってもっと仕事が捗るはずだ!っと思ったのです。

もちろんRubyで組んでみました。下記は、mac版です。

ただ喝采を浴びせるだけだと、誰が何やったか分からないので、Google の TTS API をつかって内容を音声でしゃべらせるようにしました。

これを1日流していたところ、 @kuranuki さんがツイートしてくれてそれに反応して欲しいと言ってくれた方がいらっしゃったので、せっかくなので公開します。

使い方は、rubyをインストールして以下のgemをインストールしてください。

  • pivotal-tracker
  • rest-client

上のgitのブログから喝采のwavをDLしていただいて、 .git/ というディレクトリに置いてください。(もしくは、スクリプトを書き換えてください。)

PivotalTracker::Client.token のところにPivotalのTokenを埋め込んでください。

ruby congratulate_done.rb

上のコマンドを実行していただければ、新しく finished や accespted になったら自動的に読み上げて喝采を浴びせてくれます。

デモを録画してみた。

さらに、メリットとしては、英語化を進めているSonicGardenとしては、英語を書かないとちゃんとGoogleのTTSでしゃべってくれないことです。
Google TTSは英語にしか対応していないので、日本語があるとスルーされてしまうのです。
これで、モチベーションも上がるし、英語も強制されるし、英語を日々ヒアリングするしと、いいことづくめですね!!!

Enjoy Pivotal!!

RubyKaigi2010に行ってきました

かなりブログにするのが遅くなってしまったのですが、RubyKaigi2010に参加してきました。
項目と最初の方は直後に書き始めたんですが、あとは書けずで覚えている限りを書いてみました。

Mongodb

NoSQL潮流は「パフォーマンスがよいだけでなにがいいねん!」と思っていましたが、オブジェクトDB?であるmongoDBという話し方を聞くと、メリットが理解出来た。
Rubyはオブジェクトを扱っているに、永続化レイヤーになった途端にリレーショナルDBで扱うのはシンプルじゃないよねということのようだ。
質問で「マスターを扱う時はどうしたらいいんだ?」というのがあって、その回答がそれはリレーションを利用するのが自然だという回答だった。僕の中でNoSQLのモデリングの疑問を解決してくれたように思う。リレーショナルと、エンベッド?の扱い方の違いが何となく理解出来た。質問者は id:ursm だったように思うんだけどさすがだ。

TermtterKaigi / jugyoさんのセッション

実はほとんどtermtterは使っていないんだけど面白かった。youRoomのtermtterプラグインを作ろうかと思ってたんだけど無理だった。wassrプラグインがあるから作れそうというところまでは理解できた。

るりまサーチ / 須藤さんのセッション

全文検索エンジン groonga をるりまサーチの全文検索に利用する例。ちょうど全文検索が課題になっているプロジェクトがあったので参考になった。まぁ、昨年のRubyKaigiでClearCodeさんと隣になっていて紹介してもらっていたんですが、今回でちょうど思い出せてよかったです。携帯でのプレゼンはさすが須藤さん。

Kakutaniさんのセッション

なんか熱いですよね。RubyKaigiでこういった話題をカッコよくこなすのはすごいなぁ。

Asakusa.rbのセッション

ちょっと覗いてみただけでしたが、非常に面白かった。Yehuda KatsさんがRails3について話していて非常に参考にもなりました。
Rails2.3.9にして、Warningをなくしたり、bundlerにしてみたりしてからRails3に上げるのがおすすめっていってました。

Chad fowlerさんのセッション

情熱プログラマーを読んでいたのですが、何かまた違った感覚がありました。
将来やりたいことを待っていても仕方ない、今からやり始めよう!というのが一番印象に残っています。