2012年 2月 の投稿一覧

mongo DBのバックアップファイルから特定のドキュメントをリストアする

REMP開発中に誤爆して本番環境上の開発者のプレイリストがうっかり意図しない内容に更新してしまうというケースが生じたため、デイリーでとっているmongo DBのバックアップから復旧させてみました。*1
行いたいのは、特定のユーザのプレイリストのみの復旧なので、単純にmongorestoreする訳にはいかないので、方法として以下の手順で復旧させました。

  1. バックアップファイルを展開する
  2. ローカル環境のmongo DBに一度まるごとリストアする
  3. ローカルにリストアしたmongo DBから該当ユーザのプレイリスト情報(特定のコレクション内の1ドキュメント)をJSON形式で保管する
  4. ローカルで出力したJSONをサーバ側のmongo DBでインポートする

以下、復旧した際のメモをば…。

バックアップファイルを展開する

まず、保管されていたバックアップファイルを展開します。

[ローカル環境]
% tar xvzf rempdb-20120228.tgz
rempdb-20120228/
rempdb-20120228/rempdb/
rempdb-20120228/rempdb/library.bson
(中略)

ローカル環境のmongo DBにリストアする

前段で展開されるバックアップファイルの中身のファイルがJSONであれば、直接テキストファイルなので編集して該当するユーザのコレクションのみの抽出も行えるのですが、BSONのため一度手元のmongo dbに取り込んで処理することにしました。
リストアはmongorestoreコマンドを使えばよいので、リファレンスに従って操作して、

[ローカル環境]
% mongorestore -d rempdb --drop rempdb-20120228/rempdb
connected to: 127.0.0.1
Wed Feb 29 17:43:12 rempdb-20120228/rempdb/library.bson
Wed Feb 29 17:43:12 going into namespace [rempdb.library]
Wed Feb 29 17:43:12 dropping
(中略)

これで手元のmongo dbにバックアップファイルの内容がリストアされました。
(コマンドの末尾のパスはバックアップファイルの展開先)

該当ユーザのプレイリスト(コレクション内のドキュメント)をJSON出力する

サーバ側で復旧させたいユーザのREMPのプレイリストを抽出するためmongoexportコマンドを利用して、該当ドキュメントのJSONファイルを出力させます。
mongoexportコマンドを使うとDBやコレクションを特定し且つ、クエリを与えてヒットしたドキュメントのJSONだけを出力することができます。

[ローカル環境]
% mongoexport -d rempdb -c library -q "{uid:'1234567'}" -o playlist.json
connected to: 127.0.0.1
exported 1 records

中身を見てみると。。。

[ローカル環境]
% cat playlist.json
{ 〜 , "uid" : "1234567" }

と、クエリで抽出されたドキュメントのJSONファイルがそのまま出力されているはずなので、その内容がリストア対象であることを確認します。

サーバ側で稼動しているmongo DBへローカル環境で出力したJSONをリストアする

ローカル環境でまるごとリストアしたDBから出力した特定のドキュメントのJSONをサーバ側でインポートします。
mongoimportコマンドを利用することでJSONファイルをmongo DBにインポートすることができるので、以下の様にコマンドを入力。

[サーバ環境(リストアしたいDB側)]
% mongoimport -d rempdb -c library playlist.json --upsert
connected to: 127.0.0.1
imported 1 objects

これでサーバ上のmongo DBへバックアップファイルから取得した特定のドキュメントのみを復旧させることができました。

上記の方法でリストアできない (あるいは、--upsertオプションが効かない場合)

mongoのバージョンが古い場合、mongoimportコマンドに --upsertオプションが無い場合は、一旦ドキュメントを消してインポートすると良い…のかな。

MongoDB shell version: 1.*.*
connecting to: ***
>db.**.remove({uid:"1234567"})
>

で一度消して、

% mongoimport -d rempdb -c library playlist.json
connected to: 127.0.0.1
imported 1 objects

といった具合。

*1:あ、念のためですが他のユーザの方に影響はありません。開発用のクライアントを操作している際に生じたケースです。

「Titanium Mobile」で作るiphone/Androidアプリ勉強会に参加してきました。

Web CAT Studio (株式会社リクルートエージェント)主催のTitanium Mobileの勉強会に参加してきました。
200名近く参加されており、やはり注目の技術なのだなと実感しました。(7割近くがエンジニアの方, あとはディレクターやデザイナーの方)

ひと通り聴き終わっての感想は…。

  • Titanium mobile = Android & iPhoneアプリが一気にできると思ってしまうけど現実は甘くない
    • 現実はソースコードの中でOS別に実装分けたり、最初から単一の環境をターゲットにしたりしている
  • ただ、iPhoneAndroidどちらか一方をターゲットにしたアプリを作るのであっても、Titanium mobileで作るメリットは大きそう (実装スピードの面で)
    • ゴリゴリとした描画や処理が必要無いアプリ(既存のWebアプリに数字を送るとか、既存のスマホ向けの画面を引っ張ってくるとか)であれば、むしろ書きやすそう
  • 灰色ハイジさん(普段はデザイナー)がアプリを作られた経験の話を聞くと、アプリ開発者のマスが広がりそう
    • スケッチから簡単な動きのあるモックをデザイナーの方が作って、細かいイベントの呼び出しやサーバ間通信、サーバサイドの実装をするといったことができたりしないだろうか(妄想)

