買った本とCD

合計金額 (18点): 27,315円

マリア様がみてる 32 卒業前小景 (コバルト文庫) 時載りリンネ! 4    とっておきの日々 (角川スニーカー文庫) ひぐらしのなく頃に解 第三話~皆殺し編(下) (講談社BOX) 僕僕先生 薄妃の恋―僕僕先生 鉄腕バーディー 20 (ヤングサンデーコミックス) PSYREN-サイレン- 3 (ジャンプコミックス) 女王蟻 3 (バーズコミックス) となりのネネコさん (1) (ウンポコ・コミックス) となりのネネコさん (2) (ウンポコ・コミックス) 桜庭一樹読書日記―少年になり、本を買うのだ。 書店はタイムマシーン―桜庭一樹読書日記 BACKSTAGE PASS (バックステージ・パス) 2008年 11月号 [雑誌] TECH GIAN (テックジャイアン) 2008年 11月号 [雑誌]

Ajaxセキュリティ スクリプトエンジン プログラミング 実例で学ぶゲーム3D数学

UNI-VERSE

買ったアイテムの合計金額を表示するGreasemonkeyスクリプト

インストールすると日付の横に「合計金額を計算」というリンクが現れる。それをクリックすると、その日の日記に含まれるアイテムの金額の合計が計算される。

// ==UserScript==
// @name           How much money do I waste?
// @namespace      http://d.hatena.ne.jp/nozom/
// @include        http://d.hatena.ne.jp/*
// ==/UserScript==

