2010年 02月 01日




なんか知らないけど、100mm で撮った写真の Exif 見てると 13.2m のところにピントあわせてることが多い。

2010年 02月 02日








ものの正面に立つのがうまくなった気がする

2010年 02月 03日







2010年 02月 05日







意外と空の写真を撮ってるなぁ。空ほど、写真にしてあとで見てがっかりするするものはない。







こういうシーンだとケータイの撮って出しでもいい感じ (一番下のはたぶん素子の許容量超えて光が入って飽和して色が変わってる? フレアがでたらいいなぁと思って撮ったらこうなった……)

2010年 02月 04日

Fotolife for Android リリース

Fotolife for Androidがリリースされました。

期間的にはだいたい1ヶ月で、そんなに短かいわけではないのですが、割と大変でした。Android ケータイはもちろん持っていて、使っていたわけですが、開発は Scala で rake 使って Hello, World! を動かす程度しかやっておらず (いろいろ間違ってる)、Java 自体、Rhino、Scala で間接的に一部 API を使ったことがあるだけで、ちゃんと Java のコードを書いたのは開発が決まってからでした。

「Java の文法・API」と「Android の API・バッドノウハウ」の二重でハマるので、特に最初の2週間は、アプリケーションも形にならず (アップロードもできず、表示もろくにできなかった)、たいへんしんどかったです。ちゃんとしたマルチスレッドも、普段 Perl, JavaScript ばかりなので同期方に慣れずに苦労しました (Ruby も割とシングルスレッド脳で書いてるし)、というか、いまもしてます。

スレッドまわりに関しては「Java 言語で学ぶデザインパターン入門 マルチスレッド編」を借りて読んだり、「Java 並行処理プログラミング」を買ってもらって読んだりしてみました。どちらも解りやすく、後者は特に何を使うべきかが明確で実践に即応用できたので良かったです。java.util.concurrent スゲー! ってことで使いまくってます。ソースコードは Future だらけです。new Thread() を全然書いてないです。

基本的に、Android の開発は Google のオフィシャルのドキュメントをちゃんと熟読するのが近道だと思いました。あと、それだけだと、僕みたいなのは頭に入ってこないので、android のレポジトリを全部落としてきてデフォルトで入っているアプリのソースを読むのがいいと思いました。SDK のサンプルは最小限で、それはそれでいいのですが、もっと具体的にどうするのかがよくわからなかったりするので、やっぱちゃんと動いてるコードは偉大です。

Java は食わず嫌いでしたが、やってみると案外楽しくて、思ったより書いていてイライラしない言語だなぁと思いました。基本部分のAPI (特に Stream 系) は死ぬほど冗長で意味がわからないのですが、Android 関係の API は割と普通に使える印象でした。(そのうち書きますが Eclipse と Vim を連携させて書いてます) 匿名クラス萌えです。

id:secondlife さんディレクションのもと開発し、id:nagayama さんに各種アイコンのデザイン、id:aaron さんに英語リソースのチェックをしてもらい、id:tikeda さん、id:hakobe932 さんに直前にフィードバックを貰いました。

しばらく社内用に書いたノウハウを公開していこうと思います。Fotolife for Android のAPI (インテント) も公開するので少々お待ちください。


2010年 02月 05日

Android の組込みアプリ以外のパーミッションの取り扱い

はてなアカウント管理を作るにあたり、最初は Android のパーミッションの仕組みにのっかろうと考えていましたが、調べていくうちにセキュリティリスクがあることがわかったので、別途自力でユーザにオプトインする仕組みを入れました。(はてな以外からリリースされるアプリケーションでは、アカウント管理に情報を渡す際に本当に渡すかをユーザに同意を求めるようになっています)

Android のパーミッションの仕組みは「先に定義したもん勝ち」という感じのようなので、端末デフォルトでインストールされていて、アンインストールが不可能になっているアプリケーションでのみ、安全に働くようでした。なんかやりかたがあるのかよくわかりませんが、そんな感じでしたので、今のところ一番確実な方法を選んでいます。

