2010年5月3日月曜日

UTF-8 の文字列の長さを正確に求めるためには Normalize しましょう

Twitterにメッセージを送信する際に、クライアント側でメッセージの長さを判定して画面に表示したいというような要件があると思います。当然、このような場合には入力されたメッセージを取ってきてその長さを求めてやれば良いのですが、どうやら UTF-8 などのマルチバイトな文字コードを使っていると文字列の長さを正確に求めるのが大変なようです。


■何がどう大変なのか

図を書いてみました。「文」が文字数、「b」がバイト数を表しています。 Objective-C の場合は、「文」が ``[NSString length]`` を、「b」が [NSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding] を表します。ほかの言語の場合もそれぞれ相当するメソッドまたは関数が用意されていると思います。



上のケースが一般的な UTF-8 文字列です。この場合、バイト数はマルチバイトになってしまうため実際の文字数(我々人間が見て自然な文字数)とは一致しませんが、その代わり UTF-8 文字数が実際の文字数と一致するため問題なく長さを求めることができます。

面倒なのが下のケース。ウムラウト記号や濁点・半濁点が一文字として入力されてしまっている場合です。通常滅多にこのような文字列に遭遇することはないのですが、まれにこのような文字列を入力させる処理系があるようです。で、この場合はウムラウト記号や濁点・半濁点も一文字として認識されてしまうため、実際の文字数と文字列長判定メソッドが返す文字数が異なってきます。そこまで厳密にしなくてもよいのでは・・・と思ったのですが、なんと Twitter はきちんとこのような場合も自然な文字列の長さを測定して140文字かどうかを判断しているらしいです。


■対策:Normalizeする

このように実際の文字数と UTF-8 上の文字数が異なっている場合には、 Normalize と呼ばれる処理を行って文字を圧縮する必要があります。 Objective-C の場合には以下のようなメソッドが標準で用意されています。
// NFD
– (NSString *)decomposedStringWithCanonicalMapping

// NFKD
– (NSString *)decomposedStringWithCompatibilityMapping

// NFC
– (NSString *)precomposedStringWithCanonicalMapping

// NFKC
– (NSString *)precomposedStringWithCompatibilityMapping
これらのメソッドは、 Unicode 文字列をそれぞれ NFD, NFC, NFKD, NFKC と呼ばれるフォーマットの文字列に変換してくれます。それぞれの文字列がどのような表現を表しているかは、以下のページを参考にしてみてください。英語ですが図説がついてるので非常にわかりやすいです。
http://unicode.org/reports/tr15/
http://homepage1.nifty.com/nomenclator/unicode/normalization.htm

ほかの言語、たとえばPythonやRubyなどでこの機能が提供されているかどうかはわかりません。あしからず・・・><

2010年4月29日木曜日

Three20 の TTPhotoViewController でローカルファイルを扱う際の注意点

Three20 フレームワークを使ってアプリを作成しているときに、ローカルファイルを TTPhotoViewController に読み込ませようとして一部はまってしまったので共有します。


■結論から
  • TTPhotoViewController の中に一部 NSHTTPURLResponse でないと動かないところがあるため、ファイル読み込みでは動作しない
  • @"bundle://filename.jpg"@"documents://filename.jpg" といった記法を用いることで回避可能
  • またはフレームワーク自体を書き換えてしまう

■問題
以下のコードの様に、ファイルスキームのURLを用いて画像を読み込もうとするとエラーになります。
photos = [[NSMutableArray alloc] init];
PhotoBookPhoto *photo;
NSString *imagesDirectoryPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Images"];
NSArray *imagePathes = [[NSFileManager defaultManager] directoryContentsAtPath:imagesDirectoryPath];

for (int i=0; i<imagePathes.count; i++) {
NSString *imagePath = (NSString *)[imagePathes objectAtIndex:i];
imagePath = [imagesDirectoryPath stringByAppendingPathComponent:imagePath];
LOG(@"%d - %@ %@", i, imagePath, [[NSURL fileURLWithPath:imagePath] absoluteString]);
photo = [[[PhotoBookPhoto alloc] initWithURL:[[NSURL fileURLWithPath:imagePath] absoluteString]
smallURL:[[NSURL fileURLWithPath:imagePath] absoluteString]
size:CGSizeMake(960, 640)] autorelease];
photo.photoSource = self;
photo.index = i;
[photos addObject:photo];
}

■原因
TTRequestLoader 中の以下のコードが直接的な原因です。
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark NSURLConnectionDelegate


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)connection:(NSHTTPURLConnection*)connection didReceiveResponse:(NSHTTPURLResponce*)response {
_response = [response retain];
NSDictionary* headers = [_response allHeaderFields];
本来 NSURLConnectionDelegate のデリゲートメソッドである、 connection:didReceiveResponse:NSURLResponce を引数にとるのですが、何故か NSHTTPURLResponce が直接指定されています。通常はこれでも問題ありませんが、fileスキームのURLは当然HTTPではありませんから、 NSHTTPURLResponse を返しません。従って allHeaderFields の呼び出しでエラーになってしまいます。


■対処法
ということで、この部分のコードを一部書き換えてしまい、 NSURLResponce がきても動作する様にしてしまいました。
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark NSURLConnectionDelegate


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {
_response = [response retain];
if ([_response isMemberOfClass:NSHTTPURLResponse.class]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)_response;
NSDictionary* headers = [httpResponse allHeaderFields];
int contentLength = [[headers objectForKey:@"Content-Length"] intValue];

// If you hit this assertion it's because a massive file is about to be downloaded.
// If you're sure you want to do this, add the following line to your app delegate startup
// method. Setting the max content length to zero allows anything to go through. If you just
// want to raise the limit, set it to any positive byte size.
// [[TTURLRequestQueue mainQueue] setMaxContentLength:0]
TTDASSERT(0 == _queue.maxContentLength || contentLength <=_queue.maxContentLength);

if (contentLength > _queue.maxContentLength && _queue.maxContentLength) {
TTDCONDITIONLOG(TTDFLAG_URLREQUEST, @"MAX CONTENT LENGTH EXCEEDED (%d) %@",
contentLength, _urlPath);
[self cancel];
}

_responseData = [[NSMutableData alloc] initWithCapacity:contentLength];
} else {
_responseData = [[NSMutableData alloc] initWithCapacity:response.expectedContentLength];
}
}
また、あとから知ったのですが、Three20においては以下のようなURLが使用できるみたいです。 Three20/TTGlobalCorePaths.h にて定義されています。
photo = [[[PhotoBookPhoto alloc] initWithURL:@"bundle://image.png"
smallURL:@"documents://thumbnails/smallImage.png"
size:CGSizeMake(960, 640)] autorelease];
たぶん Three20以外では使用できない記法 なので注意が必要ですが、この方法で読み込むと NSHTTPURLResponce が返却されるためコードを書き換えなくても正常に動作します。 こちらのほうが簡単でThree20の開発チームの人もそうしているのでお勧めです。