(function(){
    var item_price = {};
    var item_title = {};
    var req = {};

    function number_to_human_readable(n) {
        var num = new String(n);
        while (num != (num = num.replace(/^(-?\d+)(\d{3})/, "$1,$2"))) {};
        return num;
    }

    function getElementsByXPath(xpath, doc, context) {
        if (doc == null) doc = document;
        if (context == null) context = doc;
        var it = doc.evaluate(xpath, context, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);

        var nodes = new Array();

        var node;
        while ((node = it.iterateNext()) != null) {
            nodes.push(node);
        }
        return nodes;
    }

    function get_asin_from_link(link) {
        if (link.match(/\/(ASIN|asin|asininfo)\/([^\/]+)/))
            return RegExp.$2;
        if (link.match(/\/gp\/product\/([^\/]+)/))
            return RegExp.$1;
        if (link.match(/\/dp\/product\/([^\/]+)/))
            return RegExp.$1;

        return null;
    }

    function get_title(doc) {
        var title = doc.getElementsByTagName('title')[0];
        if (title) {
            return title.textContent;
        } else {
            return null;
        }
    }

    function get_amazon_price(doc) {
        var nodes = doc.getElementsByTagName('li');
        for (var i = 0; i < nodes.length; i++) {
            if (nodes[i].className == 'price' &&
                nodes[i].textContent.match(/Amazon価格:/)) {
                var node = nodes[i].getElementsByTagName('strong')[0];
                if (node) {
                    var price = node.textContent.replace(",", "");
                    return parseInt(price);
                }
            }
        }
        return null;
    }

    var handlers = [];
    function add_handler(handler) {
        handlers.push(handler);
    }

    function call_handlers() {
        var new_handlers = [];
        for (var i = 0; i < handlers.length; i++) {
            var handler = handlers[i];
            if (!handler()) {
                new_handlers.push(handler);
            }
        }
        handlers = new_handlers;
    }

    function new_request(asin) {
        req[asin] = GM_xmlhttpRequest({
            method: 'GET',
            url: "http://d.hatena.ne.jp/asin/" + asin,
            onload: function(req) {
                var d = document.createElement('div');
                d.innerHTML = req.responseText;
                item_price[asin] = get_amazon_price(d);
                item_title[asin] = get_title(d);
                req[asin] = null;
                call_handlers();
            },
        });
    }

    function update_label(a, n) {
        a.textContent = "合計金額を計算中... (残り: " + n + ")";
    }

    function create_result_node(total_price, n, errors) {
        var span = document.createElement('span');
        span.style.fontWeight = 'normal';
        span.style.fontSize = '80%';
        span.style.float = 'right';

        var text = "合計金額 (" + n + "点): " + number_to_human_readable(total_price) + "円";
        span.appendChild(document.createTextNode(text));
        if (errors.length > 0) {
            span.appendChild(document.createTextNode(" (エラー: "));
            for (var i = 0; i < errors.length; i++) {
                if (i > 0) {
                    span.appendChild(document.createTextNode(", "));
                }
                var asin = errors[i];
                var a = document.createElement('a');
                a.title = item_title[asin];
                a.href = "http://d.hatena.ne.jp/asin/" + asin;
                a.textContent = asin;
                span.appendChild(a);
            }
            span.appendChild(document.createTextNode(")"));
        }
        return span;
    }

    function on_calc_button_clicked(ev) {
        var a = ev.target;
        var divDay = a.parentNode.parentNode;
        var nodes = divDay.getElementsByTagName("a");

        var asinHash = {};
        for (var i = 0; i < nodes.length; i++) {
            var asin = get_asin_from_link(nodes[i].href);
            if (asin) {
                asinHash[asin] = 1;
            }
        }

        var n = 0;
        for (var asin in asinHash) {
            n++;
        }
        update_label(a, n);

        add_handler(function() {
            var n = 0;
            var errors = [];
            var wait = 0;
            var total_price = 0;
            for (var asin in asinHash) {
                if (typeof item_price[asin] == 'undefined') {
                    GM_log("waiting: " + asin);
                    wait++;
                } else if (item_price[asin] == null) {
                    errors.push(asin);
                } else {
                    n++;
                    total_price += item_price[asin];
                }
            }
            if (wait > 0) {
                update_label(a, wait);
                return false;
            }

            var span = create_result_node(total_price, n, errors);
            a.parentNode.appendChild(span);
            a.parentNode.removeChild(a);

            return true;
        });

        var asinList = [];
        for (var asin in asinHash) {
            if (!item_price[asin] && !req[asin]) {
                new_request(asin);
            }
        }

        ev.preventDefault();
        return;
    }

    function add_calc_button(h2) {
        var a = document.createElement("a");
        a.href = "#";
        a.appendChild(document.createTextNode("合計金額を計算"));
        a.style.fontWeight = 'normal';
        a.style.fontSize = '80%';
        a.style.float = 'right';
        a.addEventListener('click', on_calc_button_clicked, false);
        h2.appendChild(a);
    }

    var divDayHeadings = getElementsByXPath('//div[@class="hatena-body"]//div[@id="days"]/div[@class="day"]/h2');
    for (var i = 0; i < divDayHeadings.length; i++) {
        add_calc_button(divDayHeadings[i]);
    }
})()

レコードの前後のデータを取得する

http://d.hatena.ne.jp/pyxis-dev/20081001/1222791295

適切なインデックスが張ってあってかつDBMSのソート戦略が十分賢いなら、これで十分のような気がします。
(実行して試したわけではないのでどこか間違ってるかも)

    def get_neighbors(entry):
        m = DiaryModel

        older = m.query.filter(((m.pubdate==entry.pubdate) &&
                                (m.modified_at==entry.modified_at) &&
                                (m.id<entry.id)) ||
                               ((m.pubdate==entry.pubdate) &&
                                (m.modified_at<entry.modified_at)) ||
                               (m.pubdate<entry.pubdate)) \
                       .order_by(desc(m.pubdate),
                                 desc(m.modified_at),
                                 desc(m.id)) \
                       .first()

        newer = m.query.filter(((m.pubdate==entry.pubdate) &&
                                (m.modified_at==entry.modified_at) &&
                                (m.id>entry.id)) ||
                               ((m.pubdate==entry.pubdate) &&
                                (m.modified_at>entry.modified_at)) ||
                               (m.pubdate>entry.pubdate)) \
                       .order_by(m.pubdate,
                                 m.modified_at,
                                 m.id) \
                       .first()

        return [older, newer]

