最近Firefoxの機能拡張で、vim風な操作でFirefoxを操ることができるvimperatorを使い始めました。

で、vimperatorのプラグインをいろいろ入れて楽しんでるのですが、Tomblooをvimperatorから使うのにtombloo.jsってのがあってそれを使ってたのですが、LDRからは使えないっぽかったのでLDRから使えるようにしてみました。(もしかしたら他に同じことできるものがあるかも。もしあったら教えてください><)

tombloo.jsはCodeReposにあるので直接変更しちゃおうかとも思ったのですがブランチとか切りづらかったので、ちょっと長いですがここに貼っておきます。

使い方はLDRでフィードを読んでいる時に「:tomblooReader」と打てば候補が出てくるので「Link\ -\ LDR」を選べばアクティブはエントリがクリップできます。ちなみに「:tomblooReader!」でやればTomblooのウィンドウも出せます。

JSは得意ではないので微妙なところがあるかも。
もしあったら修正をお願いします。

let PLUGIN_INFO =
<VimperatorPlugin>
<name>{NAME}</name>
<description>Tombloo integrate plugin</description>
<description lang="ja">Tombloo 統合プラグイン</description>
<author>Trapezoid</author>
<version>0.1e</version>
<minVersion>2.0pre</minVersion>
<maxVersion>2.0pre</maxVersion>
<updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/tombloo.js</updateURL>
<detail><![CDATA[

== EX-COMMANDS ==

:tombloo arg:
    post by Tombloo (don't use prompt)

:tombloo! arg:
    post by Tombloo (use prompt)

:tomblooAction arg:
    execute Tombloo's action in tool menu

:tomblooReader arg:
    post by Tombloo on LDR (don't use prompt)

:tomblooReader! arg:
    post by Tombloo on LDR (use prompt)

]]></detail>
<detail lang="ja"><![CDATA[
== EX-COMMANDS ==

:tombloo arg:
    Tombloo を使って投稿します ( ダイアログは出てきません )

:tombloo! arg:
    Tombloo を使って投稿します ( ダイアログが出てきます )

:tomblooAction arg:
    ツールバーから選択できる Tombloo のメニューを実行します

:tomblooReader arg:
    LDR で Tombloo を使って投稿します ( ダイアログは出てきません )

:tomblooReader! arg:
    LDR で Tombloo を使って投稿します ( ダイアログが出てきます )

]]></detail>
</VimperatorPlugin>;

(function () {

// ts: "T"ombloo "S"ervice
let tomblooService;
try { tomblooService = getTombloo(); }
catch (e) {
    liberator.log(e.message, 0);
    return;
}

with (tomblooService) {

commands.addUserCommand(
    ['tomblooAction'],
    'Execute Tombloo actions',
    function (arg) {
        let f = Tombloo.Service.actions[arg.string];
        (f instanceof Function)
            ? f.execute()
            : liberator.echoerr(arg.string + ' is not Tombloo Action.');
    },
    {
        completer: function (context) {
            context.title = ['Tombloo Actions'];

            let names = Tombloo.Service.actions.names;
            let candidates = [[n, n] for([, n] in Iterator(names))];
            context.completions = candidates.filter(
                function ($_) this.test($_[0]),
                new RegExp(context.filter, 'i')
            );
        },
    }
);

commands.addUserCommand(
    ['tombloo'],
    'Post by Tombloo',
    function (args, special) {
        //let f = Tombloo.Service.extractors[args.string];
        let arg = args.string.replace(/\\(?=\u0020)/g, '');
        liberator.log(args.string, 0);
        liberator.log(arg, 0);

        let f = Tombloo.Service.extractors[arg];
        (typeof f === 'object')
            ? Tombloo.Service.share(getContext(), f, special)
            : liberator.echoerr(args.string + ' is not Tombloo command');
    },
    {
        bang: true,
        completer: function (context) {
            context.title = ['Tombloo'];

            let extensions = Tombloo.Service.check(getContext());
            let candidates = [[e.name, e.name] for ([, e] in Iterator(extensions))];
            context.completions = candidates.filter(
                function($_) this.test($_[0]),
                new RegExp(context.filter, 'i')
            );
        }
    }
);

commands.addUserCommand(
    ['tomblooReader'],
    'Post by Tombloo on LDR',
    function (args, special) {
        let win = content.window.wrappedJSObject;
        let item;
        try {
            item = win.get_active_item(true);
        } catch(e) {
            liberator.echoerr('Use only on LDR');
            return;
        }
        let target = item.element;
        let ctx = getContextOnLDR(target);
        let arg = args.string.replace(/\\(?=\u0020)/g, '');
        liberator.log(args.string, 0);
        liberator.log(arg, 0);

        let f = Tombloo.Service.extractors[arg];
        (typeof f === 'object')
            ? Tombloo.Service.share(ctx, f, special)
            : liberator.echoerr(args.string + ' is not Tombloo command');
    },
    {
        bang: true,
        completer: function (context) {
            context.title = ['TomblooReader'];

            let win = content.window.wrappedJSObject;
            try {
                item = win.get_active_item(true);
            } catch(e) {
                liberator.echoerr('Use only on LDR');
                return;
            }
            let target = item.element;
            let ctx = getContextOnLDR(target);
            let extensions = Tombloo.Service.check(ctx);
            let candidates = [[e.name, e.name] for ([, e] in Iterator(extensions))];
            context.completions = candidates.filter(
                function($_) this.test($_[0]),
                new RegExp(context.filter, 'i')
            );
        }
    }
);

} // with (tomblooService)

// helper ---
function getTombloo() {
    const serviceId = '@brasil.to/tombloo-service;1';

    if (!Cc[serviceId])
        throw new Error('Tombloo is not found. install from http://github.com/to/tombloo/wikis');

    return Cc[serviceId].getService().wrappedJSObject;
}

function getContext() {
    const doc = content.window.document;
    const win = content.window.wrappedJSObject;
    return implant(
        implant(
            {
                document:   doc,
                window:     win,
                title:      doc.title.toString() || '',
                selection:  win.getSelection().toString(),
                target:     doc,
                //event     : event,
                //mouse     : mouse,
                //menu      : gContextMenu,
            },
            {}
        ),
        win.location
    );
}

function getContextOnLDR(target) {
    const doc = content.window.document;
    const win = content.window.wrappedJSObject;
    return implant(
        implant(
            {
                document  : doc,
                window    : win,
                title     : null,
                selection : win.getSelection().toString(),
                target    : target,
            },
            {}
        ),
        win.location
    );
}

// stuff ---
function implant(dst, src, keys){
    if (keys) {
        keys.forEach(function(key) { dst[key] = src[key]; });
    }
    else {
        for (let key in src) dst[key] = src[key];
    }

    return dst;
}

})();

// vim:sw=4 ts=4 et:
ちょっと前にFirefoxがSQL OptimizerでREINDEXして速くなったと書いたのですが、そういやApple Mailもsqliteだったのを思い出しました。

以前Apple Mailも速度アップのためにVACUUMをしたことはあったのですが、REINDEXはしたことなかったのでさっそく試してみたら、なんか心なしかメールのリスト表示が速くなってる気がします。

まぁ、ある程度のメール量がないと意味ないかもしれませんが。。。

やり方は

% cd ~/Library/Mail/
% sqlite3 Envelope\ Index
sqlite> REINDEX subjects;

こんな感じでVACUUMするときとほぼ同じ。

自分はこれ以外にmessagesとaddressesもREINDEXしてみました。

まぁ気休めぐらいにしかならないかもしれませんが、Apple Mailが遅くてかなわんというひとはやってみてはいかがでしょうか。
KiokuDBがちょっと前から気になってて、そしたらたまたまid:tokuhiromがブログで使い方などを書いてたので、それを見ながら試してみようと思いインストールしていたら、requiredに入ってるProc::InvokeEditorというモジュールが目につきました。なにをするモジュールだろと思って調べてみたら便利そうだったので紹介。

これ、なにをするモジュールかと言うとよくcvsとかsvnとかgitとかでコミットする際に「-m」でコメント指定しないとエディタが開いて編集させたりすると思うんですが、その挙動を簡単に記述できるようになるモジュールです。

use Proc::InvokeEditor;

my $unedited_text = '元々のテキスト';
my $edited_text = Proc::InvokeEditor->edit($unedited_text);
warn $edited_text; # 編集後のテキスト

こんな感じで、ものすごく簡単にあれと同じ挙動をさせられます。

あと使うエディタは自分で指定することもできるし、環境変数から拾ったりもできるようです。

デフォルトは下のような順番になってますね。
$ENV{'VISUAL'}, $ENV{'EDITOR'}, '/usr/bin/vi', '/bin/vi', '/bin/ed'

コマンドラインで動かすアプリがあったら今度使ってみようと思った。
WEB+DB PRESS Vol.50のGit特集はものすごくいい。

自分はこないだまでGitを真面目に使ってなくて、そろそろ本格的にいじりたいなと思っていたところでこの特集だったのでとりあえずGitの導入としていいかなと思って見てたのですが、思いのほか詳しく書かれていてものすごく役に立ちました。

下手な本より詳しく書かれているので、これ読めばGitの基本的な使い方はマスターできるんじゃないかと思います。

Gitの開発者である Junio C Hamanoさんが書いてることもあってとてもわかりやすく、無駄がない構成になってます。

Gitに興味のある人は必読ですね。

WEB+DB PRESS Vol.50
WEB+DB PRESS Vol.50
posted with amazlet at 09.05.15
WEB+DB PRESS編集部
技術評論社
売り上げランキング: 174


Firefoxってしばらく使ってるとどんどん起動が遅くなる。

入れてるExtensionの多さも関係してるのかもしれないけど、とにかく遅くなる。

その起動の遅さゆえ最近は起動が速いSafariを使っていたんだけど、そんなときこの記事を見付けました。

http://cheebow.info/chemt/archives/2009/05/firefox3.html
http://d.hatena.ne.jp/tanemori/20090514/SQLiteOptimizer

ここに紹介されているSQLite OptimizerというExtensionをFirefoxに入れて、REINDEXをするとあら不思議、Firefoxの起動が爆速になります。

これはなにやってるかというとFirefoxの履歴とかのデータが入ってるファイルの中身のインデックスを再構築してるんですが、それだけでこんだけ早くなるんだとちょっと驚いた。

さっき会社の開発ミーティングでその話になって、まだやってない人にその場でやってみてもらったら、みんなびびってました。

とりあえず遅いなぁという人は試してみる価値ありです。

Yokohama.pm #4

Yokohama.pm #4に行ってきました。


会場は前回と同様。あの会場は駅近でとてもいいのですが開始時間を考えると、やはり東京組には距離的に大変ですね。ZIGOROuさんも書いてますが、次回からはもうちょっと時間を後ろにして参加しやすい時間にした方がいいかなぁと思いました。


今回はlopnorさんという映像担当がいて、かつikasam_aさんが分配器的なものを持ってきてくれたため前より質の高いustもができたのがとてもよかったです。ありがとうございました!


それで肝心のトークについてですが、今回は「オレオレ○○」の会と言われるように自分プロダクトの紹介が多めだったのですが、これはちょっと使ってみようかなというのがあり見ていて楽しかったです。あとやっぱりyoheiさんのCAPは普段実装ベースのトークが多い中、あぁいったものが聞ける機会が少ないためとても面白かったです。


あと今回嬉しかったのは懇親会の歳にcraftworksさんとnekoyaさんが話しに来てくれて「Yokohama.pmはとてもいい雰囲気ですねー」と言われました。これからもそう感じてもらえるようにいろいろと検討していきたいと思ってるので、いろいろとご意見を頂けたらと思います。


最後に司会のZIGOROuさん、受け付けのtomyheroさん、懇親会担当のkdaibaさん、タイムキーパーのoverlastさん、ありがとうございました。


そういえば懇親会でdannさんのDI講座があってとてもためになりました。dannさんありがとうございました。

antipopさんのApp::SocialSKKをさっそくインストールして使ってみた。
これは素晴しすぎる!

使えるまでを軽く説明しておく(Macの場合)。

まずインストール。

# cpan App::SocialSKK
この際 socialskk.pl も同時にインストールされます。
次に $HOME/.socialskk に

plugins:
  - name: SocialIME
  - name: HatenaBookmark
  - name: Wikipedia
こんな感じに書く。
あとはsocialskk.plを起動するだけでApp::SocialSKK使える状態になります。

AquaSKK側での設定は「環境設定」の「辞書」タブで「+」を押して辞書を追加し、辞書の種類を「外部 skkserv 辞書」にし、場所を「localhost:1179」にするだけです。
(ちなみにいままでMacではMacUIMを使っていたのですが、現状のMacUIMだとskkservとskkの辞書を同時に設定できないっぽいので2年ぶりぐらいにAquaSKKに乗り換えました。そしたらAquaSKKがめっちゃ進化していてびびった)

とりあえず「ほってんとり」って書いて変換したら

料理のススメ:これから料理をしようと思っているひとへ - Money does not hurt your heart
ってでた。はてブからうまく取れてるようですね。
まぁ、これはネタとして。

その他にもSocialIMEには郵便番号とかも登録されてるっぽくて、日本語入力モードで「/0000000」(0000000はちゃんとした郵便番号ね)と打って変換を押すとその郵便番号の住所が出てきました(昔は郵便番号データベースをファイルに落したりしてたのを思い出した)。

っていうか実はSocialIMEに郵便番号が登録されてること知らなくてApp::SocialSKK::Plugin::ZipCodeとか作っちまった。悔しい...。

悔しいからソースを公開しておく。

package App::SocialSKK::Plugin::ZipCode;

use strict;
use warnings;
use base qw( App::SocialSKK::Plugin );
use XML::Simple ();
use URI;
use Encode ();

sub get_candidates {
    my ($self, $text) = @_;
    return if !defined $text || $text !~ /^\d{7}$/;

    my $uri = URI->new( 'http://zip.cgis.biz/xml/zip.php' );
    $uri->query_form( zn => $text );
    my $res = $self->ua->get( $uri->as_string );
    if ($res->is_success) {
        eval {
            my $xml = XML::Simple::XMLin(
                $res->content, ValueAttr => [ qw(state_kana city_kana address_kana company_kana
                                                 state city address company) ]
            );
            my @candidates;
            for my $seq ( [ 4..7 ], [ 0..3 ] ) {
                my $val = join( '', map {
                    my $v = $xml->{ ADDRESS_value }->{ value }->[ $_ ];
                    $v ne 'none' ? $v : '';
                } @$seq );
                push @candidates, Encode::encode(
                    'euc-jp',
                    Encode::is_utf8( $val ) ? $val : Encode::decode( 'utf8', $val ),
                );
            }
            return @candidates;
        };
    }
}

1;

__END__

=head1 NAME

App::SocialSKK::Plugin::ZipCode - Retrieves Candidates from Zip Code API

=head1 SYNOPSIS

  # Add a line like below into your .socialskk:
  plugins:
    - name: ZipCode

=head1 DESCRIPTION

App::SocialSKK::Plugin::ZipCode performs retrieval of
candidates from Zip Code API

=head1 SEE ALSO

=over 4

=item * Zip Code API

http://zip.cgis.biz/

=back

=head1 AUTHOR

Yoshiki Kurihara E<lt>kurihara __at__ cpan.orgE<gt>

あ、本題を忘れそうになってしまった。
socialskk.plをログインしたら毎回起動するのはめんどうなのでlaunchdで起動するようにしといた。これはMac OS X 10.5.6でしか動作確認してませんのであしからず。

設定は簡単。$HOME/Library/LaunchAgents/socialskk.plist ってファイルを作って下記の内容にで保存したあとMacを再起動すると socialskk.pl が自動で起動しているはず。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>socialskk</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/local/bin/socialskk.pl</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardErrorPath</key>
        <string>/dev/null</string>
        <key>StandardOutPath</key>
        <string>/dev/null</string>
</dict>
</plist>


MySQL5系だとvarchar型の長さが0〜65535バイトの間で設定できるようになってるんだぜ。1ヶ月前ぐらいに知ってばびった。

MySQL4系だと0〜255バイトまでしか設定できないんだけどね。

これでちょっとしたテキストはvarchar型でよさげだ。

ただしchar型は固定長だから、MySQL5系でも0〜255バイトの間でしか設定できないんです。

sbinのsってなんだろうと気になったので調べてみた。

結論から言うと下記のようにSystem BINaryのことでした。
Wikipediaのこの辺にも書いてありますね。

http://momox.net/blog/2007/02/bin_sbin_lib_libexec.html

<ディレクトリ名の意味>
bin -- binary 実行可能なプログラムのこと。
sbin -- system binary システム管理者が主に使用するプログラム。
lib   -- ライブラリ--同じ階層のbinとかsbinのプログラムが使う関数(プログラムの断片)
libexe -- プログラムが使うプログラム


よくサービスとかパッケージのディレクトリ内でbinとsbinって作って使い分けてたりするけど自分の中でいまいちその棲み分けができていない。

cronとかに設定するのはsbin?
コマンドラインで実行するのはbin?
とかばっくり自分基準があるけど、この基準も合ってるか怪しい。

こんな基準のやってるよ!ってのがあったら教えて欲しいです。

あとlibexecとかもあるけど、これもなにを置くか微妙で「プログラムが使うプログラム」はこの中みたいですね。ぱっと思いつくので言うとmysqldとかがlibexecに置いてあったりしますね。

emacs lispを書いててふと思ったんだけど、Perl moduleのmake testみたいなことをやる機構ってあるのかな。

絶対あると思うから
あとで探してみよう。

検索

広告

最近のコメント

アイテム

  • Yokohama.pm #4
  • F-03Aで撮った写真
  • F-03A
  • iMT
  • シュウォッチ
  • シュウォッチのパッケージ
  • remedieの動画再生画面。
  • remedieの動画リスト画面
  • remedieの起動画面
  • Gmailのターミナルというテーマ
OpenID対応しています OpenIDについて
Powered by Movable Type 4.22-ja