■んーでも
そもそもこのThree20、開発者の人(FaceBookアプリを作った人)がiPhoneでのプログラミングを既にやめちゃっていると聞いたのでメンテがどうなるのか不安です。

iPadアプリに挑戦中

運良くiPadを輸入して手に入れることができましたので、現在iPadアプリの作成にとりかかっています。最初はiPhoneと対して変わりあるまいと思って作っていたのですが、実機で動かしてみると様々な違いや問題が分かってきました。


■今作っている物とか課題とか

現在作っているのはiPad用の紙のノートです。



数年前からシステム手帳を持ち歩いていたのですが、実際スケジューリング等はすべてiPhoneで行っていました。それでも紙を手放せなかった唯一の理由がアイディア出しです。アナログ人間な物で、手で紙に書き付けないとアイディアが出てこないのです。iPhoneのスクリーンは明らかに小さすぎて手書きには不向きでした。

そこでiPadの大きくなった液晶を使えば紙の代わりができるのではないかと思って早速試して見ました。

ペンの色を4色、太さを4種類用意しています。二本指で左右にスワイプするとページをめくることができます。デバイスを横向きにすると、二ページ見開きの状態にすることができます。この見開きのままでも線を書いたりページをめくったりできます。



できるかぎり実際の紙に近づけたかったため、UIを一切排除しています。


■Gesture Recognizer

ページング処理やペン選択ツールの表示にはスワイプジェスチャを使用していますが、今回このジェスチャを実装するためiPhone OS 3.2より新しく搭載された UIGestureRecognizer というクラスを使用してみました。このクラスを使えば各種ジェスチャを自動的に認識してくれるので、自分でいちいちtapの位置を拾って前回のtapとの差分を検出し・・・ということをしなくても済むようになります。

// Add gesture recognizer to the paper view
UISwipeGestureRecognizer *toolPopoverGestureRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self
action:@selector(handleToolPopover:)] autorelease];
toolPopoverGestureRecognizer.numberOfTouchesRequired = 2;
toolPopoverGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;
[self.bookView addGestureRecognizer:toolPopoverGestureRecognizer];
しかしながらいくつか問題が。まず認識精度がそれほどよくないです。特に複数指のスワイプに関しては自分で実装した方が精度が出ると思います。また、ジェスチャは通常のタップとは別に検出されているようなので、二本指でタップしてジェスチャしたときには通常の線描画を切るような処理を含めないと、ジェスチャするたびに線が画面に増えてイライラします。


■他のお絵かきアプリのUI

予想通りというか、ふたを開けてみれば他にもたくさんのドロー系アプリがApp Storeにリリースされていたので、それらのUIを見て研究してみる事にしました。

Adobe ideas for iPad



二本指ドラッグでスクロール、ピンチ操作で拡大縮小。一本指で描画中に二本目が触れると即座に描画をキャンセルしてくれるため、誤ってキャンバスに線が増えてしまうということはありません。よく考えられています。

常に左横にツールバーが表示されているようになっています。ツールバーを置くのは邪魔だろうと思っていたのですが、キャンバスの拡大縮小や移動が自由にできればほとんど邪魔にはならないことがわかりました。むしろすぐアクセスできて便利です。

Autodesk SketchBook Pro



こちらも二本指ドラッグでスクロール、ピンチ操作で拡大縮小。一本指で描画中に二本目が触れると即座に描画をキャンセルしてくれるところも全く同じです。

ツールバーを表示するには、画面中央下の小さなポッチをタップするか、または指三本で画面にタップ。この方が画面を広く使えて嬉しい・・・と思っていたのですが、実際に試してみると意外とイライラします。三本指というのが直感的ではないのかもしれません。このへんは人によるのかもしれませんが、私は常にツールバーが表示されている方がスムーズに操作できました。

Penultimate



iWorkと非常に良く似た作りになっていて、しかも操作系はシンプルです。ジェスチャは一切なく、ボタンはツールバーとして常に表示。それはまったく問題ないのですが、画面がスクロールできず拡大縮小もないため、非常に画面が狭く感じ書きづらいです。いくらiPadが大画面とはいえ、ペン先が通常のペンに比べて太い(私が好んで使うペンは0.4mmですが、iPad上の指は10mmぐらい幅があるので、およそ25倍も大きい)ため、せいぜい数文字しか綺麗に書けません。

その他特筆として、ペンの書き味が素晴らしいです。描画速度が速く非常に追従性がよい。すらすら書けます。


■お絵かきアプリのUIまとめ

いくつか使ってみて、さらには自分でも作ってみて感じたのが以下のようなこと。
  • 紙ではなくてホワイトボードのメタファとして使用するとうまくいく
  • 拡大・縮小・スクロールは絶対必須 無いとアプリとして成立しない
  • ジェスチャを使ってメニューを出すのは思ったよりも効果的ではない
  • 書き味はや動作速度は大事、使っていて楽しくなる
まず一番目。ペン先が太く、消しゴム削除がたやすく、アンドゥリドゥもできるので、書いている間隔がホワイトボードに近い気がします。そのため紙ではなくてホワイトボードだと思って実装するとよさそうです。

二番目。ペン先がやたら太いわりには画面サイズが1024x768しかないので、拡大縮小スクロールできないと話になりません。逆にこれができれば事実上キャンバスサイズは無限大にできるわけで、デバイスサイズ以上の活躍をしてくれます。

三番目。ジェスチャ自体は上手く使えば非常に有効です。たとえばアンドゥリドゥなどの操作はジェスチャで行う方が直感的でした。しかしメニューは常時画面に表示していた方が良い気がします。

最後、四番目。動作速度は極めて大事だと感じました。特に描画速度は最も大事で、線を引くのがもっさりしてしまうとそのせいで綺麗な曲線にならなかったり、単純にイライラしたりします。実物のホワイトボードに書くぐらいの速さで描画ができるように目指したいです。

2010年4月1日木曜日

UIBarButtonItem の見た目を画像にしたいときのテクニック

iPhoneアプリでよく使われる、ナビゲーションバーに配置するボタン UIBarButtonItem の見た目を完全に画像にする時のテクニックです。
参考にしたページはこちら。
http://discussions.apple.com/thread.jspa?threadID=1505647
http://www.iphonedevsdk.com/forum/iphone-sdk-development/13809-uibarbuttonitem-customview-action.html
http://discussions.apple.com/thread.jspa?threadID=1546506&tstart=60


■作戦1:initWithImage
てか、 UIBarButtonItem には initWithImage あるからそれでいいんじゃないか、と思ってさっそく以下のコードを書いてみました。
UIBarButtonItem* buttonItem = [[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:@"appstore.png"]
style:UIBarButtonItemStyleBordered
target:self
action:@selector(appstoreAction)];
self.navigationItem.rightBarButtonItem = buttonItem;
するとあら不思議、確かに画像は表示されたのですが、画像の周りにいつものボタンの枠が表示されてしまっています。styleを変更してもうまくいきません。どうやらこれはあくまでいつものボタンの中に画像を表示するだけのメソッドで、ボタンの見た目そのものをそっくりそのまま画像に入れ替える(ボタン画像を作ってさしかえる)用途には使えないようです。


