ひげろぐ

技術者として仕事人としての思うところや覚え書きやらです
Home      Profile      Works     
2010-08-29

RubyKaigi2010の感想

2日目だけ行って来ました。

実はRubyKaigiには今回初めて行ってみたのだけれど、お祭りみたいで楽しかった。
これは来年もまた是非行きたいなあと思いました。

以下とりとめもなく。

超絶技巧 Ruby プログラミング

アホなことをまじめにやっている姿が感動的。
最初の参加セッションでいきなりエンドロールを見せられてしまった。(個人スポンサーだったので自分の名前も出た!)

ニコ動に上がっていたので暇な時間に是非見てみて欲しい。息抜きの時間に最適です。

Vim

セッションをやっている201-Aに行ったら超満員御礼状態。
入り口から人があふれていて立ち見状態だった。
というわけで入るのは断念。

隣でやっていたEmacsの方はだいぶ余裕があるようだった。
RubyistはVim使いが多いのかな?

Cucumberハンズオン

一番楽しみにしていたセッションだったのだけれど、環境構築が出来ていなくて\(^o^)/

当日出発前にいろいろ入れて大丈夫と思っていたら、Ruby自体がopensslやらいろいろと無効な状態でのインストールになっていて、いざ動かそうとしたらRailsがまともに動かなかったという。
これは必要なgemが入らないとかそんなチャチなもんじゃねえ(ry

Cucumberを動かすための環境構築のサポートもしてくれていたけれど、なんというそういうサポート以前の問題だこれは。
そんなわけで動かすのはあきらめて、コードの書き写しだけひっそりとやってました。

apt-getで必要なパッケージを入れてRubyの再構築というのも並行してやっていたのだけれど、会場のWifiも熾烈な帯域の奪い合いが実感されるほどのつながったりつながらなかったりっぷりで、終わったのがちょうどセッション終了と同時でしたとさ。どっどはらい。

ライトニングトークス

制限時間内に終えるとKY呼ばわりされるおかしなLTでした。
何が言いたいかというと、ドラ娘かわいかったです。

ネタ的にはGoogleWave本の人が全部持って行ったかんじ。
本が刷り上がった日に開発終了のお知らせとか涙なしには聞けない。
X-Ruby、O-Ruby、F-Rubyも最高でしたw

そのほかで個人的に刺さったのはMessagePack、Almanac for Ruby、Lingoあたり。
折を見て追いかけてみようかと。

それから達人出版会の今後には期待。
売り上げの配分とか金銭的な面が難しそうだけれど、うまいことできれば相当面白いことになりそう。

懇親会

人見知り的にはある意味大変な場所ではあるが何人かと話せてそこそこ有意義な時間を過ごせた。

会ってみたい人の名前を書く、というのはいきなり振られたのですでにMatzと書きました。
すみません。すでに帰途についていること承知でした。
でも生Matzを見たことがないのでいつか実際会ってみたいです。

トートバッグ

思いもかけずもらえたのでうれしかった。
作りもしっかりしていて便利そうなので常用させてもらいます!

交通アクセス

横浜の片田舎からだとつくばまで片道3時間というのはさすがに遠かった。
これは連日参加するなら泊まり必須ですねー。

来年はどこでやるのか分からないけれど宿泊も要検討事項としておく。

iRubyKaigi

出発直前に入れてみたが、自分の参加したいセッションを整理するのにとても便利だった。
作者様ありがとうございます。

すてきな会場の雰囲気

心浮き立つような祭りの空気があった。
と言っても別に騒がしいわけではないんだけど、なんか良い雰囲気だった。

廊下にいくつか机といすが並べられていて、作業スペースとしてもいいかんじに見えた。
参加したいセッションがなくても、思い思いに作業しつつ時間をつぶせるかんじ。

ぶらっと好きなときにやってきて、ぶらっと好きなときに帰れるような気楽な空気でもありました。
文化祭とかが感覚としては近い?

スタッフさんたちの対応も気持ちよくてよかったです。
運営ほんとうにお疲れ様でした。

2010-08-25

iPad開発を始めたい人へ

周りにiPad開発してみたい!という開発者がいくらかいるのでひとつ。
言語問わずある程度UIを持ったアプリ開発の経験のある開発者向けに。

しかしながらすでにけっこうためになるまとめがあるので主にそちらを参照。

と、丸投げ。
あとは思うところをちょっとだけちょっとだけ。

開発の準備

iPhone Developer Programへの登録が必要。
10800円ケチケチせずに払う。

登録時日本語使わない。
使うと修正のためにオペレーターとのメール経由でのやりとり数週間コースに突入。

IDEはおとなしくXCodeで。補完がないと無理。

SDKをひとつのフレームワークとしてとらえる

フレームワークの提供する枠組みに沿って開発すれば、ちょっとしたユーティリティ系のアプリであれば割とさくっと作れる。
よくスクリプトを組んだ経験のないデザイナーの人などが「簡単に作れちゃいました」と言っているのを聞くのもそのため。

逆にフレームワークの文脈を理解するのを億劫がってオレオレに走ると苦難の道が待っている。
待っていましたとも。車輪の再発明どころじゃねえ。

基本を理解するために必要そうなドキュメントは全部目を通す。
とりあえず以下の「一般」にカテゴライズされている物は必読。

Interface Builderは使う?使わない?

最初はどっちでも。

使う場合。
フレームワークとObjective-Cの学習コストに加えてInterface Builderとの連携の仕方を覚えるのはちょっとしんどい。

使わない場合。
とはいえUIに部品を追加するたびにリファレンスと首っ引きになってたら開発が遅々として進まなくてしんどい。

いずれにしろ最初はしんどいのかと思われる。
最初はサンプルに沿って何かを作ってみると言うところからスタートすると思うので、そのサンプルに追随すればいいのではないかと。

コードによるUIの構築は基本addSubviewでUIViewオブジェクトをレイヤーのノリで重ねて行くだけ。
ナビゲーションバーにボタンを表示するなどの、関連性のあるオブジェクトの設置に関してはその処理をラッピングしてくれるメソッドが準備されている。

なので、コード書くのに慣れたらInterface Builder使わない方がむしろ簡単でよいと思う。

メモリ周りはしっかりと理解を

iOS上では残念ながらガベージコレクションが使えないので、リファレンスカウンタでオブジェクトの解放を管理する。
どのタイミングでリファレンスカウンタが増減するかをきちんと知っておかないと、メモリリークしまくったり、逆にまだ使っているオブジェクトを解放して往生する。

ちなみにObjective-C 2.0にはガベージコレクションの仕組みがある。
iOSで使えなくしているのはハード性能の制約のため。
とはいえiOS自体が徐々に下位機種切り捨ての方向に向かっているので、いずれは使えるようにならないものかと期待してしまう。

その他

過去記事。

2010-08-18

最近のiPadの使い道

iPad買っては見たが飽きてすっかり使わなくなった、なんて声もあるようだけれど、発売とほぼ同時に手に入れて以来、自分の生活の中では日用品として定着している。

最近の主な用途は以下のようなもの。

* 電子書籍(GoodReader)
* 手書きメモ(neuNotes)
* プロトタイピング(neuNotes)
* 天気予報(そら案内 for iPad)
* フォトフレーム
* 時計
* ごろ寝インターネッツ
* 開発テスト機

中でも頻度が高いのは電子書籍、メモ、プロトタイピングあたり。

特にやっぱり電子書籍が便利すぎる。
GoodReaderも横スワイプでのページ送りにいつの間にか対応していたので、普通の本のページをめくるようにすいすい読んでいけるようになった。
あとは細かい字も読みやすくなるように、RetinaディスプレイのiPad登場が待ち望まれる。

メモやUIのプロトタイピングにneuNotesが大活躍。
手書きメモはほかに有料のものも試してみたりしたけれど、フリーのneuNotesが今のところ一番使いやすい。
紙とペンはすっかり使わなくなった。

2010-08-12

ウェブアプリケーションで必ず5秒以内に応答を返す方法

ソーシャルアプリのアプリケーションサーバーは5秒とか10秒以内にリクエストを返さないとプラットフォームの中継サーバーからタイムアウト扱いにされる。
で、一定時間内に一定数以上のタイムアウトが起こると新規ユーザー登録停止などのペナルティをプラットフォームから受けるようになっている。

これはとても痛い。
なので必ず規定時間内に応答を返したいのだがどうしたらよいのか。

答えとしてはロードバランサーを利用して実現することができる。
要はバックエンドが一定時間内に応答を返してこなかった場合にロードバランサーが代わりに応答を返すというやり方。

ロードバランサー自身が5秒以内に応答を返せないくらいにいっぱいいっぱいになる可能性もあるが、そんなことは滅多にないだろう。

Apacheのmod_proxy_balancerによる設定

Apacheのmod_proxy_balancerでバックエンドから応答が返ってくるまでのタイムアウトを設定できる。
これによりクライアントがタイムアウトする前に「502 ProxyError」を返すことができる。
加えてErrorDocumentを適切に設定すればHTTP的には全くエラーが起きてないように振る舞うことも可能。

例えば以下のように設定すればバックエンドサーバーから4秒以内に応答が返ってこなかった場合にhttp://example.com/sorry.htmlへとリダイレクトされる。

ProxyPass /example http://backend.example.com timeout=4
ErrorDocument 502 http://example.com/sorry.html

このときクライアントへの応答は302のリダイレクトになり、http://example.com/sorry.htmlを表示した時点で200となる。

注意すべきこと

これでプラットフォームから課せられるペナルティの恐れはなくなったが、一方でエラー画面ばかり表示されるアプリに大してユーザーがどういう感想を抱き、どういう行動を取るかは明らか。
またタイムアウトが起きているという事は把握しておかないとその状況を改善するための適切な対処がとれなくなるということも忘れてはならない。

ご利用は計画的に。

ダメだった代替案

もっと簡単にできないものかとちょっと調べたり試した。

ApacheのTimeout

このディレクティブが扱うのはクライアントとの通信のタイムアウトであって、内部処理がどれくらいかかったからタイムアウトさせるというものではなかった。
Apache10年以上触ってるのに初めて知った衝撃の事実。

PHPのmax_execution_timeあるいはset_time_limit()

これで設定できるのはスクリプト自体の実行時間の制限であって応答時間ではない、とのことだ。

つまりスクリプト自身が外部のリソースにアクセスして応答を待っている時間などは制限を受けない。
例えばスクリプトからデータベースを叩いて応答を待っている時間などはスクリプトの実行時間ではない。

こういう事情により以下のコードはタイムアウトしない。

< ?php
  set_time_limit(5);
  sleep(10); // 寝ている時間は実行時間に含まれない
  echo 'hoge';
?>
2010-08-11

WordPressのウィジェットの作り方

今更だけどウィジェットが鬼便利。
WordPressよくできてるなあと感心する。

サイドバーに何か追加したいとき、標準のテキストウィジェットでテキストやHTMLのレベルですむものならさくっと追加できる。
簡単なPHPのコードを実行したい場合はPHP Code Widgetを使えばいける。

更にウィジェットを自作するともっといろいろなことができる。

そういうわけなので基本的な表示方法と、ウィジェットごとのオプションの設定の仕方について整理してみる。
かなり簡単。

まずは基本のHello World

テキストウィジェットでいけるレベルであるが、最初の一歩はシンプルに。

< ?php
/**
 * @package Hogehoge
 * @version 0.1
 */
/*
Plugin Name: Hoge Widget
Plugin URI: http://brass.to/
Description: Hoge Hoge Hoge
Author: akahige
Version: 0.1
Author URI: http://brass.to/
*/

class HogeWidget extends WP_Widget {
    function HogeWidget() {
      parent::WP_Widget(false, 'ほげうぃじぇっと');
    }

    function widget($args, $instance) {
      extract($args);

      echo $before_widget;
      echo 'Hello World!';
      echo $after_widget;
    }
}

add_action('widgets_init', create_function('', 'return register_widget("HogeWidget");'));

これをWordPressのプラグインディレクトリにhoge_widget.phpとでも名前を付けて保存すれば動く。
冒頭のコメントはプラグインとして認識させるためのコメント。

WP_Widgetクラスを継承

2.8以降はWP_Widgetを継承することでウィジェットを作成できる。
それより前のバージョンでは作り方が違うが、今更過去を振り返る必要はないので割愛。

コンストラクタ

とりあえずウィジェットの管理画面に表示されるウィジェット名を設定。

widgetメソッド

widgetメソッドの中身がウィジェットに表示される内容になる。

$before_widgetやら$after_widgetやらはサイドバーごとに決まっているオプション。
他のウィジェットとの和を乱さぬためにお約束として入れる。

echoが残念な感じだがサンプルなので気にしないことにする。

ウィジェットの登録

add_actionの行でウィジェット初期化時にウィジェットを登録して使用できるようにしている。
この一行がないとウィジェットが使えるようにならない。

以上。
表示はこれだけ。スゲー簡単。

ウィジェットのオプションを設定できるようにする

ウィジェットの管理画面でオプションを設定して、それをウィジェットの表示に使えるようにする。

オプションはウィジェットのインスタンスごとに保存されるので、同じ種類のウィジェットを複数配置してそれぞれ別のオプション内容に基づいた表示をさせることが可能。

class HogeWidget extends WP_Widget {
    function HogeWidget() {
      parent::WP_Widget(false, $name = 'ほげうぃじぇっと');
    }

    function widget($args, $instance) {
      extract($args);
      $title = apply_filters('widget_title', $instance['title']);

      echo $before_widget;
      if ($title) echo $before_title . $title . $after_title;
      echo 'Hello ' . htmlspecialchars($instance['name']) . '!';
      echo $after_widget;
    }

    function update($new_instance, $old_instance) {
      $instance = $old_instance;
      $instance['title'] = strip_tags($new_instance['title']);
      $instance['name'] = strip_tags($new_instance['name']);
      return $instance;
    }

    function form($instance) {
      echo '<div>title:<br /><input name="' . $this->get_field_name('title') . '" type="text" value="' . $instance['title'] . '" /></div>';
      echo '<div>name:<br /><input name="' . $this->get_field_name('name') . '" type="text" value="' . $instance['name'] . '" /></div>';
    }
}
formメソッド

formメソッドが管理画面のオプション設定フォームの内容。

get_field_nameでinputタグのnameを取得するのがミソ。
$instanceには設定済みのオプションの値が入っている。

echoが残念なかんj(ry

updateメソッド

変更後の値($new_instance)と変更前の値($old_instance)が渡ってくるので、それらを使ってオプションとして保存する内容を返す。
フォームでの入力内容を検証したり加工したりしたい場合にはここでいろいろと処理することができる。

変更後の値をそのまま使うのであれば

return $new_instance

の一行でもよい。
というか親クラスのWP_Widgetでそういう内容になっているので、それでよければupdateメソッドの定義は略せる。

特にウィジェット用のテーブルなどを作らなくてもオプションを保存できるところが素敵。

widgetメソッド

設定したオプションの内容が$instanceに入っているのでそれを使うことができる。

新しく出てきている$before_titleやら$after_titleやらもサイドバーごとに決まっているオプション。

以上。
これも簡単。

ほんとうにWordPressはよくできていると感心する。

参考

2010-08-10

ウェブアプリの負荷テストについてのメモ

負荷テストってどうやってやったらいいんだろう?
って長年思いつつ、適当にabとかで負荷かけてとりあえずDone、みたいなことでお茶を濁してきたわけだけど、最近のプロジェクトで腰を据えてやる機会があり一定の知見を得たのでメモっておく。

手順と心がけるべきいくつかの事柄について。

手順

  • かける負荷の規模を決める
  • シナリオを作る
  • ツールを準備する
  • 負荷テスト対象のサーバーを準備する
  • 負荷をかける側のマシンを準備する
  • データを準備する
  • 負荷テストを走らせる
  • 計測結果を記録する
かける負荷の規模を決める

並列度とシナリオのループ回数くらいを決めておく。
1000並列で5000ループ、みたいな。

パフォーマンスの目標値があればそれをそのまま使うか、目安にできる。

シナリオを作る

ユーザーの実際の動きを想定した画面遷移のシナリオを作る。
これはURLのリストでよい。(リダイレクトなどは考慮すること)

負荷テストをより意味あるものにするためには、実際のユーザーの動きをより忠実に再現したものであることが望ましい。
サービス開始後ならばログから精度の高いシナリオが作成できる。
サービス開始前でもクローズドベータなどで実際のユーザーにさわってもらう機会があれば、そのログからよいシナリオが作れる。

また思わぬところに潜んでいるボトルネックを洗い出すためにシナリオはすべての処理を網羅するようにする。

ツールを準備する
  • 多数のユーザーが
  • 同時にアクセスして
  • 一連のシナリオ(画面遷移)をたどる

という動作をシミュレートできるツールならばなんでもよい。
これらはアプリの負荷テストには必須。

abは指定できるURLがひとつだけなのでちょっと力不足ということになる。
単一ページのチューニングのお供にはお手軽でよいだろうけど。

このあたりは得意な言語で使い捨てのスクリプトを組んでも良いと思う。
また探せばいいツールがいくつかあるようだ。

ツール選びの基準としては上記の必須項目の他に以下のようなものがあげられる。

  • 動作が軽い
  • 設定が簡単
  • レポーティング機能がすぐれている

動作が軽くないと負荷をかける側がボトルネックになって十分に負荷をかけられない場合がある。
そういう意味でJMeterは重いのでよろしくないと思う。

負荷テストについて考える際にいろいろと参考にした『キャパシティプランニング』ではhttperfとかSiegeをおすすめしている。

テスト対象のサーバーを準備する

本番に近い構成のサーバーを準備する。
そういう意味ではサービスのローンチ直前などは本番環境でのテストがやりやすくていい。

負荷をかける側にもリソースが必要になるので、本番環境の構成の半分とか四分の一とかの構成でテストする場合もある。

ローンチ後の負荷テストに関しては理想的には本番環境と同等のテスト環境があればよいが、そんな恵まれた環境は滅多にないと思うのでいったいどうしたらいいのだろうか。
このあたり答えが出ていない。
環境構築やデプロイの自動化が十分にできていればクラウドを使うという選択もあるのだろうか。

負荷をかける側のマシンを準備する

負荷をかける側のマシンも十分に強くないと、負荷をかける側の限界が先に来てしまって負荷をかけきれないので、こちらもちゃんと準備する。
できれば複数台がよい。

ちょっと前までは負荷テスト用にマシンを複数台確保するというのはなかなか現実的ではなかったけれど、最近はEC2などのクラウドがあるので容易に必要なだけ安価に調達できるようになった。
こういうところはまさにクラウドの使いどころ。

データを準備する

サービス開始当初は問題がなくても、データがたまってきて問題が出てくるケースというのは多い。
なので大量の実データを準備することも必要。

データ投入はSQLで行うのが一番速いが、一方で実際にアプリを運用した場合に比べるとデータの抜けが発生する可能性も高い。
なのでSQLによるバルクインサートなどより時間はかかるが実際にアプリの動作によってデータを作るのが一番確実なデータを準備できると思う。

具体的には負荷テストツールでデータの生成をともなうシナリオを作って走らせる。
一度生成したデータはバックアップしておけば、次回データを準備するのは短時間でできるようになる。

負荷テストを走らせる

いろいろと準備ができたので負荷テストを実行する。
最初は少ない負荷をかけてアプリが正しく動いているかどうかを確認する。

これだけ負荷をかけているのにスゴいパフォーマンスだ!
とか思ったら実はほとんどのページがエラーになっていて処理をきちんと実行していなかったなんてこともあり得るので。

一連のシナリオが正しく動いていることが確認できたら本腰を入れて負荷をかける。

負荷をかける側のリソース状況を見て、そちらがいっぱいいっぱいになってないことを確認する。
負荷をかける側がいっぱいいっぱいになっていたら十分に負荷をかけ切れてない可能性があるので、マシンの追加などを検討する。

計測結果を記録する

実行条件とともにしっかり記録しておく。
この結果がパフォーマンスチューニングやキャパシティプランニングの指標になる。

  • 負荷テストツールのレポート結果
  • 負荷をかけている間のサーバーのモニタリング結果
  • httpdのログ
  • スロークエリログ

クライアントから見たパフォーマンスとサーバー側のリソースの変化の両面から。
モニタリングに関してはMRTG、Munin、ZABBIXなどのツールで負荷がかかっている間のグラフが見られるようにしておくとよい。

httpdのログにはレスポンスタイムを含めるようにしておく。(ApacheならばLogFormatに%Dを加える)
そうすれば後で重いページを探し出すことができる。

スロークエリは0.1秒以上かかったらNGとか条件を厳しめにしておくと問題を効果的にあぶり出せる。

心がけるべきいくつかの事柄

負荷テストは負荷をかける側も痛みを伴う

負荷をかける側のマシンリソースが十分でないと負荷をかけきれないことのほか、ウェブアプリの負荷テストはネットワーク帯域を圧迫する。
ネットワーク帯域は意外と盲点なので注意。

パフォーマンスチューニングと負荷テストは別

テスト駆動開発で実装の帽子とリファクタリングの帽子を交互にかぶり直すように、チューニングの帽子と負荷テストの帽子も交互にかぶり直すようにする。
つまりふたつを一緒くたにして同時にやらないように。

これらを同時にやり始めると、ちょっとパラメータを変更しては負荷テストを繰り返すといったスパイラルに陥ってしまいがち。
このスパイラルはとても楽しいが、かかる時間の割に得られるものはそれほど多くない。

負荷テストの第一の目的はパフォーマンスの計測であって、パフォーマンスの向上はまた別の話。
そのあたりの線引きを意識しておかないと無駄に負荷テストを繰り返して時間を浪費してしまいかねない。

スロークエリはダメ、絶対

ごくまれにしかアクセスされないページにひとつスロークエリが潜んでいるだけで一気にパフォーマンスが半減したりする。(実のところ本当に50%減とかする)
高負荷時にはひとつの小さな傷が致命傷になりかねないという話。

負荷テストを要するようなシステムではスロークエリは致命的なバグと同列と言っていい。

また、そうした重箱の隅に潜んでいるスロークエリをあぶり出すためにもシナリオはすべての処理を網羅している必要がある。

追記

- 手順の項にかける負荷の規模についてなど追記。

ほか何か思いついたら適宜追記していくかも。

2010-08-09

WordPressのプラグイン用テーブルの追加

自作プラグインでテーブルを作成する必要があったのでメモ。
以下の二点がわかれば如何様にも作れる。

  • プラグインを有効化したときに処理を実行する方法
  • テーブルを作成する方法

プラグイン有効化時のフック処理

有効化時に実行する処理を関数として定義してregister_activation_hookで登録する。

function hoge_plugin_activate() {
  hoge_setup_table('hoges');
}
register_activation_hook(__FILE__, 'hoge_plugin_activate');

register_activation_hookの第一引数は関数が定義されているファイル。
同じファイルなので「__FILE__」でよい。別のファイルに切り出すならそのファイル名を指定する。

第二引数がフック処理を定義した関数名。
WordPress本体や他のプラグインが提供する関数とかぶらないような名前を付ける。

テーブル作成処理

テーブルの有無を確認してなければ作成するというのが一般的な処理。
別のテーブルを作るときでも使えるようにhoge_setup_table関数として切り出した。

function hoge_setup_table($table_name) {
  global $wpdb;
  if ($wpdb->get_var("show tables like'" . table_name_with_prefix($table_name) . "'") != table_name_with_prefix($table_name)) {
    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    $sql_create_func = "sql_create_$table_name";
    $sql = $sql_create_func();
	add_option("hoges_version", '1.0');
    dbDelta($sql);
  }
}

$sql_create_funcはSQLを得るためのテーブル名に応じた関数。テーブル名から機械的に関数名を作る。
add_optionはプラグインを更新した際にテーブル構造が変わる可能性があるのでプラグインのバージョン番号を記録しておくのだそうだ。

プレフィックス付きのテーブル名を使う機会は作成時以外にもいろいろあるので、関数として定義。

function table_name_with_prefix($table_name) {
  global $wpdb;
  return $wpdb->prefix . $table_name;
}

SQLを返す関数は以下のように定義する。テーブル名がhogesなのでsql_create_hoges。

function sql_create_hoges() {
  return "CREATE TABLE " . table_name_with_prefix('hoges') . "(
    id int not null auto_increment,
    name varchar(255) not null,
    primary key(id)
  );";
}

dbDeltaの仕様的に「CREATE TABLE」は大文字でなくてはならない。なんかイマイチだけど仕方ない。

その他メモ

テーブル作成のデバッグ

SQLが間違っていてテーブルが作成されないことがあるが、エラーがあっても出力されないので、add_option又はupdate_optionによってSQLを記録してデバッグした。
なんかもっと他に良い方法があるかもしれないが・・・

削除時にテーブルを削除するか?

再び有効にすることも考えられるので、普通は削除しなくてもよいだろう。
削除したければregister_deactivation_hookを利用すればよい。

$wpdbやdbDeltaを使わなくてもいいのでは

まあ郷に入れば郷に従えということで。

参考

2010-08-05

Dropbox+Gitでフリーのプライベートリポジトリ

最近開発マシンとしてMacとUbuntuを併用しているのでDropboxが手放せない。
単なるファイル共有だけではなく、DropboxにGitのマスターリポジトリを入れておくとプライベートリポジトリとして使えてますます便利に。

フリーのプライベートリポジトリが持てるサービスもあるけれど、新しくそういったサービスのアカウントを増やさなくていいので気軽に使える。

ただし複数人で共有するようなリポジトリの運用は難しい。
Dropboxには他ユーザーとのファイル共有機能があるのでいけるかな、と思ったがよく考えるとファイル更新の衝突(リビジョンの衝突ではなく)が起こる可能性があるのでその当たりに気を遣う必要がでてくる。

がんばればいけそうだけど、素直に他のサービスを使った方がよさげ。
フリーのサービスは「フリー プライベートリポジトリ」でぐぐればいろいろ出てくる。
またパブリックなリポジトリでよけばGoogle CodeやGitHubでよい。

2010-08-04

iPadアプリ出しました『温泉探索地図』

iPhoneアプリとして出していた『温泉探索地図』をiPad対応させました。

えー、酒代にするので買ってください。よろしくお願いします。
iPad/iPhoneのユニバーサルアプリになっているのでひとつのアプリで両方で使えてお得です。

両対応する作業はiPad独自のSplitViewControllerやPopoverControllerの勝手がわかればそれほど大変でもなかった。
その他の処理はiPadの場合とiPhoneの場合で条件分岐するコードをちょっと埋めたくらいで問題なく。

しかし今回は審査にけっこう時間がかかった。
7/24に提出して、In Reviewになったのが7/31、Ready for Saleになったのが8/3。
10日以上かかっている。
スケジュールに合わせて出そうとするなら、リジェクトされる可能性も考えるとずいぶん時間を見ておかないと心配だ。
自分の場合はいつ出てもでなくても大して問題はないが、何かのキャンペーンに合わせてアプリを出したりとかしようとすると大変だろうなあ。

2010-07-29

Android開発環境の構築

備忘メモ。
IDEを使う気は今のところないのでシェルとVimでがんばる方針。
開発マシンはUbuntu 9.10 or Mac(Snow Leoperd)。

手順は以下。

* Java環境の整備
* Android SDKをインストール
* AVD(仮想デバイス)を作る
* プロジェクトを作る
* Hello worldしてみる

Java環境の整備

MacはSnow Leoperdに最初から入ってるやつを使う。
Ubuntuは以下のコマンドでインストール。antも入れておく。

$ sudo apt-get install sun-java6-jdk ant

64bit版の場合はia32-libsというパッケージもいるらしい。

Android SDKをインストール

以下のページからそれぞれのプラットフォームに対応したものを落とす。

落としたものを展開。
展開されたディレクトリ名には「-mac_86」とか「-linux_86」とか書いてあるけども、パス設定を共通にしたいのでその部分を取り除いて/opt/local以下に置く。

その上でandroid-sdk/toolsにパスを通す。激しく手動。

$ export PATH=/opt/local/android-sdk/tools:$PATH

.zshrcにも書いておく。

ここまでやった時点で

$ android

とコマンドを実行すると「Android SDK and ADV Manager」なるGUIが立ち上がる。
左メニューからInstalled Packageを選択して「Update all」するといろいろと落ちてくる。

これで一応SDKのインストールは完了。

AVD(仮想デバイス)を作る

仮想デバイスはいわゆるシミュレータ。
実機がなくてもAndroidアプリの動作確認ができる。

AVDの作成は上記のGUIでもできるがコマンドラインでも簡単にできる。

$ android create avd -n A16 -t 4

-nは –name の略。
適当に任意の仮想デバイス名をつける。

-tは –target の略。
仮想デバイスのOSバージョンごとに決まっているIDを指定するのだが、これは連番で振られているようだ。
どのIDがどのバージョンに対応しているかはGUIでの作成を見たら表示順でなんとなくわかる。たぶんどこかにまとまっているのだろうけど。

作成した仮想デバイスは以下のコマンドで起動できる。

$ emulator -avd A16

起動にはちょっと時間がかかる。

まあここまではIDEに依存しているわけではないので、GUIでもいいでしょう。

プロジェクトを作る

EclipseなどのIDEを使わない場合はコマンドラインで。従ってここからはコマンドライン必須。

$ android create project -n HelloWorld -t 4 -p /path/to/project --a HelloWorld -k com.example.Hello

これでできた。
オプションの詳細はDeveloping In Other IDEs | Android Developers参照。

Hello worldしてみる

作ったプロジェクトのディレクトリに移動してコードを書く。
コードは以下のチュートリアル通りに。

Hello, World | Android Developers

仮想デバイスで動かすときだけ以下のコマンド。
このantタスクはデバイスにアプリをインストールするためのadbというコマンドをラップしてくれているっぽい。

$ ant install

これで仮想デバイスにインストールされたアプリを起動すると、感動のHelloWorldが!

なおデバイスを起動しておかないとインストールできないので注意。

これで環境構築はおしまい。
あとはSDKのドキュメントなど読んでアプリ開発がんばる。

参考

ほか公式のDev Guideにいろいろとまとまっている。

2010-07-28

最近の個人的なiPhoneやらXperiaやら周辺の通信環境

今年になってからモバイルの通信環境についていろいろ検討していた。

現状はこうなった。

  • iPhone 4 パケットし放題フラット
  • Pocket Wifi ギガデータプラン
  • Xperia auから乗り換えてネット利用を一切やめた

Pocket Wifi分増えたけども、電話用の端末でのネット利用をやめたことで通信エンゲル係数はほぼ変わらず。

iPhoneとPocket Wifi

iPhoneに関してはiPhone 4予約時の縛りで使っても使わなくてもパケット通信料が4,410円。
そんなわけでPocket Wifiとの併用でiPhoneの通信費を抑えるという目論見は露と消えた。

実はiPhoneの通信をPocket Wifi経由にすると言うのは一時期やってみていたのだが、実際やってみるとiPhoneで通信を始める前にPocket Wifiの電源を入れなくてはならないというのは致命的に不便だった。
iPhoneの使い勝手が激しくスポイルされてしまい、正直その手間をなくすのに月3,000円くらいだったら払ってやるよ、といったレベル。
なのでiPhone 4予約時にパケットし放題フラットの契約縛りの文言を見ても迷わず予約した。

Pocket Wifiは宙に浮いてしまった形とも言えるが、外出時にiPadやノートPCを持って行く場合に積極的に使っている。
プランは今後の様子を見て調整していくつもり。

電話回線と実験機としてのXperia

Xperiaは電話用のdocomo回線のためと、Android開発にも最近興味があるので、実機確保としてゲット。
山間部でのバイクの故障や事故などの可能性を考えるとSoftbank一本化はちょっと厳しい。
OSが2.1にアップグレードされるというウワサをあてにして買ったので、もしアップグレードされなかったらちょっと悲しい。
ネットワークはWifiのみで利用している。

ガラケーでなくなってしまったため、キャリアからの通信のみに制限しているモバイルサイトは見られなくなってしまったが、出先での情報検索などはiPhoneにすでにシフトしていたので個人的な利用に不便はない。
仕事的にも簡単な表示確認だったら実機はなくてもできるし、逆に本格的に実機確認しようと思ったら自分が持っている一機種だけじゃどうせ焼け石に水なので問題はない。

・・・とか思ってたら携帯ソーシャルアプリがらみの仕事なんかではちょっと不便だったのがやや誤算ではあった。

にしても久々に店頭で携帯の契約とかしたけれど、料金プランとか口頭で説明されても理解不能。複雑すぎです。
いらんオプションもいろいろと勧めてくるし、通信費安くした分をそういうところでがんばって回収しようとしているのかな?とか思った。

2010-07-22

iOS開発で遭遇した謎のエラーたち

メモをあげておく。
基本をちゃんと理解してれば謎ではないんでしょうが。

unrecognized selector sent to instance hogehogeっつってるけど
  • 定義されてないメソッド、またはinterfaceで公開されてないメソッドを使った
  • 対象のオブジェクトを使った覚えがない場合はポインタが意図してないアドレスを見てる

後者はメモリ周りで以下のように適当にやってると起こる。

  • まだ使われているオブジェクトを解放してるかもしれない releaseしすぎ
  • またはautorelease対象を解放してるかもしれない releaseしすぎ

プロパティへの代入はセッターでretainされてるかされてないかちゃんと意識すること。

error: expected specifier-qualifier-list before ‘HogeController’
  • クラスが見つからないようだ
  • フレームワークとか他のクラスのヘッダファイルとかインポートし忘れてないか確認
  • 忘れてないのにアレだったら@classを使うとなんとかなるかもしれない

@classが必要なケースはいろいろとインポートしていて順序の前後関係がごちゃごちゃしている時?
よくわからず。

ユニバーサルプロジェクトがiPhoneで動いてくれません
  • 「データフォーマッタが一時的に使用できなくなっています。」って言われてもなんのことだか
  • UIKitをWeak LinkingにするとかNSClassFromStringを使うとかして解決
  • 参考:http://useyourloaf.com/blog/2010/6/21/symbol-not-found-errors-in-universal-apps.html
  • 参考:http://d.hatena.ne.jp/KishikawaKatsumi/20100625/1277476249
何も言わずに止まりやがる
  • コントローラのViewを作り忘れてるのにそれにaddSubviewとかいけませんよね
2010-07-08

RubyのoauthでSignature検証

なんかサンプルコードが見つからなかったので書いておく。

OAuth::Signitureというクラスが利用できる。

require 'oauth'

def verify(request)
  consumer = OAuth::Consumer.new(
    'SOME CONSUMER KEY', 'SOME CONSUMER SECRET',
    :site => 'http://api.example.com'
  )

  headers = oauth_headers(request)
  signature = OAuth::Signature.build(request) do
    [headers['oauth_token_secret'], consumer.secret]
  end

  signature.verify
end

def oauth_headers
  headers = {}
  request.env['HTTP_AUTHORIZATION'].split(',').each{|item| k, v = item.split('='); headers[k.strip] = v.gsub(/"/, '')}
  headers
end

OAuth::Signature.buildの部分は

signature = OAuth::Signature.build(request) 

だったり

signature = OAuth::Signature.build(request) do
  [nil, consumer.secret]
end

でよかったりすることもあると思いますよ。

コード解説

requestって何者

requestはRailsのコントローラ内で使えるものをそのまま渡せる。

Sinatraなどの場合もrequestという名前で定義されているものを基本そのまま渡せるが、以下のrequireが必要。

require 'oauth/request_proxy/rack_request'

requestは内部的にOAuth::RequestProxy::Baseのサブクラスに適宜変換される模様。

oauth_headersメソッド

oauth_headersメソッドはoauth_token_secretが取れなかったので作ったメソッド。

他のoauth_tokenなどの値はOAuth::RequestProxyを使うと取得できる。

request_proxy = OAuth::RequestProxy.proxy(request)
request_proxy.oauth_token
request_proxy.signature

などで取れるんだけど、なんでかoauth_token_secretは取れず。

自前でSignatureを求める

ついでにSignature Base Stringから自分で求めてみる方法も。

Signatureがどうしても一致しない場合の原因究明をする場合などに、自前でやる方法は把握しておくと役に立つ。
# というかRubyのoauthのドキュメントが整備不足なのでこのあたり自前実装している人が多いような気がする。

require 'openssl'
require 'oauth'

def my_signature(consumer, request)
  headers = oauth_headers(request)
  signature = OAuth::Signature.build(request) do
    [headers['oauth_token_secret'], consumer.secret]
  end

  digest = OpenSSL::Digest::Digest.new("sha1")
  secret = "#{escape(consumer.secret)}&#{escape(headers['oauth_token_secret'])}"
  signature_string = Base64.encode64(OpenSSL::HMAC.digest(digest, secret, signature.signature_base_string)).chomp.gsub(/\n/, "")
end

def escape(value)
  URI.escape(value.to_s, /[^a-zA-Z0-9\-\.\_\~]/) # Unreserved characters -- must not be encoded
end 

# oauth_headersは略

上記コードの自前Signatureを求めるあたりはroauthから拝借した。

Signature Base String求めるとこは手抜きして既存のOAuth::Signatureを使ったもので。
まあSignature Base Stringがおかしい場合は目視でわかるので、自分でSignature計算する前にまずはそこを見てみればいいのかも。

2010-06-29

iOS開発 2010年6月終わり頃の記録

土日にいろいろやったので記憶の定着をかねてメモっておきますよ。

画像を回転させる
  • SDK 4.0からは既存のUIImageからCGImageを取得して、そこから回転を指定した新しいUIImageが作れる
  • でも今3.2なのでめんどくさいから回転した画像を別途作った
ナビゲーションバーに複数ボタン
  • 普通にやると右と左に一個ずつ(合計二個)しか作れない
  • ツールバーにまとめて、そのツールバーをもとにしたUIBarButtonItemを作るかんじ
  • 参考:Osmorphis: Multiple Buttons on a Navigation Bar
ボタンの見た目を画像に
Safariで開く
  • openURLしたらSafariで開く。超簡単。
標準のマップアプリで開く
  • Google MapのURLを指定してopenURLした時にマップアプリがインストールされていればマップアプリが開く
  • シミュレータにはマップアプリが入ってないのでSafariでGoogle Mapが開く
  • 開いた後にマーカーをたてるにはq=で場所の名前も指定する必要あり ちゃんとURLエンコードする
  • 参考:Launching Other Apps within an iPhone Application
  • 参考:「Custom URL Schemes」でぐぐれ
テーブルのセルの追加と削除時にアニメーション
iPadでボタンからActionSheetが出ているように表示する方法
  • showFromBarButtonItemとかいうメソッドがあるのでそれを使う
アクティビティインジケータの表示

開発の近況としてはだいぶ慣れてきて分からないことにも推測が働くので結構楽になってきた。
あと基本的なことは本家のリファレンスを見ることでだいたい解決できるようになってきたけど、手の込んだことはやはり先人たちの知恵を借りると目から鱗が落ちることがしばしば。

それから残念なこととしては少し前にTDDのエントリ出したけどOCUnit使いにくすぎるので結局あんまりやってない始末。
なんかいいテスティングフレームワークないんですかね。

2010-06-26

iPhone4がやってきた

ソフトバンクのオンラインショップで予約してた分がやってまいりました。
実は予約してたんです。
システムトラブルで予約できなかった初日があけた翌日の朝方、寝る前になにげなくオンラインショップをのぞいたら予約再開してたのでさくっと。
珍しく勝ち組です。

手持ちの3GSはiSO4にアップグレードしないで待ってたので初のiOS4機だったりもする。
目玉であるマルチタスクの恩恵は今のところ感じない。
すでに起動しているアプリをフォアグラウンドにしても初回起動と同じくスプラッシュが表示されるし、バックグラウンドでの動作をしてくれるアプリもないし、要するに素敵なマルチタスク体験は今のところなし。
まあアプリ側の対応待ちといったところなんでしょう。

開発者的にもiOS4はまだキャッチアップしてないのでしばらくしたらもうちょっと調べなければならないな。

画面のドットが見えないほどの解像度とか、カメラがきれいに撮れるようになってるとか、内蔵スピーカーのパワーがアップしてるとか、ハード的なパワーアップは顕著に感じる。

特にカメラはかなりよくて、単純に画素数が上がっただけじゃなくて撮れる絵がなにやらきれいになってる。
これは旅に出るときなど持って行ってたデジカメがいらない子になってしまったかもしれない。
その場合の心配はバッテリーと手を滑らせて落としてしまわないかと言うことだけども・・・
ストラップがつけられるケースでも買いましょうかね。バッテリ付きも検討ということで。

順調に進化してますます手放せないデバイスになってきた。

2010-06-16

MapKitのアノテーションからポップオーバーする

iPadプログラミングでちょっと試行錯誤したのでメモ。
convertRectでAnnotationViewのframeの座標をmapView上の座標に変更している部分が肝。

- (void)showPopover:(SomeContentViewController *)contentViewController
        fromAnnotationView:(MKAnnotationView *)annotationView
        onMapView:(MKMapView *)mapView
{
  UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController: contentViewController];
  [popoverController setPopoverContentSize:CGSizeMake(320, 480)];

  [popoverController presentPopoverFromRect:[annotationView.superview convertRect:annotationView.frame toView:mapView]
                                     inView:mapView
                   permittedArrowDirections:UIPopoverArrowDirectionAny
			                       animated:YES];
}

これでアノテーション(ピン)からふきだしが出ているように見える。

しかしObjective-Cのサンプルコードを抜き出すのはなんか難しいな。
動いてるコードから書き換えた部分があるので動かなかったら適当に直すってことで。

2010-06-15

iPad ヒューマン インターフェイス ガイドライン

がんばって英語で読んでたら以下に日本語訳がありました。なんてこったい。

iPhone Dev Center

iPadは画面が広くなった分、iPhoneとはまた違ったUI設計をしないといけないわけですが、そのあたりの指針をまとめてくれてるドキュメントです。
UISplitViewControllerとUIPopoverControllerあたりを使いこなすのがどうやら肝ですな。

UISplitViewController

画面を分割して左がメニュー、右がコンテンツといった2ペインのアプリが簡単に作れる仕組み。
メニューは幅がデフォルトで320。たぶんiPhoneのビューがそのまま違和感なく使えるように配慮されてるのだと思う。

またポートレート表示の際には自動的にメニューが隠れるようになっている。(隠れないようにすることも可能)
隠れたメニューはUIPopoverControllerを使ってボタンアクションなどで表示するようにするのが定石。

ちなみにこれと同じことを自前でやろうとするとローテーションの処理がめんどくさくてたまらない。
ええ、たまらなかったですよ。なんかいろいろ勉強にはなったけど。

UIPopoverController

任意のサイズのポップオーバーを表示する仕組み。
主にリストの行をタップした時、ボタンを押したとき、マップ上のアノテーションボタンをタップしたときなどに表示する。

ポップオーバーのコンテンツの表示は他のビューコントローラに委譲するので、Action SheetやAlertよりリッチな表示ができる。
また関連するリスト行やボタンからふきだしが出ているように表示されるので、何に関連したポップオーバーなのかわかりやすいようになっている。

Action SheetやAlertとの使い分けは適切に。

2010-06-13

iPhone4に乗り換える!

画面サイズが変わるとかいろいろあるし開発者的にもゲットしないわけにはいかんだろうという気持ちが強くなってきた。
そういうわけで乗り換えを決めた。

が、今使っている3GSからの乗り換えではなく、電話としてのみ残していたauからiPhone4にナンバーポータビリティで、という形での乗り換え。
で、今3GSで使ってる回線は解約することにした。

回線一本化でスッキリ!
通信エンゲル係数も下がって家計にも優しいです。ワーイ。

そもそもauは携帯サイト作るのにガラケーもやっぱ必要かと思って持ち続けていたもの。
けど一応の確認ならFireMobileSimulatorみたいなものがあるし、しっかりと実機確認したいのならちゃんと3キャリア複数機種で確認しないと意味ないってことが分かってしまったので、1キャリアの1機種だけ持っていてもあんまり意味がないなー、と感じてしまった次第。

そうなればガラケー切っても個人的にはまったく不便がないわけで。
仕事的に不都合があるとしたらモバゲーとかそこらの携帯向けのサイトが使えなくなってしまうことか。
しかしまあこのあたりは必要に応じてなんとかなんとかなるだろうー。たぶん。

2010/6/15追記

と、思ったけど山間部でのソフトバンクのだめっぷりを考えるとやっぱ無理だ。
3GSからの機種変で。

auはDocomoにでも乗り換えしてXperiaとか言う変な考えも浮かんできてますが、それはまた別の物語。

2010-06-09

デフォルトの.gitignoreを設定する

*.swpとか.DS_Storeとかを指定した.gitignoreをプロジェクトごとに作るのがいい加減面倒になってきたので重い腰を上げて設定してみた。
自分のホームディレクトリに.gitignoreを作成して以下のコマンドを打てばよい。

git config --global core.excludesfile ~/.gitignore

やればすぐなのになー。
こういうのつい先送りにしてしまう。

参考:~/.gitignoreを複数プロジェクトで使い回す – satoko’s blog – s21g

2010-06-06

Interface Builderよ、さようなら

というわけでここ数日の間に「Interface Builder使わない派」にめでたくクラスチェンジしました。
今までは「ケースバイケースでInterface Builderも有り派」という穏健な一派に属していたが、iPhone SDKの理解度が上がるにつれ必要性を感じなくなってきたと言う。

そもそもInterface Builderを使うメリットは

  • UIのデザインがGUIベースですばやく簡単にできる

というものだが、この機能で物足りなく感じていたこととして

  • UIの動きまではわからない
  • Xcodeとの連携がシームレスと言えるほどではない

静的なUIの設計部分はさくさく気持ちよくできるものの、動かさないと分からないと言った面もあるわけで、要するにInterface Builderの中でUI設計が完結しないところがイマイチだなあと。
またコードとの連携の簡単さに関しては10年くらい前にさわったきりのVBとかDelphi以下と思う。めんどくさい。

加えてコードからUIをいじることに慣れてきたので、下手するとコードで書く方がInterface Builderを使うよりも楽に感じるようにもなってきた。
動かないUIのスケッチならペーパープロトタイピングでやれば十分だし。

要するに自分の中ではInterface Builderを使うメリットが完全になくなったというわけです。

一方デメリットの方は依然として生きていて

  • Xcodeとの連携の仕方を覚えなくてはならない(忘れるたびに覚え直し!)
  • Interface Builderで作ったオブジェクトに余計なオプションがついていたりして意図通り動かないことがある
  • コードを書いてる最中にInterface Builderにスイッチする脳のコストが大きい

とまあいろいろ。
ほぼ満場一致でInterface Builderにはご退場願いました。

あでぃおす!

copyright brass.to | powered by WordPress ME