あるいはもっと一般化して、

    def make_filter(mapper, entry, keys, is_desc):
        import operator
        op = is_desc and operator.lt or operator.gt

        filters = []
        for i in reversed(xrange(len(keys))):
            filters.append(
                and_(*([getattr(mapper, key) == getattr(entry, key)
                        for key in keys[0:i]] +
                       [op(getattr(mapper, keys[i]), getattr(entry, keys[i]))])))
        return or_(*filters)

    def make_order_by(mapper, keys, is_desc):
        order_by = []
        for key in keys:
            order_by.append(getattr(mapper, key))
        if is_desc:
            return map(desc, order_by)
        else:
            return order_by

    def get_olders(query, mapper, entry, sort_keys):
        return query.filter(make_filter(mapper, entry, sort_keys, True)) \
                    .order_by(*make_order_by(mapper, sort_keys, True))

    def get_newers(query, mapper, entry, sort_keys):
        return query.filter(make_filter(mapper, entry, sort_keys, False)) \
                    .order_by(*make_order_by(mapper, sort_keys, False))

    def get_neighbors(entry):
        sort_keys = ['pubdate', 'modified_at', 'id']
        query = DiaryModel.query()
        return [get_olders(query, DiaryModel, entry, sort_keys).first(),
                get_newers(query, DiaryModel, entry, sort_keys).first()]

買った本とか

“本の姫”は謳う〈4〉 (C・NOVELSファンタジア) ヒメカミ覚醒。-マツロイの剣- (ファミ通文庫) 宇宙からの訪問者―テレパシー少女「蘭」事件ノート〈9〉 (講談社青い鳥文庫) テレパシー少女「蘭」(7) ゴースト館の謎 (シリウスKC) バットマン~デス マスク~ (Flex Comix) いばら姫のおやつ (ヤングキングコミックス) オクターヴ 1 (アフタヌーンKC) 逆走少女 2―終わらない夏休み (電撃コミックス) 大人の手がまだ触れない (XOコミックス) LO画集 TAKAMICHI LOVE WORKS (FLOW COMICS) ひだまり―ごとPアートワークス (電撃姫プレミアム) とらドラ ! VS (バーサス) 禁書目録 2008年 11月号 [雑誌]

ソフトウェアライセンスの基礎知識 bashクックブック 物理数学の直観的方法

Fairy Dance~KOKIA meets Ireland~

  • Apple iPod nano 8GB (シルバー) MB598JA
  • 4th iPod nano用シリコンジャケット ホワイト
  • オーディオテクニカ ネックストラップインナーイヤーヘッドホン ATH-C100NS BK
  • avenue-d Book Sleeve Air 13(ダークブラウン) TR707J/A

ASIN:B001FTEF7E レイ・アウト 4th iPod nano用シリコンジャケット ホワイト RT-N4C2/W audio-technica ネックストラップインナーイヤーヘッドホン ATH-C100NS BK Apple Store (Japan) - avenue-d Book Sleeve Air 13 (ダークブラウン)

買った本

今日は渋谷のブックファーストでSF特集(そうじゃないのもあるけど)。

穂足(ほたる)のチカラ 妙なる技の乙女たち 宇宙塵傑作選〈1〉日本SFの軌跡 宇宙塵傑作選〈2〉日本SFの軌跡 ブラッドベリはどこへゆく―未来の回廊 サイエンス・イマジネーション 科学とSFの最前線、そして未来へ S-Fマガジン 2008年 10月号 [雑誌] 小説新潮 2008年 09月号 [雑誌]

鋼殻のレギオスX  コンプレックス・デイズ (富士見ファンタジア文庫) レギオス顕現  レジェンド・オブ・レギオスIII (Style‐F) ハーフボイルド・ワンダーガール (一迅社文庫) 神様のおきにいり (MF文庫J) 神様のおきにいり〈2〉びしゃがつくの巻 (MF文庫J) イメイザーの美術  2 泥の子どもたち (ガガガ文庫) イメイザーの美術  3 砂と星のあいだに (ガガガ文庫) DRAGON MAGAZINE (ドラゴンマガジン) 2008年 11月号 [雑誌]