■作戦2:initWithCustomViewとUIImageViewを組み合わせる
それなら initWithCustomView を使って画像を表示させてみましょう。
UIImageView* customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"appstore.png"]];
customView.userInteractionEnabled = YES;
UIBarButtonItem* buttonItem = [[UIBarButtonItem alloc] initWithCustomView:customView];
buttonItem.target = self;
buttonItem.action = @selector(appstoreAction);
self.navigationItem.rightBarButtonItem = buttonItem;
この方法だと、ボタンの縁が消えてappstore.pngがそのまま表示され、見た目は臨み通りの状態に表示させることができたのですが、今度は別の問題が。なんとボタンを押してもアクションが呼びだされません。海外のフォーラムでも同じような問題を抱えている投稿が見受けられました。


■作戦3:initWithCustomViewとUIButtonを組み合わせる
それならということで、カスタムビューを UIImageView から UIButton に変更してみることにしました。
UIButton *customView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 95, 33)];
[customView setBackgroundImage:[UIImage imageNamed:@"appstore.png"]
forState:UIControlStateNormal];
[customView addTarget:self action:@selector(appstoreAction) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem* buttonItem = [[UIBarButtonItem alloc] initWithCustomView:customView];
self.navigationItem.rightBarButtonItem = buttonItem;
ビルドして試してみると、見た目は作戦2と同様に綺麗に表示され、さらにタップするときちんとアクションが実行されるようになりました!

2010年3月31日水曜日

Flash の埋め込みパラメータ wmode で発生したトラブルいろいろ

Flashまわりの仕事をしていて wmode パラメータで躓いたのでメモ。


■事の発端
開発機のMac上では正常に動作していたコンテンツが、Windows機のFirefoxでテストを開始したとたん以下のような症状に見舞われてしまいました。
  • TextFieldにフォーカスを合わせて日本語をタイプするとTextFieldとは全然違うとんちんかんな位置に入力中の文字列が表示される
  • しかも入力した日本語が文字化けする
ちょっと調べてみて、どうやら wmode パラメータが悪さをしているのではないかということが分かりました。
http://www.326studio.net/blog/2009/10/firefox.html
http://fukata.org/2009/11/29/1905/

ということで早速 wmode パラメータを消してみたところ上手く動作しました。


■そのときのSkype上での会話(一部改変)
登場する皆様ご紹介
 akisute: そもそもテキストボックス内に入力が表示されないのが普通な気がしてきた
easy: あいうえお=0B0D0F0H
akisute: http://www.326studio.net/blog/2009/10/firefox.html
akisute: こいつだな
wozozo: なんでその指定してたんだっけ
AE35: > htmlにswfファイルを呼び出すときにwmodeの指定でtransparent,opaqueの指定をするとこの現象が起きます。
akisute: http://fukata.org/2009/11/29/1905/
akisute: ということでをぞぞんよろしくお願いします
wozozo:
{
id : 'externalswf',
quality : 'high',
wmode : 'transparent',
allowScriptAccess : 'always',
width: 100,
height: 100,
flashvars: 'abesi hidebu tawaba'
});
wozozo: いまこうなってます。
wozozo: wmodeごと削除?
feiz: お、そこは以前のプロジェクトでぶつかった問題ですね
feiz: wmode=transparentを付けると
feiz: flash上にhtmlのダイアログを表示したりしたときにflashが上に出てしまう問題が回避できます
feiz: ただし日本語が化けます。
wozozo: e
wozozo: じゃあこれ消していいの
feiz: 両方いっぺんに回避する方法はあったっけ・・・
wozozo: でもいま
feiz: ああ、無いんだった
wozozo: flashの上にhtmlを表示するときには、
wozozo: embedされてるdivごと hide してる
feiz: あ、それが正しいはず
feiz: 以前のプロジェクトでもそうした。
wozozo: naru
wozozo: じゃあ消す。
feiz: ちなみに二つの問題は両方とも特定ブラウザ限がついてたはず。
feiz: どうでもいいけど。
akisute: それはすごい面白いネタなので
akisute: メモってBlogる


■さらに調査

調べれば調べるほどこの wmode には問題だのバグだのが山盛りだと言うことが分かって驚愕。
http://blog.graffiti-web.org/archives/2006/04/wmode.html
http://chabudai.org/blog/?p=34
http://d.hatena.ne.jp/jedisystemer/20090227/1235754536
http://3ping.org/2004/05/15/1441
Flash Player 10では修正されているらしいのですが、上記のバグはFlash Player 10で発生してますし、うーん。これは触らぬ神にたたり無しですね。


■結論
wmode は指定しないようにしましょう、と。Flash Player 9以下ではIEでフレームレートが落ちる問題がありますが、そもそも9を使っている人も少なくなりましたし問題ないと思います。

2010年3月20日土曜日

IE6, IE7 上で flash.net.URLLoader を使って Comet (Long Poling) によるプッシュ通知を行う際の注意点

Flashは一度書いたらどのブラウザでも同じように動く、そう信じていた時が私にもありました。その私の考えを裏切るかのように、 またIEか と言いたくなる現象が発見されました。

調査の際に参考にしたページはこちら。
http://saruzaurus.blogspot.com/2008/07/comet_10.html


■問題
IE6, IE7 で、 flash.net.URLLoader / flash.net.Loader を使用すると、同時に2本しかコネクションが張れません。通常の使い道でしたらこの制限で何ら問題はないのですが、問題になるのは CometTornado のように所謂ロングポーリングと呼ばれるHTTP通信の仕方をするアプリケーションを書いたときです。ロングポーリングではいったんHTTPリクエストをサーバーに投げた後、サーバーで何かイベントが発生するまでそのレスポンスをひたすら待ちます。従って常時HTTPコネクションが1本消費されている状態になるため、事実上1本しかHTTPコネクションを使うことができません。

またIE6, IE7 では、 flash.net.URLLoader.close() の実装にも問題があるようです。
http://www.kirupa.com/forum/showthread.php?t=335691
http://stackoverflow.com/questions/455656/urlloader-gets-stuck-when-polling
きちんと使用後に URLLoaderclose しているのですが、それでも何故かメモリ使用量が減らなかったり、コネクションが閉じられなかったりして貴重なコネクション数が消費されっぱなしになったりするなど、どうにもこうにも不安定です。



これらが原因となって、常時ロングポーリングでイベントを待ち、イベントを受け取ったら処理を行ってまたすぐロングポーリングを張りに行くようなアプリケーションを書くと、その他の通信を行った際にロングポーリングが止まってしまったりその他の通信がコネクション数限界で実行されなかったりします。


■原因
Flashは内部的にはブラウザの機能を用いて実装されています。したがってブラウザ側のHTTP接続数が2本であれば自動的にFlash側のHTTP接続限界も2本になります。当たり前と言えば当たり前なのですが、盲点でした。