手元の実機 (Android 1.6) での検証では

  • <permission android:name="com.example.permission.FOOBAR" android:protectionLevel="dangerous".../> というパーミッションを定義する、アプリケーションA
  • それを uses-permission で定義する、アプリケーションB
  • <permission android:name="com.example.permission.FOOBAR" android:protectionLevel="normal".../> というパーミッションを定義する、悪意あるアプリケーションC

A の signature と B, C の signature は別

A → B とインストール

  1. A インストール
    • permission が定義される
  2. B インストール
    • uses-permission の内容が表示される
  3. B で該当パーミッションAに対し使おうとする
    • 成功する

→ 正常

B → A とインストール

  1. B インストール
    • uses-permission は表示されない (Unknown permission)
  2. A インストール
    • permission が定義される
  3. B で該当パーミッションAに対し使おうとする
    • 該当パーミッションは定義されていないためセキュリティエラー Permission Denial

→ 正常 (この場合 B をインストールしなおすしかないのでユーザビリティ的に問題があるため採用できない)

C → A → B とインストール

  1. C インストール
    • permission が定義される (protectionLevel="normal" == ユーザに確認がでないパーミッション)
  2. A インストール
    • permission は定義されない (既に存在するので無視される)
  3. B インストール
    • uses-permission の内容が表示されないが C で定義された permission が grant される
  4. B で該当パーミッションAに対し使おうとする
    • 成功する

Bがユーザへの確認なしに、不正にパーミッションを行使していることになる。

インストール関係のインテントとかをコマンドラインから発行する

パーミッションのテストみたいなのは死ぬほどめんどいです。コマンドラインからやってもめんどい。

adb install は PackageManager を使わないのでダメです。もちろんちゃんと sign した apk じゃないとダメです。

adb push Foo.apk /sdcard
adb shell am start -a android.intent.action.VIEW -t application/vnd.android.package-archive -d file:///sdcard/Foo.apk

でインストール

adb uninstall com.example.foo

でアンインストール (パッケージ名を指定するみたいです)


ちまたの sdcard からアプリをインストールするやつは、上記 Intent を投げているだけなので、特別パーミッションがいらないはずですが、なんかやたらパーミッションを要求するのが多くて、開発者以外にテストしてもらう際に困ります。

全くパーミッションを要求しないやつには com.mmg.appin というものがあるっぽいです。

ウェブアプリとAndroidアプリの対応

  • ページ表示 (View, Controller) → Activity
  • モデル → ContentProvider
  • ワーカー → Service
  • cron バッチ → AlarmManager
2010年 02月 06日

Eclipse + Vim 開発環境構築

Android SDK は Eclipse 前提でだいたい話が進むので、かたくなに Eclipse を使わないようにしているといらない苦労を負いすぎます (リソース管理とか)。ので、これを期に Eclipse と連携させて Vim を使う環境をつくりました。

Eclipse をコンパイル、実行環境にし、編集を Vim にするために

  • Eclim をいれる http://eclim.org/
  • eclimd を起動 (Window -> Show View -> eclimd or /Application/Eclipse/eclimd) して :PingEclim :EclimValidate がうまくいくか確かめる。ぼくは大丈夫だった。ダメなら FAQ
  • ワークスペースの設定をする
    • Eclipse デフォルトなら変えなくてもいいっぽいけど、僕の場合 ~/project なので
  • eclimd はメモリ増やして起動する
  • :ProjectInfo がうまくいくかを確認する。できなければ FAQ を見る

AutoComplPop の設定

.vimrc に追記

let g:AutoComplPop_Behavior = {
      \   'java' : [
      \     {
      \       'command'  : "\<C-n>",
      \       'pattern'  : '\k\k$',
      \       'excluded' : '^$',
      \       'repeat'   : 0,
      \     },
      \     {
      \       'command'  : "\<C-x>\<C-u>",
      \       'pattern'  : '\k\k$',
      \       'excluded' : '^$',
      \       'repeat'   : 0,
      \     },
      \     {
      \       'command'  : "\<C-x>\<C-f>",
      \       'pattern'  : (has('win32') || has('win64') ? '\f[/\\]\f*$' : '\f[/]\f*$'),
      \       'excluded' : '[*/\\][/\\]\f*$\|[^[:print:]]\f*$',
      \       'repeat'   : 1,
      \     }
      \   ]
      \ }