絶対可憐チルドレン (14) (少年サンデーコミックス) 月光条例 2 (少年サンデーコミックス) 海皇紀(37) (講談社コミックス月刊マガジン) 妖怪のお医者さん(8) (講談社コミックス) 鋼殻のレギオスMISSING MAIL2 (角川コミックス ドラゴンJr. 123-2)

[入門] Debian パッケージ スピンはめぐる―成熟期の量子力学 新版

Chromium を Mac でビルドしてみた

Google ChromeMac 版はまだ出てないけど、ソースコードが公開されているので試しにビルドしてみた。
とはいえ、公式ページにも書いてある通り、まだ実行ファイルができる以前の段階なので、ビルドしても動かせるわけでもないんだけど。

基本的には Build Instructions (Mac OS X) (Chromium Developer Documentation) の手順に従えばOK。以下は微妙にアレンジが入っている。あと、 Leopard の付属 CD から入れた Xcode では古すぎると怒られたので、 Xcode の最新版を入れる必要があった。

$ cd ~/work
$ svn co http://src.chromium.org/svn/trunk chromium
$ cd chromium
$ ./depot_tools/mac/gclient config http://src.chromium.org/svn/trunk/src
$ ./depot_tools/mac/gclient sync

gclient sync は svn up 相当の処理 + DEPS ファイルの内容に従って依存ライブラリの取得をやってくれるらしい。

$ open ./src/build/all.xcodeproj

Xcodeが立ち上がったら「ビルド」をクリックしてビルド開始。途中結果は「ビルド」メニューの「ビルド結果」で見ることができる。
ビルドが成功したら、今度はターゲットを run_all_unittests にしてビルドするとユニットテストが実行される。試したときは全テストにパスした。

ディレクトリ配下を色々物色していたら v8_shell なんてファイルを発見。早速実行してみる。

$ ./src/xcodebuild/Release/v8_shell
V8 version 0.3.1
> 

お、動いた。いくつか式を評価させてみると、

> 1 + 2
3
> var x = 1
> x
1
> (function(x){return x*x})(3)
9
> 

それなりにちゃんと動いてるっぽい。

実行できるブラウザがビルドできるようになるのがいつになるかは分からないけど、 gclient sync する度に沢山のソースコードが更新されているのを見る限り、ものすごい勢いで開発が進んでいるみたい。

買った本とか

きのうの世界 時砂の王 (ハヤカワ文庫JA) 狼と香辛料〈9〉対立の町(下) (電撃文庫) お隣の魔法使い 語らうは四季の詩 (GA文庫) under〈2〉異界イニシエイション (電撃文庫) サヴァイヴド ファイブ 2 (GA文庫) リミテッド・ヴァンパイア  (1)髪喰鬼と吸血鬼 (角川スニーカー文庫) リミテッド・ヴァンパイア    (2)髪喰鬼とおおうさぎ (角川スニーカー文庫) 時間商人 不老不死、売ります (ガガガ文庫 み 1-4) 翡翠の封印 (C・NOVELSファンタジア) ひぐらしのなく頃に解 第三話~皆殺し編~(上) (講談社BOX) エンバーミング-THE ANOTHER TALE OF FRANKENSTEIN- 1 (ジャンプコミックス) Luck Stealer 2 (ジャンプコミックス) 夢みる7月猫(ジュライキャット) (Beam comix) ハジメルド物語 (Beam comix)

  • 空の境界 未来福音 -the Garden of Sinners recalled out summer- / 竹箒
  • DEATH FATE / サークル負け組クラブ
  • DEATH FATE 2 / サークル負け組クラブ

空の境界 未来福音/©竹箒 - メロンブックス通信販売 Death Fate 2/©負け組クラブ - メロンブックス通信販売

絶対可憐チルドレン 01 [DVD]

Eric Sink on the Business of Software 革新的ソフトウェア企業の作り方 フーリエの冒険 量子力学の冒険 日経 Linux (リナックス) 2008年 10月号 [雑誌]

HANABI waltz