Flash側の接続数とブラウザ側の接続数が共有されるかどうかは未知数・未検証ですが、おそらく共有されるのではないかと思っています。従って(まずこんな構成にすることはないと思いますが)FlashでCometによる通知待ちを行い、Ajaxでも同様の通知待ちを行うと、それだけで接続限界になってしまいます。


■対策
今のところ最良の方法がまだ分かっていません・・・ごめんなさいorz 一応いくつか思いついている作戦は、
  1. Windowsのレジストリを操作してIE6/IE7の同時接続数を増やす
  2. 同時接続数制限は同一ドメインに対してのみ発生するらしいので、サブドメインを切って別々のドメインに分ける
  3. 他の通信が発生しているときはロングポーリングを切断する
などなど。上手い対策が発見されましたらまた書きます。

2010/03/31 23:59 追記:サブドメインを切って別々のドメインに分けたら上手く動作しましたので、この方法を一応お勧めしておきます。

2010年3月18日木曜日

Flash の fl.controls / fl.containers の visible プロパティはADDED_TO_STAGEで変更する

最近イマイチなFlashネタしかお届けできておらず残念です>< が、ちょっとしたネタを発見しましたので書いてみます。


■問題
fl.controls.Buttonとか, fl.containers.ScrollPaneのvisibleプロパティをコンストラクタの中でfalseにしても、直後にtrueに戻されてしまうようです。試しに、以下のような構成のMovieClipをFlashで作成して、
  • TestMovieClip
    • aButton
    • aScrollPane
以下のようなコードを書いてみます。
package {
import flash.display.*;
import fl.controls.*;
import fl.containers.*;
public class TestMovieClip extends MovieClip () {
public function TestMovieClip() {
aButton.visible = false;
aScrollPane.visible = false;
}
}
}
このコードを実行すると、なぜかボタンとスクロールペーンが 消えません 。なんでやねん。デバッガを使って調べてみると、どうやらコンストラクタが終わった後、どこかのタイミング?でvisible=trueに戻っているようです。