キーワードマッチしなければ eclipse に投げる (めっちゃ重い)。

デバッグ

保存すると自動で :Validate が走る。:lope で確認できる。+sign つきで vim がコンパイルされているならいい感じにでてくる!

:Validate が走るタイミングが保存よりも早いときがあってちょっと変

import 分関係

http://eclim.org/vim/java/import.html

  • :JavaImportMissing
  • :JavaImportClean

override しまくる

:JavaImpl すると override できるメソッド一覧がでてくるので、適当に RET おすと テンプレートが挿入される。便利なんだけど、Vim の undo が効かなくなってしまうのであんまり使ってない (表示させてコピペ、っていう使いかたをしてる)

データクラス

ハッシュを自由に作れないのが LL に比べて極めて不便なので、つどデータクラスを作るわけですが、getter setter 作るのが死ぬほどめんどい。

  • :JavaGetSet

Android アプリケーションのヒープメモリを観察する

メモリが限られていると頻繁に OOM エラーがでますが、OOM エラーは出さないことが大前提なので、頑張って何が容量食ってるか調べないといけません。まぁ、コード的に勘が効く部分は良いのですが、ライブラリに隠蔽されていたり、普段 LL 使ってると見落したりする部分があるのでちゃんと計測したいもんです。

http://www.eclipse.org/mat/ をいれる。

エミュレータでアプリケーションを起動して、ある程度動かしてから

↑ のボタンを押す

メモリーリークしているっぽいものまで解析してくれる。すごい

Histogram -> ソート -> 右クリック -> List objects でどんなオブジェクトがみれるっぽい。List objects の outgoing incoming はよくわからない。

開発中これを使ったら、画像のURLを保持する的な部分で随分メモリを食っていることが分ったので、はてな記法のみを保持してURLとかは必要なときだけ更めて生成 (パフォーマンスは劣化しますがOOMでるよりはマシです) するようにしたらだいぶ改善しました。

あと、スレッドの一覧を見ながら動かして想定通りのスレッド数かを観察するのも大事だと思いました。HttpClient はインスタンスごとにプールスレッドを持つので注意が必要でした (若干反則な気がしますが static に確保するようにしました)

2010年 02月 07日







雪が猛烈に降ったり、止んで太陽が出たりと、安定しない日だった。おかげで、雪の中を自転車で走りまわることになり、風邪をひきそうだなぁという感じだった。どうなるかな。

滅多に使わない、シャッタースピード優先で撮ったりしたけど、慣れてないと難しいですね。ストロボを持っていないので、太陽がでてるときうまいこと光を使う必要がありめんどかった。

2010年 02月 08日




2010年 02月 09日

Good Dog Happy Men の新譜 The Light が先日でたので聴いているけど、慰霊堂清掃奉仕 (Happy Birthday!) が本当にとてもいい…… こういうちょっと皮肉まじりのファンタジー世界観はいいなぁ。

アルバム全体として GDHM はあんまりキャッチーなの作らないなーという感覚が強くなってきた。しばらく聴いてないとよくわからない。

2010年 02月 10日



2010年 02月 09日

Android リファレンスを Chemr で検索

SDK が手元にあると思うのでそれでインデックスさせたのをつかってる。CSS は付属のやつを生かす方向で、一部の要素を非表示に……

http://gist.github.com/299044

2010年 02月 11日









2010年 02月 10日

Android Emulator で DNS がひけない場合

エミュレータを

emulator  -dns-server 8.8.8.8  -avd android1.6   

みたいに起動。プライベート IP を指定するとアクセスできなかったりするので Google DNS をつかうのが楽

さらに、自作のアプリケーションの場合、

    <uses-permission android:name="android.permission.INTERNET" />

が入っていることを確認する。

Android でアプリケーションが強制終了したとき、エラーレポートを送るようにする

フォトライフアプリは例外発生時にバグレポを送れるようになってます。