といった具合です。
何はともあれ面白そうなので、早速何かつくってみようかな…。

以下、雑ではありますが、参加した際に作成したメモです。

Titanium Mobile intro 増井 雄一郎さん

  • HTML5をモバイルアプリに使えるのか?
    • 進化途中。Websocketで2年半…。
    • そもそもストア, Androidマーケットに載せられない
    • Java, Objective C の学習コストもある
  • HTML5 vs Titanium ?
    • 競合はしない
      • グラフとかの描画系のところだけHTML5を使うことと使い分けができる
  • 実際に使われているの?
    • 開発者数 = 30万, 3万5000件のアプリ(=iPhone & Androidアプリ)
    • niftyのココログエディタ, サイボウズLive, amadanaケータイの組み込みアプリ
    • Cybirdのアプリ(AKBのアプリ)
    • モバゲーで公開中のゲーム (ライトゲーム, 指のジェスチャーでキャラクターを切るとか)
    • MogSnap (食べ物の写真共有)
  • モバイルアプリに大事なこと = User Experience
    • Titaniumであれば、「タブを作る」という実装はそれぞれのOSにあわせた変換が行われる
  • どこまで書けるのか?
    • 大体OSがサポートする全機能使えます
  • できないことはないのか?
    • 画面描写系は弱い (線を描画する等)
    • 写真の撮影はできるが、画像のフィルタリングは厳しい
    • シューティングゲームとか更新の激しいものは厳しい

ただし、ObjCとかJavaで拡張できる。(但し両方OSに対応する必要あり)
有償のモジュールも使える

  • Titanium → Write once run anywhereではなくて、モジュールで柔軟に対応できる
    • (モジュールで柔軟に対応できることを表したキーワードを増井さんが言われたのだけどメモできなかった…)
  • やっぱり得意なのは...
    • フロントエンド系モバイルアプリの開発
  • おすすめする本・Webサイト等の話
    • Titanium Mobileアプリ開発入門
    • Titanium Mobileで開発するiPhone/Androidアプリ
    • gihyo titanium (倉井さんの連載)
    • ti.masuidrive.jp (増井さん個人のページ)
  • ところでAppcelerator社のビジネス
    • Titanium用追加Native Module (グラフ描画とか) の販売
    • サポート
    • 認定トレーニング(12万円, 2日間)→トレーニングの最後のテストにパスするとロゴ等が利用できるらしい。