■対策
以下のようにADDED_TO_STAGEイベントを使って回避します。
package {
import flash.display.*;
import flash.events.*;
import fl.controls.*;
import fl.containers.*;
public class TestMovieClip extends MovieClip () {
public function TestMovieClip() {
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(event:Event):void {
aButton.visible = false;
aScrollPane.visible = false;
}
}
}
公式のドキュメントを見てもこのあたりの解説が無くて困りました。

2010年2月28日日曜日

Sphinx の Blogger 原稿用拡張を作ってみました

Bloggerの原稿をいちいち手で書くのが面倒だったので(特にリストとかpreとか)、Sphinxを使ってBloggerの原稿を書く拡張を作ってみることにしました。
参考にしたページはこちら。
http://sphinx.shibu.jp/


■サンプルとダウンロード

githubにアップしてみましたので、こちらからご利用いただけます。
http://github.com/akisute/sphinx_rst2blogger

実際に変換させてみたサンプルをこちらに用意してみました。
http://gist.github.com/317478


■設定

完全に私の個人使用向けになっているので、そのままではたぶん使いにくいと思います。まずはMakefileの以下の部分をご自身の環境に合わせて書き換えてください。
blogger:
$(SPHINXBUILD) -b blogger $(ALLSPHINXOPTS) $(BUILDDIR)/blogger
mate $(BUILDDIR)/blogger/draft.txt
@echo
@echo "Build finished. The draft pages are in $(BUILDDIR)/blogger."
今私は mate コマンドを使って結果をTextMateに表示するようにしていますが、これはTextMateがないと使えないので適当に消すなり pbcopy に書き直すなりすると幸せになれます。

また、Blogger側の以下の設定を有効にしてください。



■実際に使ってみる

使用方法はとっても簡単です。以下のコマンドをシェルから実行してください。
make blogger
make.batは全く操作していないのでWindowsの人はごめんなさい。たぶん使えません><

また、100%完璧な変換はできておらず、主にpreとかtitleの箇所で手動微調整が必要になります。まぁおおよそ上手くやってくれればいいや、という考えです。


■おまけ:Sphinxの拡張機能でカスタムのビルダーを作る方法

拡張機能の作り方自体は以下のページのやり方に沿えば楽勝です。
http://sphinx.shibu.jp/extensions.html
ですが、肝心のビルダーを作るのはかなり苦労しました。

ビルダーの作り方ですが、一から手で書くと挫折するので、もともとSphinxに付いているビルダーをまるごとコピーしてそれを修正するようにすると比較的楽でした。リファレンスとかドキュメントとかないので、ソース見て、 visit_hogehoge とか depart_hogehoge とか書いてあるところを適当に修正してみたら動いた、って感じです。 TextBuilder を元にすると書きやすかったです。 HTMLBuilder はdocutilの機能を拡張しているためdocutilがわからないとさっぱり分かりません。

Sphinx を用いて、 reST の文章を Blogger の原稿に変換する

改良版@2010/02/28 18:08

BPStudy #30 にてSphinx講座があったので、早速それを生かしてreSTでBlogger向けの原稿を作ってみることにします。


■見出しのテスト

Hello world!
foo bar
改行のテスト

段落のテスト

それではインラインマークアップのテストをしてみましょう。 強調強い強調code sample(): print sample 。いかがかな?

リンクのテスト リンクの末尾には必ず_を付ける必要があります。


■さらにもう一つ見出し
  • ul 1
  • ul 2
  • ul 3
    ul 3 continues
  1. ol 1
    1. ol 1-1
    2. ol 1-2
  2. ol 2
    ol 2 continues
    ol 2 continues again
  3. ol 3
番号リストの後に普通のパラグラフを入れてみるとどうなるか
定義リスト
定義リストをつくるにはインデントを頭に付けるなんか定義リストの作り方がおかしいって怒られた
どうすればいい?
しらんがな
どうするもこうするも
/** This is the pre message*/
def pre(self, message):
print 'this is the pre message'
だいたいうまくいっているようです。 okay fine.

Razor Orochi を Mac 用の無線ゲーミングマウスとして使ってみる



自宅のマウスを無線化したくなったので、Macでも左利きでも使える無線マウスを探してみたところ、 Razor Orochi という製品が見つかったので試してみることにしました。


■諸君 私はマウスが好きだ
「そもそもMacで無線マウスならMagic Mouseを使っておけばいいだろう」と皆様お考えであろうと思うのですが、残念ながら以下のような理由により私はMagic Mouseに満足できません。
  • Boot CampしたWindowsでの動作がイマイチ。基本的にMac専用。
  • ボタンがないためゲーミングで使用できない。右クリックすら怪しい。
  • 薄すぎ小さすぎて手に収まらない。
仕方がないのでその他の無線マウス製品を探してみたのですが、なんと以下の条件にマッチするマウスが世界中探しても一つもないのです!
  • 左利きなので、左右対称デザインであること
  • ゲーミングに使用するため、最低でも5ボタン付いていること
  • ゲーミングに使用するため、ホイールはクリック感があること(武器切り替えの時にクリック感のないホイールはまるで使えません)
  • 可能な限り大きく手のひらに収まるサイズであること、モバイルサイズはダメ
特に条件が厳しいのが「左右対称デザインであること」で、まぁ何処の会社も無線マウスは右手で持つものだと言わんばかりに大きく左にひねくれさせ放題ひねくれさせてくれやがりまして、まるで選択肢がないのです。あきらめかけていたときにようやく海外のサイトでこのRazor Orochiを見つけました。


■Razor Orochiって?
ゲーミング用製品で有名なRazor社の製作した世界初のBluetoothモバイルゲーミングマウスです。詳細はこちらのページをご覧になってください。

どうも日本国内では正式に流通していないようで、そもそも日本語版のRazor社の公式ページには名前すらありません。Razor社の通販ページから直接通販して買うしかありませんでした。送料込みで100ドルぐらいです。

おそらく日本国内でこんなへんぴなマウスをわざわざMacで使っているのは私ぐらいなものでしょう><


■設定してみる
最初に重要なことですが、Macだけで使用したいと思っていても、Razor Orochiの設定にはWindowsが絶対必須です。Mac版のドライバも用意されていますが、残念ながらMac版のドライバはOrochi本体のメモリに設定を書き込むことができず、またボタン配置を変更したりもできないため、全く役に立ちません。極めつけに有線モードでなければ設定できず、有線から無線に切り替えると設定がすべてリセットされるため、タダのお荷物です。USB Overdriveを代わりに使うことを強くお勧めいたします。

また、Razor Orochi ファームウェアの更新にもWindowsが必須です。ファームウェアの更新ですが、これが大変重要で、ファームウェアを更新しないとこのマウスはまるで役に立ちません。論より証拠、以下の図を見てください。



これはRazor Orochi farmware version 1.02の状態でお絵かきソフトで線を引いてみた図なのですが、見ての通り赤い線の部分がポインタ飛びを起こしています。高速で引いた線、水平垂直な線は比較的綺麗ですが、斜めにゆっくり動かすとすさまじい勢いでポインタが飛んで使い物になりません。farmware version 1.06にすると多少改善しますので、まず何はなくともファームウェアを更新する必要があります。

ということで、まずはWindowsを起動してファームウェアを更新し、それから設定を行います。私は左利きなので、サイドボタンの設定を変更する必要があります。初期設定では4,5が戻る/進むになっていますが、これを6,7と入れ替えてやります。こうすることで、左手の親指でマウスボタン4,5が押せるようになります。右利きの方はそのままの設定で十分使えると思います。


■使ってみる
良い点はきちんと5ボタン使える左右対称デザインで、しかもモバイルサイズながら十分な大きさがあり、手にフィットするところです。小指がマウスに乗っからないので若干小さいですが、この程度なら十分使えます。重さは結構ありますがすぐ慣れました。精度も、大きくマウスを振り回し続けている間は非常に良いです。Microsoft IntelliMouse Opticalとほとんど変わりません。

悪い点はなんと言ってもポインタが飛びます。このRazor 3G レーザーセンサー、低速時の挙動が最悪です。steelseries 4HDをマウスパッドにしているからわざと相性悪くしているのかと思って別のマウスパッドを使ってみたり直接机で動かしたりしてみましたが、やっぱりダメです。ファームウェア1.06で多少良くなりますが、スナイパーの方や細かい線を書くお仕事の方には絶対にお勧めしません。このセンサーの問題は有線モードにしても解決しません。

それからもう一つ悪い点として、スリープ状態からの起き上がりが非常に遅いです。5秒休めておいてから急にマウスを動かすと最初の1ストロークが完全に無視されます。最悪です。Magic Mouseは起き上がりが非常に早いのでそんなものなのかと思って買ってみましたが、なるほどアレはAppleのMac専用チートだったわけですねorz


■まとめ
まだ無線マウスは使い物にならないというのが正直な感想です。以前、ゲーミングデバイスを開発してらっしゃるSteelSeries社の方に無線マウスは作らないのかと質問してみたのですが、そのときに得た答えが「まだ現在の技術では無線マウスは使い物にならない」というものでした。なるほど納得です。

しかしながら全く使えないのかというとそうでもなくて、線が無くなったおかげで確かに机が広く使えて助かってます。私が最近プレイしているHeroes of NewerthのようなRTSゲームや、DiabloみたいなRPGスタイルのゲームであれば、常にマウスを大きく動かすためほとんど問題のない操作感になります。

逆にスナイパースタイルのFPSをプレイするのには全く向きません。日常使いにもちょっと不満があります。たいていキーボードを触っているので、ちょっとマウスをさわりに行くと遅くてイラッ。

2010年2月20日土曜日

hg clone で巨大なリポジトリを取得するときは --debug オプションが便利

参考にしたページはこちら。
http://mercurial.selenic.com/wiki/Clone

mercurial hgを使っていると、特にバイナリでサイズの大きいファイルを頻繁に変更するようなリポジトリ(たとえばFlashとかflaとかflaとか)はすぐにサイズが肥大化して、cloneに時間がかかるようになってしまいます。このような巨大リポジトリのcloneの進捗状況を表示できないかと思い調べてみました。

結論から言いますと、今のところそのようなコマンドは用意されていないようです。そこで代わりに--debugオプションをつけて実行するとよいと公式ページに説明がありました。
> hg clone --debug http://www.selenic.com/repo/hello my-hello
using http://www.selenic.com/repo/hello
sending between command
sending heads command
requesting all changes
sending capabilities command
capabilities: unbundle=HG10GZ,HG10BZ,HG10UN branchmap lookup changegroupsubset
sending changegroupsubset command
adding changesets
add changeset 0a04b987be5a
add changeset 82e55d328c8c
adding manifests
adding file changes
adding Makefile revisions
adding hello.c revisions
added 2 changesets with 2 changes to 2 files
updating the branch cache
updating to branch default
resolving manifests
overwrite False partial False
ancestor 000000000000 local 000000000000+ remote 82e55d328c8c
Makefile: remote created -> g
hello.c: remote created -> g
getting Makefile
getting hello.c
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
全体の進捗がわからないのでいまいちですが、何も表示されないよりは格段に良くなりました!

2010年2月15日月曜日

Flash の SWC は使ってはいけない

タイトルからしてなかなかひどいですが、調査結果もなかなかひどいです。

■結論:fl.controls(最初からFlashに付属しているコンポーネント)を含むSWCを作ると正しく読み込まれない
まずはこちらに検証用のプロジェクトを用意しましたので、ご覧になってみてください。
package {
import flash.display.MovieClip;

public dynamic class Abesi extends MovieClip
{
public function Abesi() {
trace("abesi");
trace(this.button, this.textArea);
}
}
}
package {
import flash.display.MovieClip;

public dynamic class Hidebu extends MovieClip
{
public function Hidebu() {
trace("hidebu");
trace(this.button, this.textArea);
}
}
}
とまぁ、なんの変哲もないタダのFlashコードをswcとして出力し、TestMainの中でnewして出力しているだけなのですが、なんとswcに書き出すタイミングに応じて確実にエラーになって落ちるというひどい問題があるようなのです。


■検証結果
以下のスプレッドシートにまとめてみました。
http://spreadsheets.google.com/ccc?key=0AoXhhCSOuqOtdE5rNy1wc2N2Z2JuV1NPUFBjdlRNeHc



たぶん私の書き出し方に何か問題があるのだろうとは思っているのですが、何処を見てもそれに関する情報が見あたらなかったので解決できず、結局swcの使用はあきらめることにしました><

2010年2月8日月曜日

ActionScript 3 の flash.net.Loader が読み込んだ MovieClip の挙動がローカルファイルとリモートファイルで変わるみたいです

flash.net.Loaderを用いて、リモートサーバーからswfファイルをロードし、画面に表示するようなアプリを書いていたのですが、そのときふとしたことから以下のような問題に気づきました。


■前提条件
仮に以下のような条件があるとします。
  • main.swfとresource.swfがあって、main.swfはflash.net.Loaderクラスを用いてresource.swfをロードしてくるものとする。
    var loader:Loader = new Loader();
    loader.load(new URLRequest("http://akisute.com/static/swf/sample.swf"); //リモートサーバーからロード
    loader.load(new URLRequest("../swf/sample.swf"); //ローカルファイルからロード
  • ロードしてきたswfは、以下のコードでloader.loaderInfo.contentから取り出して、MovieClipとして処理する。Loaderは破棄する。
    function onComplete(event:Event):void {
    var loaderInfo:LoaderInfo = event.target as LoaderInfo;
    var content:MovieClip = loaderInfo.content as MovieClip;
    var mc:MovieClip = content.getChildAt(0) as MovieClip;
    }
  • ロードしてきたswf(以下resourceと呼称)には合計200フレームのタイムラインがあり、1から100フレーム目までにleftというラベルがついて左向きのデータが、101から200フレーム目までにrightというラベルがついて右向きのデータが格納されている。1から100フレーム目までに右向きのデータは存在しない。その逆もしかり。


■問題
  • 対象のファイルresource.swfを、ファイルシステムからロードしたときは、なんの問題もなくラベルleftとrightを入れ替えることができる。
  • 対象のファイルresource.swfを、httpでリモートサーバーからロードしたときは、leftとrightを入れ替えた瞬間に、resourceのアニメーションがバグる。stop()やgotoAndPlay()などの命令を無視した動きをする。
  • ローカルファイルとリモートサーバー、どちらのresource.swfも完全に同一のファイル。ファイルサイズもタイムスタンプもsha1ハッシュも完全に一致。
・・・なにこれ。最初に見かけたときはAdobe焼き討ちじゃコラと思ったものです。


■調査:取ってきたswfをdescribeTypeしてみる
愚痴っても仕方がないので調査します。まずは取ってきたswfに対して、flash.util.describeType()を実行し、オブジェクトダンプしたXMLを見てみます。するとローカルファイルから取ってきた場合とリモートサーバーから取ってきた場合で、以下のような違い(diff)がある事が分かりました。
1c1
< ## LOCAL ##
---
> ## REMOTE ##
5,6c5
< <type name="sample_fla::left_33" base="flash.display::MovieClip" isDynamic="true" isFinal=&quot;false" isStatic="false">
< <extendsClass type="flash.display::MovieClip"/>
---
> <type name="flash.display::MovieClip" base="flash.display::Sprite" isDynamic="true" isFinal="false" isStatic="false">
15,18d13
< <variable name="leftArm" type="flash.display::MovieClip"/>
< <variable name="leftLeg" type="flash.display::MovieClip"/>
< <variable name="leftHead" type="flash.display::MovieClip"/>
< <variable name="leftBody" type="flash.display::MovieClip"/>
diffなのでちょっとわかりにくいかもしれませんが、よく見ると以下のような差異があることが分かります。
  • LOCALにはきちんとしたクラス名がついており、MovieClipのサブクラスになっている
  • LOCALにはプロパティ情報がきちんと残されている
  • REMOTEはタダのMovieClipクラスになっており、プロパティも一切無い
つまり、全く同一のファイルをロードしているにもかかわらず、flash.net.Loaderは、ローカルファイルからロードするかリモートサーバーからロードするかによって生成するMovieClipオブジェクトを変化させているということがわかりました。

どうしてこのようなことをになっているのか少し考えてみたのですが、おそらくセキュリティ要件の問題ではないかと言う結論に至りました。というのも見ての通り、ローカルファイルから読み込んだswfをdescribeTypeすると、クラス名としてファイル名とシンボル名がついてしまっていて、これを悪用することができるのではないかと危惧したのではと。

■対応:しかしさらに泥沼
クラス情報が欠落してMovieClipになってしまっているのが問題だとすれば、読み込まれる側のresource.swfのすべてのシンボルを「アクションスクリプトに書き出し」して、きちんとクラスとして定義すれば、問題が解決するのではないかと考えました。

ということで、早速試して見たところ・・・
TypeError: Error #1034: 強制型変換に失敗しました。flash.display::MovieClip@677b34c1
を rightArm に変換できません。

at flash.display::MovieClip/gotoAndPlay()
at com.akisute::Main/onLoadCompleted()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()
・・・bullshit。ジョブズがどうしてあんなにFlashを毛嫌いするのか分かった気がします(絶対違)。

まぁスタックトレースが出るようになってくれたので、なんとかなりそうです。さっきまではスタックトレースすら出さずに勝手にバグを出してくれやがってたので大変困っていました。まったく、これ読んで出直せ、Adobe。
Errors should never pass silently.


■考察:どうしてこうなるのか
さらに考えてみます。一見さっきのスタックトレースはロードに失敗してバグが発生している用に思われますが、実際にエラーを吐いているのはonLoadCompletedの中のgotoAndPlayです。そう、ロードが完了した瞬間にgotoAndPlay("right")を実行しているのですが、そこでエラーが発生しているのです。

ここでちょっと仮説を立ててみます。
  • ロードが完了された瞬間のresourceのcurrentFrameは1、つまりleftである。このとき、right側のパーツは一切読み込まれていない(nullである)。
  • gotoAndPlay("right")を実行すると、おそらくロードが完了しているMovieClip自身がタイムラインに従って101フレーム目以降の内部構造を補完しようとする。すなわち、right側のパーツ(MovieClipとかShapeとか)をどっからか取ってきてセットし、left側のパーツをnullにセットしようとする。ここまでの動きはデバッガを使って確認できました。
  • このとき、先ほどのflash.net.Loaderは完全な情報を持っていたのできちんとMovieClipを作ることができた(left側のパーツをセットできた)が、MovieClip自身にはその情報がないので、right側のパーツをセットしようとしてタダのMovieClipをrightArmクラスの変数にセットしようとし、キャストに失敗して落ちている。
・・・長くてすみません><
要するに、ローカルファイルからロードし生成されたMovieClipはデータを完全に保有しているのでMovieClip自身でnullフレームを埋めることができるが、リモートサーバーからロードしてきたswfから生成されたMovieClipは生成時にLoaderクラスによってデータを欠落させられているので、自力でnullフレームを埋めることができないのではないかと考えたのです。


■解決策:外部ロードするswfは、絶対に空のフレームを作るな
そこで以下のような対応をしてみました。
  • leftラベルの箇所にもright側のパーツを配置する。ただし見えては困るので、透明にして配置する。
  • 同様に、rightラベルの箇所にもleft側のパーツを配置する。
  • こうすることで、最初からすべてのフレームのデータがLoaderクラスによって読み込まれ、 nullフレームがないのでタイムラインがどのように動いても一度ロードされたデータが欠落することがない。従って問題なく動作する。
はたしてこの方法が功を奏し、見事に動作するようになりました!


■というのを
@moriyoshiさんから教えて貰いました。ありがとうございます!
・・・というか、なんでFlash使いでもないのにこんな詳しいんですか><

Mercurial と git の branch にまつわるちょっとした tips 3選

3選とか言ってますが大した内容ではございません>< すみませんすみません><


■1:gitのbranchは跡形もなく消せる
ほとんど常識ですが、以下のコマンドでgitのbranchは消せます。
git branch -D
このコマンドを実行すれば、たとえHEADに対してマージされていなくてもそのままブランチを消すことができます。ということで、ちょっとしたテストコードなどはブランチを切ってそこで実験し、後からブランチごとたたき落とす運用が可能です。

余談ですが、gitのbranchはSubversionと使い勝手や実装が似ている感じがします。Subversionのbranchもタダのディレクトリコピーなので、好き勝手に作って消してが可能ですから。


■2:Mercurialのbranchは基本消せない、「未使用」か「クローズ」にはできる
問題はここから。Mercurialのbranchは、基本的に完全に跡形もなく削除することができません。また、マージしていないブランチがあるとpushの際に怒られます。そのため、ちょっとしたテストコードをブランチ切ってそこで作成し、いざいらなくなったので削除しようと思うとえらい大変なことになります。

一応、ブランチを「クローズ」するコマンドを使うことで常用範囲内からは消す事が可能ですが、完全に情報が消えたわけではないのでリポジトリのサイズは小さくなりません。ゴミが溜まります。もしMercurialでテストコードを管理しようと思うならば、branchを切るのではなくリポジトリごとcloneしてそちらでテストする運用にすればよいです。


■3:Mercurialのbranchに数字で名前を付けてはならない
gitと違ってMercurialのリビジョンには、簡便のため、リビジョンのハッシュIDの他にリビジョン番号が1番から採番されるようになっています。で、各種操作の際に、このリビジョン番号を代わりに指定して実行することが可能です。

ここまではOK。

問題はbranchに数字の名前が付けられることで、もしbranchに数字の名前を付けてしまった場合、Mercurialはその数字をリビジョン番号として解釈してしまい、ブランチ名指定の処理ができなくなってしまいます。
hg branch 255
hg commit -m "created branch 255 for ticket #255"
hg update 255
# ブランチ255ではなくリビジョン番号255番に対してアップデートしようとする・・・
ただそれだけなのですが、チケットシステムなどと連携しているとよくチケット名でブランチを切る事があると思いますので、そのような際にはticket255などという風に指定するほうがよいかと思います。

2010年2月7日日曜日

Mercurial の、 hg revert / hg rollback / hg backout の使い分け

以前からgitを使っていたのですが、最近は職場のバージョン管理システムがMercurial hg になっているので、もっぱらhgばかり使っています。ということで、いくつか覚えたhgネタ。

Mercurialやgitに限らず、いかなるバージョン管理システムを使用していても、人間が使う以上運用中にミスが発生することは避けられません。今回はMercurial使用中に間違ったコミットやプッシュを行ってしまった際の対処法を調べてみました。

参考文献はこちら。
4798021741入門Mercurial Linux/Windows対応
秀和システム 2009-01

by G-Tools


間違いを修正するためのコマンドは、大きく分けて以下の3つがあります。
  • hg revert
  • hg rollback
  • hg backout
またコマンドを用いて修正する意外にも、ローカル作業する際に作業用リポジトリを別に作って、問題に気づいたらリポジトリごと削除するという運用もありますが、今回は説明を省きます。


■hg revert
適用可能なのは「ローカルのリポジトリ上で、コミットをする前に問題に気づいたとき」。
作業中にちょっと間違った際など、ファイル単位でコミット前の状態(parentの状態)に戻すことができ大変便利です。コミットしてしまった場合は、次のhg rollbackを使用します。


■hg rollback
適用可能なのは「ローカルのリポジトリ上で、コミットをした直後に問題に気づいたとき」。
直前の1回分だけ、commitを取り消すことができます。マージ作業だろうがなんだろうが跡形もなく消してくれるため、後から見ても痕跡がなく綺麗です。操作も簡単で安全なため、これが使用可能なときはこちらを使用すると便利です。

一応、直前の1回だけならリモートの共有リポジトリへのpushも取り消すことができるのですが、これは後述の理由からお勧めしません。一度リモートにpushしてしまったら、次のhg backoutを使って取り消すことになります。


■hg backout
適用可能なのは「ローカルのリポジトリ上で、複数回コミットした後にに問題に気づいたとき」か、または「リモートの共有リポジトリにプッシュしてしまった後に問題に気づいたとき」。
どうしようもなくなってしまったときの最後の手段がこれ。指定したリビジョンのcommitの内容を完全に打ち消す新しいリビジョンを作ってくれます。あとは、手動でこの自動で作られたリビジョンを元のリビジョンにmergeしてcommitすることで、いかなる問題であろうとも無理矢理打ち消す事が可能になります。

もっぱら間違ってプッシュしてしまった際に使用します。
一応hg rollbackでも直前のpushを取り消すことが可能ですが、一度pushされてしまった内容はどこかの誰かのリポジトリにpullされてしまっている可能性があり、そうなると共有リポジトリだけをrollbackしても効果がありません。(このように、間違いも分散して広まってしまうのが分散バージョン管理の恐ろしいところ)

このようなときは、hg backoutで修正リビジョンを作りマージしたあと、再度共有リポジトリにpushすれば、他の誰かのリポジトリに問題が取り込まれてしまっていても次のpullで無事修正されます。

2010年1月26日火曜日

Google Docs の Presentation がチャートを描くときに地味に便利らしい

@shibukawaさんに教えていただいたのですが、

http://twitter.com/shibukawa/status/8210913937

@akisutesama Google DocsのPresentationが地味に便利です。チャートを書く時には。


またまたぁ。Google DocsのPresentationなら昔一度使ったけど、最低限の図しか作れなかったですよ。と半信半疑でGoogle Docsを開いてみたら、



あらまぁこれは凄い。いつの間にこんなオプションが増えてるんですか。普通にPowerPoint 2003程度の図が描けてしまいました。
知らない間にどんどん進歩してますね、Google Docs。本当にそろそろOfficeいらないですね。

2010年1月9日土曜日

ASDoc の書き方講座

仕事用に作ったまとめをそのまま転載。


■参考リンク
以下のリンクを見ておけば一通り大丈夫です。
ASDoc コメントの作成
ASDoc タグ一覧
@see タグや@copy タグの使用方法
びんた先生(beinteractive)のパーフェクトASDocタグ講座


■Flex 3 SDKのasdocコマンドを使ってみる
asdoc -helpを見れば良いのですが、helpが見づらくてややっこしいので、まとめてみました。
http://livedocs.adobe.com/flex/3/html/help.html?content=asdoc_9.html#142061
以下のコマンドを入力すればOKです。ここで、$FLEX_HOMEは Flex SDKをインストールしたパスです。
asdoc -compiler.source-path=自分が追加したいソースパス, -compiler.library-path=$FLEX_HOME/frameworks/libs,自分が追加したいライブラリパス -doc-sources=ドキュメントに出力したいソースのパス
$FLEX_HOME/frameworks/libsをライブラリパスに含めないとビルドが通らないのがくせ者です。JavaDocはtools.jarを自動的にパスに含めてくれるんだから、ASDocだって自動的にやってくれと言う気がします>< ちなみに、Flex Builderとか使えばこんな面倒な事をしなくても簡単にASDocを出力することができるみたいです。


■気をつけるところ
ASDocの最初の一文は必ずピリオドで終了する
腹立たしいですがピリオド{{.}}が最初の一文の区切りになっているようなので、句読点{{。}}ではなくピリオドを使いましょう。 たとえば、
package {
/**
 * ASDocのテスト用のクラスです.
 * 
 * 第一文は必ずピリオドで終了する必要があります。
 * 試しに上記のピリオドを句読点に変更してドキュメントを出力すると違いが分かると思います。
 */
 public class ASDocTestClass {}
 }
のように書く必要があります。こうしないと、ドキュメントを生成したときに説明文が長ったらしくなって困ります。

パスワードが無くなってログインできない Mac に無理矢理ログインする方法

会社で前任者が使用していたMacを使用する際など、管理者ユーザーのパスワードが紛失してしまってログインできなくなってしまう場合があります。っていうか、実際になって大変困りました><
このような場合、Mac OS Xでは「シングルユーザーモード」でOSを起動して、ユーザーのパスワードを自由に書き換えてログインできるようにすることができます。万が一の場合に便利なのでご紹介いたします。

参考にしたページはこちら。
http://d.hatena.ne.jp/viz3/20091029


■対象環境
  • Mac OS X 10.4以上であること (launchctlを使用するため)
  • UNIX / Linuxの対話環境についてある程度以上の知識があること
  • 失敗しても泣かない心があること
大変申し訳ありませんが、こちらに記載されている内容を実行して失敗してシステムが起動しなくなっても当方は責任を負いかねます><


1.シングルユーザーモードでMacを起動する
Macの電源を押して起動する際に、キーボードのCommand + Sキーを同時に押しっぱなしにしておくと、シングルユーザーモードで起動することができます。真っ黒な画面に文字がずらずら出てきたら成功です。


2./を読み書き可能にする(再マウントする)
画面上に
If you want to make modifications to files:
/sbin/fsck -fy
/sbin/mount -uw /
と表示されているので、ここに表示されているコマンドをそのまま入力します。コマンドの内容は以下の通り:
  • /sbin/fsck -fyは強制的にディスクチェックとディスクの問題修正を行う。-yがついているので、全ての問い合わせに対してYESを返したものと見なす。
  • /sbin/mount -uw /はすでに読み取り専用モードでマウントされている/を再度書き込み可能なモードでマウントし直す。

3.dsclをlocalonlyモードで起動する
お次はdsclというツールを使用します。ですがこのツールそのままでは起動できないようなので(dscl .とタイプするとなにやら警告が出ると思います)、以下のようにしてlaunchctlを使ってdsclツールの設定ファイルを読み込んでから、dsclをlocalonlyモードで起動します。
launchctl load /System/Library/LaunchDaemons/com.apple.DirectoryServicesLocal.plist
dscl localonly


4.対象のアカウントのパスワードを書き換える
dsclがlocalonly対話モードで起動したら、cdコマンドとlsコマンドを利用して対象のアカウントのある/Local/Target/Usersまで移動します。
 > ls
Local

Contact
Search
> cd Local
/Local > ls
Default
Target
/Local > cd Target
/Local/Target > cd Users
/Local/Target/Users > ls
ずらずらとここにアカウント名が羅列されるはず
ここまで移動できたら、以下のコマンドを入力すればパスワードが書き換わります。
passwd 対象のユーザー 新しいパスワード
たとえばさっきの状態からユーザーakisuteのパスワードをabesiに書き換えたいなら、
passwd akisute abesi
です。


5.再起動してログインする
あとは再起動して普通にMacを起動します。
reboot
先ほど入力したパスワードでログインできるはずです。


■注意
この方法を用いれば、物理的にアクセス可能な任意のMacの操作権限を奪うことが可能になってしまいます。くれぐれも悪用しないように。

逆に、この方法を用いてアクセスされたくないMacについては、以下のページで紹介されている方法を用いてシングルユーザーモードによる起動を禁止することができます。
Mac OS X 10.1 以降で、ファームウェアのパスワードプロテクションを設定する


■UNIX / Linuxユーザー向け
Mac OS Xのユーザーって、/etc/passwdじゃなくてDirectory Serviceで管理されてるんですねー。最初/etc/passwd以下を調べに行ってハマりました・・・

2010年1月3日日曜日

Google Code Prettify のテスト

■Python(自動判別)
abesi='abesi'
hidebu=3
print abesi+hidebu
def tawaba(n=0):
result = 0
for i in range(n):
result = result + i
return result
きちんと自動判別してくれているみたいです

■Python(言語指定)
abesi='abesi'
hidebu=3
print abesi+hidebu
def tawaba(n=0):
result = 0
for i in range(n):
result = result + i
return result
lang-pyを指定してみました

■Objective-C(自動判別)
NSString *abesi = @"abesi";
int hidebu = 3;
NSLog(@"%@%d", abesi, hidebu);
@synthesize anProperty;
#pragma mark -
#pragma mark Functions
int tawaba(int n) {
int result = 0;
for (int i=0; i<n; i++) {
result += i;
}
return result;
}
こちらも判別に成功。

■JavaScript(自動判別)
var abesi = 'abesi';
var hidebu = 3;
console.log("%s%d", abesi, hidebu);
// 日本語コメントのテスト
/**
* 日本語コメントのテスト
*/
function tawaba(n) {
var result = 0;
for (var i=0; i<n; i++) {
result += i;
}
return result;
}
日本語コメントのテストなど。

いまいち配色が気に入りませんが、svnのtrunkに直リンクしていてこちらでは修正できないのでまぁ仕方ないかなと思います。それからActionScript3に未対応なのがかなり不満です。外部CSSとJavaScriptが増えて重くなるしonloadのタイミングで実行しなくちゃいけないし・・・Googleらしからぬイマイチさ。