バグレポの仕組みは、http://www.adamrocker.com/blog/288/bug-report-system-for-android.html で述べられているようなやり方で、フォトライフアプリではサーバサイドなしにしたりとか簡略化して実装をしています。

開発中やらデバッグ中も、ケーブルレスでエラースタックトレースが見れるようになるのでとても便利です。

100行程度なので、めんどくさいのでコード貼っておくと

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Build;

class ErrorReporter implements UncaughtExceptionHandler {
    private static Context sContext = null;
    private static PackageInfo sPackageInfo = null;
    private static ActivityManager.MemoryInfo sMemoryInfo = new ActivityManager.MemoryInfo();

    private static final String BUG_FILE = "BUG";
    private static final String MAIL_TO  = "mailto:cho45@lowreal.net";
    private static final UncaughtExceptionHandler sDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();

    public static void setup(Context context) {
        context = context.getApplicationContext();

        try {
            sPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }

        sContext = context;

        Thread.setDefaultUncaughtExceptionHandler(new ErrorReporter());
    }

    public static void bugreport(final Activity activity) {
        File bugfile = activity.getFileStreamPath(BUG_FILE);
        if (!bugfile.exists()) return;

        File dstfile = activity.getFileStreamPath(BUG_FILE + ".txt");
        bugfile.renameTo(dstfile);

        final StringBuilder body = new StringBuilder();
        String firstLine = null;
        try {
            BufferedReader br = new BufferedReader(new FileReader(dstfile));
            String line;
            while ((line = br.readLine()) != null) {
                if (firstLine == null) {
                    firstLine = line;
                } else {
                    body.append(line).append("\n");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        final String subject = firstLine;
        new AlertDialog.Builder(activity)
            .setIcon(R.drawable.icon)
            .setTitle(R.string.bug_report)
            .setMessage(R.string.bug_report_message)
            .setPositiveButton(R.string.bug_report_positive_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    activity.startActivity(
                        new Intent(Intent.ACTION_SENDTO, Uri.parse(MAIL_TO))
                            .putExtra(Intent.EXTRA_SUBJECT, subject)
                            .putExtra(Intent.EXTRA_TEXT, body.toString())
                    );
                }
            })
            .setNegativeButton(R.string.bug_report_negative_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                }
            })
            .show();
    }


    public void uncaughtException(Thread thread, Throwable error) {
        error.printStackTrace();

        try {
            PrintWriter writer = new PrintWriter(sContext.openFileOutput(BUG_FILE, Context.MODE_WORLD_READABLE));
            if (sPackageInfo != null) {
                writer.printf("[BUG][%s] versionName:%s, versionCode:%d\n", sPackageInfo.packageName, sPackageInfo.versionName, sPackageInfo.versionCode);
            } else {
                writer.printf("[BUG][Unkown]\n");
            }
            try {
                writer.printf("Runtime Memory: total: %dKB, free: %dKB, used: %dKB\n",
                    Runtime.getRuntime().totalMemory() / 1024,
                    Runtime.getRuntime().freeMemory() / 1024,
                    (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024
                );
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                ((ActivityManager)sContext.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(sMemoryInfo);
                writer.printf("availMem: %dKB, lowMemory: %b\n", sMemoryInfo.availMem / 1024, sMemoryInfo.lowMemory);
            } catch (Exception e) {
                e.printStackTrace();
            }
            writer.printf("DEVICE: %s\n", Build.DEVICE);
            writer.printf("MODEL: %s\n", Build.MODEL);
            writer.printf("VERSION.SDK: %s\n", Build.VERSION.SDK);
            writer.println("");
            error.printStackTrace(writer);
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        sDefaultHandler.uncaughtException(thread, error);
    }
}

てな感じで

  • R.drawable.icon
  • R.string.bug_report
  • R.string.bug_report_message
  • R.string.bug_report_positive_button
  • R.string.bug_report_negative_button

あたりをリソースに使いつつ、各 Activity の onCreate で

ErrorReporter.setup(this);

を早い段階で呼び、

if (mPref.getBoolean(Setting.SEND_BUG, true)) ErrorReporter.bugreport(FooActivity.this);

を適当に、ダイアログを表示する準備ができたときに呼んでやっています。

ErrorReporter.bugreport は前回アプリケーションが強制終了した痕跡があるなら、ダイアログを表示してユーザにレポートの送信を求めます。やってることは単にメール Intent を投げてるだけなので、特にパーミッションもいりません。

2010年 02月 12日


斜め上から目線ってのが流行ってるんですかね?

インターネットにも結局、賑やかな孤独があるだけだった。無意味だ。

何書いてもバカにされるだけ。↑ みたいなのを見て笑ってるやつばっかりってこと

2010年 02月 13日





2010年 02月 14日




2010年 02月 15日


ほんと晴れないなぁ… せめて雪が降ってくれると……

修学旅行ぶりに金閣寺に行った。財布に1000円入っていなかったので入場できるか不安だったけれど、400円だった。御札が入場券の変わりになっていた。御札をもらっても (宗教的な理由で) いらない人はどうするのかと思ったけど回収箱が設置されてるっぽかった。

意外と記憶の中の金閣寺と違っており、派手だった。金色だなーと思って地味に感動した。眺めながらよくよく考えてみると、金閣寺のリアルな記憶よりも、プラモデルで金閣寺を作ったときの記憶のほうが強いことに気付いた。

修学旅行のときは「金閣寺で外国人観光客とコミュニケーションをとれ」という課題がでていてテンパった覚えがある。この課題、よく考えるとどうなんだって感じもする……

[[foursquare]] というサービスを使ってみているけど、なんともいえないなぁ。バッジ集めるのは楽しいんだけど、それ以上が特にない…… というのも京都内の Venue も TODO もサッパリ登録されていないせいな気がする。人口密集地とか派手な観光地じゃないと面白くないのかもしれない…… 位置情報ゲームで終わってないところは好きなので少し継続してやってみる。

神社も全然登録されてないので現地で登録するんだけど、なんかGPS情報をちゃんと使わないっぽく、ウェブ上で見るとよくずれていて自力で修正するハメになる。意味がない…… 僕は写真にGPS情報付与して撮っているので (GPS 情報を保存するためだけに写真撮ったりする)、それ上げれば済むとかになってればいいのになぁ。

バックエンドが Scala で再構築されたらしいので、そっちのほうでの興味もあります。

2010年 02月 16日




一方的に、高台から発砲して人を殺すことを自覚してるのかしてないのか

[tabelog:896885:detail]

割とよく行きます。


[tabelog:900921:detail]

この前 nanto_vi さんと motemen さんと一緒に行きました。パフェうまかったです (京大学食)


[[たべぞうさん、お楽しみ袋欲しい!]]

2010年 02月 17日





5つ以上スターを連打するとグリーンスターつけるように変える Greasemonkey script

5つ以上連打しようとすると勝手にグリーンスターを消費するようになるグリモンです。

グリーンスターに切り替わるとつく速度が落ちますが、連打したぶんはちゃんとつくので待っていてください (色つきスターはリクエストが増えるみたいなので純粋につけるのに時間がかかってます)

最近 Java の Executors にラブなので SingleThreadExecutor みたいなやつをつくってやってます。

2010年 02月 18日


2010年 02月 20日

最近 www.lowreal.net のページの作り直しをしている。もともと、1ページだけ好き勝手いろいろやってみるためのものだったので大分適当だったのを、もうちょいちゃんと自己紹介用のページに使えるようにするという狙いと、あとまぁやっぱ好き勝手またやってみたくなったという感じで、久しぶりに HTML のマークアップから CSS、 JS までいろいろと使った。

今回は、普通に内容情報を変更をするとともに、ページ単体でのスマートフォン (Mobile Webkit = iPhone, Android) 対応と、実験的に巨大スクリーン対応を入れている。

スマートフォン対応は大抵のまともなサイトは最近入れはじめているし、小さいスクリーン用に別の情報コントロールを入れるのはまぁ正しいと思うので、適度にそうやった。このページは twitter からの流入が一番多いし意味もある程度はありそう。

巨大スクリーン対応は、デカイ画面に全画面にすると余白があきすぎで滑稽になるのがあまりにもあれなので (かつてはまぁリキッドレイアウトならば、利用者が適時ウィンドウサイズを変えればいいだろうと思っていたけれども、そうはいっても、という感じなので)、特定サイズ以上のウィンドウサイズになったら、ページ全体を拡大するように (1240 を基準として、拡大率が 1.4 倍以上になるなら適用 = 1920程度から拡大される) してみた。(WebKit, Gecko のみ) この対応が正しい対応かどうかはちゃんと考えきれてはいない。フォントサイズが固定ではなく、エラスティックレイアウトなので、ピクセル基準ではなくフォントサイズ基準にすべきだけどとりあえず。

PC で開いたとき、一番上の画像を切り替わるようにしてみたけど、なんとなく実装したらおもいのほか簡単にできてびっくりした。簡単に管理したいなーと思ったので、切り替わる画像は fotolife の特定 ID の RSS からとっていて、あとは fotolife にアップロードしていくだけで切り替わる写真が変化していくので、管理も楽な感じになった。fotolife のフィードなら何でもいけるのでブログパーツみたいにできそう。

2010年 02月 21日



JavaScript、仮引数で宣言した変数へ代入したあとの arguments の挙動

さて問題です。以下のコードで alert されるのは何でしょう!!

(function (x) {
  x = 2;
  alert(arguments[0]);
})(1);


答えはやってみてください。ビビりました。どうやら arguments オブジェクトは、変数の参照 (値の参照ではなく) を持っています。

ECMAScript 3rd Edition 日本語訳から根拠を探すと、ちゃんと書いてありました。

  • 0 以上 length プロパティの値未満の整数 arg それぞれについて、属性 { DontEnum } のプロパティ ToString(arg) が作成される。このプロパティの初期値は対応するパラメータの呼出側に供給される実際の値である。最初の実際のパラメータ値が arg = 0、2 番目は arg = 1, 以下同様である。arg が Function オブジェクトの仮引数の数より小さい場合、このプロパティは Activation オブジェクトの対応するプロパティとその値を共有する。このことは、このプロパティの変更が Activation オブジェクトの対応するプロパティを変更すること、そしてその逆を意味する。
http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/10_Execution_Contexts.html#section-10.1.8

ちゃんと説明しとくと、この動作の奇妙さは

var obj = new Object();

obj[0] = 1;
obj.foo = obj[0];

obj[0] = 2;
alert(obj.foo); //=> 1

var baz = new Object();
baz.foo = obj[0];

obj[0] = 3;
alert(baz.foo); //=> 2

というあたり前 (に感じる) 挙動崩れるところです。普通、オブジェクトの別プロパティに代入したら、他のプロパティの値も同時に変わることなんてことはありえないし、別オブジェクトのプロパティが変わるなんてもってのほかですが、Activation オブジェクトの引数関係のプロパティだけは違う、ということです。

Activation オブジェクトはそもそも何かというと、変数を保持するオブジェクトです。この、隠れたオブジェクトを見えるようにしたコードを書くと、冒頭のコードは

(function (x) {
  // // Variable Instantiation
  // var a = new Activation(); 
  // a.arguments = { };
  // a.arguments[0] = 1;
  // a["x"] = a.arguments[0];
  // // End

  // a["x"] = 2;
  x = 2;

  // alert(a.arguments[0]);
  alert(arguments[0]);
})(1);

のように (実際は Activation オブジェクトにはアクセスできない) なり、そもそも仮引数 x が保持されているオブジェクトと、渡された引数第一番目の arguments[0] は別々のオブジェクトなのにも関わらず、値が共有されて相互作用をします。

これは「変数」を保持している「オブジェクト」が存在するという仕様上の説明からすると、一貫性がない不可解な動作です。

2010年 02月 22日



土曜日は清水寺にいった。金閣寺が良かったので清水寺もいいかもしれないと行ってみた。けど、どうも、人が多すぎて全くおもしろくなかった。まぁもう行かなくていいかなという感じだった。あと、地主神社 (清水寺境内にある) もいったけど、そんなに面白くなかった。

近くの豊国神社にいったら、思ったよりも立派ですごかった。スカっとする厳格さがある神社だった。

日曜日は前々から行こうと思って、なかなかタイミングがあわなかった[[愛宕神社]]にいった。朝8時ぐらいに目が覚めて、大阪のほうの別の神社と悩んだ末に愛宕神社へ。奇跡的に晴れたのもあって、ものすごく良かった。

前もって想像していた感じでは、大山阿夫利神社本社みたいに社殿はこじんまりした感じで、あとは社務所というか休憩所だけデカいのかなーと思っていたのだけれど、そんなことなくて、社殿もものすごく立派だったし、厳格で美しかった。

愛宕神社、でかい神社では珍しく本殿も割と近くまで普通に入っていける。いくつか社殿はあったけど本当に良かったとしか言いようがない。

冬の愛宕山の山頂付近ということで、そもそも人も少なくて (それなりにはいるけど)、晴れていて、雪が残っている、というとてもいい状態だった。

神社まで最寄りバス亭から2時間山登りで、結構キツいかなぁと思って上っていったけど、それほどでもなかった。とはいえそれなりにはキツかった。雪が残っている山頂付近は滑って転びそうで怖かった。大山ほど登山道がひどいことになっていないし、落ちついていて、少し軽めの登山にもいいなぁと思った。また暖くなったら行きたい。

あと、登山靴ほしい (雪が残りすぎていて山頂までいけそうにないならひきかえそうと思ってた)

鐸比古鐸比賣神社はヲタク的聖地巡礼の類です。文乃さん関係です。

2010年 02月 21日

JSDeferred Deferred.chain

http://lab.hisasann.com/addCommand/index.html を見ていて、next().next() 以外に、こういう風に書けても別にいいよなぁと思ったので実装を書いてみた。next().next() はめんどい割とめんどいし

git master HEAD には今のところ Deferred.chain() として入ってる。これと Deferred.connect() の変数バインドを組み合せて、addCommand.js のデモ相当をしてみた。

それっぽいとこを抜きだすと

Deferred.define();

var img1 = $("#img1"),
	img2 = $("#img2"),
	img3 = $("#img3");
(function() {
	var callee  = arguments.callee;

	var animate = function (target, prop, speed, easing) {
		return Deferred.connect(target, "animate", { args: [ prop, speed, easing ] });
	};

	Deferred.chain(
		animate(img1, {
			opacity: 0, top: 90 + "px", left: 260 + "px"
			}, 1000, "easeInOutCirc"),
		animate(img2, {
			opacity: 0, top: 170 + "px", left: 450 + "px"
			}, 1000, "easeInOutCirc"),
		animate(img3, {
			opacity: 0, top: 260 + "px", left: 620 + "px"
			}, 1000, "easeInOutCirc"),
		[
			animate(img1, {
				opacity: 1, top: 0 + "px", left: 0 + "px"
				}, 1000, "easeInBack"),
			animate(img2, {
				opacity: 1, top: 0 + "px", left: 0 + "px"
				}, 1000, "easeInBack"),
			animate(img3, {
				opacity: 1, top: 0 + "px", left: 0 + "px"
				}, 1000, "easeInBack")
		],
		function () {
			return wait(0.5).next(callee);
		},
		function error (e) {
			alert(e);
		}
	);
})();

となっていて、デモのコードとほぼ同じになる。エラーをキャッチする関数には function error() としておくルールがある。

キモは animate() を実行した時点ではアニメーションは実行されず、アニメーションを実行する関数を返すだけのところです。(Deferred.connect は関数を返す関数です)

便利なので入れたい気もするけど、うまく書かないと混乱するのでむずかしい。

2010年 02月 23日

デバッグの基本は問題の切り分けなのだから、下手にあんまりいじってはいけないのだけれども、人生はみじかく試行錯誤ができないために、どうしようもないことは多々ある



todo

jsdeferred の recipe をかく

gerry++

2010年 02月 25日



2010年 02月 26日


2010年 02月 27日