Titanium Mobileで作るアプリ事例紹介1【Zaim】 閑歳 孝子さん

  • 自己紹介(ユーザーローカル所属)
    • IT系ニュースサイトの記者からWebディレクター、そしてWebエンジニアへ
  • サービスの紹介
    • zaim(ザイム)
      • ソーシャル家計簿アプリ (半年前にリリース)
        • 簡単に入力できて他の人と比較ができる!
      • 日本語&英語対応, 20貨幣対応 (円から南アフリカの通貨まで), 8万ダウンロード
      • お金のAPIが面白そうだったのでOAuth APIも提供しています。
        • レストランに食事へ行ったときに写真を取って写真は別サービス、会計情報はzaimへ!
        • OCN家計簿は連携例
      • Android版は現在準備中…。
  • サービスのリリーススケジュール
    • 2010年末に思いつく
      • まずPHPでサーバサイドを開発→Titanium mobileでiOS対応版開発→リリース
  • なぜTitanium mobile?
    • Objective C, Javaの経験がなかった
    • 昨年2月リリースされたMogSnapがTitanium mobileで作られたことを知った
    • Titanium mobileの素晴らしいところ
      • サンプルが多い
      • Javascriptがかければよい (エンジニアではない人ももっとやるべき)
      • コミュニティがしっかりしてる (質問したら回答してもらえる環境)
  • Titaniumの注意
    • 現実問題として両対応は一筋縄にはいかない。
    • リッチなアプリは厳しい感じ (※ここは増井さんも言われていた通り、やはり
  • 実装
    • Common JS・シングルコンテキストで実装するスタイルが推奨されている(らしい)
      • Android対応アプリを作るためにはこのスタイルが必須なのでは?
      • Githubにサンプルが多く上がっているので見てみるとよい
    • CoffeeScriptのサンプルも参考になる
  • 実際のソースコード
    • 共通モジュールを読み込む → 設定 → 部品のイベント実装

問題事例集… (悩めるAndroid)

  • デザイン問題
    • Androidグラデーション効かない
    • 画面の比率がひとつじゃない
    • JSSは少し使いづらい (JSS=Titaniumが提供するスタイル設定)
      • なので独自でスタイル設定用のライブラリを作っている
  • スプラッシュ画像問題
    • iPhone, iPadは規定のサイズで済むが、Androidは比率が一定でないのでボヤケる…。
    • Andoirdは単色塗りつぶしがオススメ
  • TableView問題
    • iOS向けだとgetChildrenでテーブル内の要素にアクセスできたけど、Androidだとアクセスできない!
      • 行情報をアプリ上で保持していて、修正が発生した場合は全てテーブルを作り替える様な形で対応した…。
  • OAuthAdapter問題
    • oauth_adapter.js (1種類のOAuth providerが前提になってる)
    • 変えたものをgithubで公開しますー。 (http://github.com/unicco )
  • ボタン&タブバー&ピッカー問題
    • Android OSとiOSで全く違う
    • OS別に呼び出すオブジェクトを分けている
  • フォーカス問題
    • Androidだと親ウインドウにフォーカスが効かない…。
    • Androidだとfocus, blur が同時に発行される…。
  • 多言語対応問題
    • Androidだとうまくいかないケース多数 (スペースやカンマなどが入るとNG)
      • 多言語対応が便利なスクリプトもgithubに公開しますー。

情報源

  • github
  • 最新版ビルド

絶賛募集中

Titanium Mobileで作るアプリ事例紹介2【積ん読本】 灰色 ハイジさん

  • 開発したアプリ
    • "積ん読本"
      • 社内プロジェクトで始まったけど、時間確保が困難になる… → 自分で企画+デザイン+実装することに! (開発期間2週間くらい)
      • 買って読んでいない本を管理する (デザイナだから見やすいUIで管理したい)
  • Titaniumだと簡単にテーブルが作れた!→デザイナーでも実装できそうだと思えた
  • デザインを忠実にするために
    • Titanium標準のナビゲーションバーを使うと自分の想定した色を再現できない…。→ナビゲーションバーを自作して解決。
    • スクロール
      • 通常は上から下へだけど「積ん読」をさせるには下から上へスクロールするできるUIが欲しい
      • ScrollViewに高さ4400pxの背景画像を入れて、積ん読の画像をTable viewで重ねて .scrollTo メソッドでスクロールさせた
        • そうするとスクロールしている最中の様子がみえてしまう…
        • interval入れてスクロール状況を監視させて、スクロールが完了したら表示させる様にした。
    • アイコンの話
      • 細かいところまでこってます。
        • f:id:hideack:20120214205103p:image
        • 実は背表紙の文字にそれぞれ意味ある言葉が入れてある。アイコンだけど拡大されたときに読める様に。
        • iPhone用にアイコンを作るときには「角丸処理」重要。(Apple側で角取り処理されてもきれいな縁が出る様にしておく)
      • アイコンを作るまで…。
        • レイヤーをたくさん重ねて作ってます。
        • デザイナーがどうやってアイコンとか作ってるかを知ってもらいたかった

PUSHERでWebhookを行ってみる

PUSHERWebHookができるという記事がブログで紹介されてますよ!と会社で教えてもらったので、試してみました。
先日試してみたwebsocket版の通知とは異なってWebHookなのでユーザが設定する任意のURLに対してPUSHリクエストがあったことをHTTPのPOSTで通知する仕組みになります。
(下の図の赤字で書いたWeb Hookの部分)
f:id:hideack:20120205220755p:image
ですので、PUSH通知できる対象はブラウザ等では無く、Webサーバ上で稼働する任意のアプリケーションが通知対象となります。

利用方法はとても簡単でPUSHERのコントロールパネルにWebHooksという項目があるので選択をするとWebHookを有効にするためのチェックボックスとフックする先のURLを指定する欄があるのでURLを指定します。
f:id:hideack:20120205221444p:image

そうすると、PUSHERのAPIを通してWebアプリケーションからPUSH通知が走った場合に、設定したURLへ対してPOSTリクエストが行われます。(同時にWebsocketで接続中のWebブラウザへも通知が行われる)
こうすることによって、フック先のWebアプリケーション側でもPUSH通知が走ったことを検出することができます。
PUSH通知をされた側のWebアプリケーションは、このままでは本当に真のPUSH通知元(=PUSHER)からのPOSTリクエストかどうかを判定することができませんが、PUSHERから通知されたリクエストには、

  • X-Pusher-Key
  • X-Pusher-Signature

という二つのHTTPヘッダが付いていて、PUSH通知内容(POSTリクエストの内容)に対するHMAC SHA256が付いているので、この内容がPUSHER APIの秘密鍵から作られるSHA256のメッセージダイジェストの内容と一致するかを確認することでPUSHERから通知されたことを確認することができます。

以下、ドキュメントに掲載されているサンプルのままですが、読めばシンプルで分かりやすいと思います。

<?php
$app_key = $_SERVER['X-Pusher-Key'];
$webhook_signature = $_SERVER['X-Pusher-Signature'];
$body = http_get_request_body();
$expected_signature = hash_hmac( 'sha256', $body, $app_secret, false );
if($webhook_signature == $expected_signature) {
$payload = json_decode($body);
foreach($payload['events'] as &$event) {
// do something with the event
}
header("Status: 200 OK");
}
else {
header("Status: 401 Not authenticated");
}
?>