JAMADAM.COM

RSS Subscribe to my RSS feed

Stripe Calendar

Sep, 2010
Aug 1516171819202122232425262728293031 Sep 123456789

Entry: Perl - クラスの定数引数はインスタンスメソッドで指定する

Perl - クラスの定数引数はインスタンスメソッドで指定する

Initial post: 2010.05.21 | Last modified: 2010.05.21

Perlでやたら定数風関数を使うようにしてみた。最近この方針を徹底しているけど、後で後悔しなきゃいいが。

use strict;
use warnings;
use Switch;

$a = MyClass1->new;
$a->crud($a->MODE_CREATE);
$a->crud($a->MODE_READ);
$a->crud($a->MODE_UPDATE);
$a->crud($a->MODE_DELETE);

package MyClass1;
use strict;
use warnings;
use Switch;

    sub MODE_CREATE() {1}
    sub MODE_READ()   {2}
    sub MODE_UPDATE() {3}
    sub MODE_DELETE() {4}
    
    sub new {
        
        return bless {}, shift;
    }

    sub crud {
        
        my ($self, $mode) = @_;
        switch ($mode) {
            case MODE_CREATE {print "create\n"}
            case MODE_READ   {print "read\n"}
            case MODE_UPDATE {print "update\n"}
            case MODE_DELETE {print "delete\n"}
        }
    }

名前つき引数としてのハッシュ引数は可読性が高いんだけど、名前を文字列で入力するとミスしやすい。なので下記のようにする。

use strict;
use warnings;

my $ins = MyClass2->new();
print $ins->some_method(
    $ins->ARG_NAME => 'saito', 
    $ins->ARG_ADDR => 'sapporo'
);
print "\n";

package MyClass2;
use strict;
use warnings;
    
    sub ARG_NAME() {1}
    sub ARG_ADDR() {2}
    
    sub new {
        
        return bless {}, shift;
    }
    
    sub some_method {
        
        my ($self, %hash) = @_;
        return $hash{ARG_NAME()}. ' lives in '. $hash{ARG_ADDR()};
    }

定数風関数はメソッド呼び出しできるので

  •  アロー演算子により、クラスが期待する値だけがIDEの補完候補にあがる(クラスを取り違える心配がない)。
  •  パッケージ修飾が不要(本当の定数だと$Path::To::Module::ARG_NAMEとか長い)。
  •  継承もできる(ただし主要なIDEでは継承先まで補完できない)。

 Perl Best Practiceが届いた。けど、まだ読めてない。

 

Entry: ついったらータグs API(仮)

ついったらータグs API(仮)

Initial post: 2009.11.03 | Last modified: 2009.11.05

「ついったらータグs API(仮)」は、ついったらータグs(仮)のデータベースへの問い合わせをアプリケーションから利用するためのインターフェースを提供します。ついったらータグs(仮)のデータベースにはTwitterの各ユーザーページに対する、はてなブックマークタグの情報を格納しています。本APIを利用することで、任意のユーザーに対するタグ付けの情報はもちろんのこと、任意のタグがどのユーザーに適用されているかという情報を取得することができます。また、対象となるデータベースとして、独自のルールでタグ文字列の正規化を行ったテーブルを対象にすることもできます(任意)。

 全てのAPIは下記のURLで提供されます。

http://jamadam.com/th/

 現在サポートされている操作は、タグ-ユーザー検索(パラメータt=api/tag.json)とユーザー-タグ検索(パラメータt=api/user.json)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできます。

 

タグ-ユーザー検索

任意のタグ文字列を指定することで、このタグがつけられたユーザーのスクリーンネームのリストを得ます。

パラメータ

  • t (必須)
    "api/tag.json"を指定します。
  • str (必須)
    任意のタグを指定します。
  • nn (オプション)
    正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。

 リクエスト例

/th/?t=api/tag.json&str=it

レスポンス例

{"result":[
{"user":"takapon_jp","occurance":"2"},
{"user":"m_kumagai","occurance":"1"},
{"user":"huehara88","occurance":"1"}
]}

 ※実際はレスポンスに改行は含まれません。

 

ユーザー-タグ検索

任意のスクリーンネームを指定することで、このユーザーに付与されたタグの一覧を得ます。

パラメータ

  • t (必須)
    "api/user.json"を指定します。
  • str (必須)
    任意のスクリーンネームを指定します。
  • nn (オプション)
    正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。

リクエスト例

/th/?t=api/user.json&str=jamadam

レスポンス例

{"result":[
{"tag":"twitter","occurance":"38"},
{"tag":"音楽","occurance":"9"},
{"tag":"有名人","occurance":"4"},
{"tag":"webサービス","occurance":"4"}
]}

※実際はレスポンスに改行は含まれません。

 

実装例

ユーザー-タグ検索を利用してタグ一覧を取得するPerlコードは以下のようになります。

use LWP::UserAgent;
use JSON::XS;

my $tags = _twitterer_tags('jamadam');
foreach my $entry (@$tags) {
    print($entry->{tag});
    print(":");
    print($entry->{occurance}."回");
    print("¥n");
}

sub _twitterer_tags {
    my $user = shift;
    my $ua = LWP::UserAgent->new;
    my $res = $ua->get('http://jamadam.com/th/?t=api/user.json&str='. $user);
    my $obj = decode_json $res->content();
    return $obj->{result};
}

 

Entry: ある意味、世界一のURL短縮サービスを作った

ある意味、世界一のURL短縮サービスを作った

Initial post: 2009.07.18 | Last modified: 2009.11.04

[2009.09.30] Asiajinで取り上げていただきました。ありがとうございます。そして、同じネタがすでにあったと知りました。リサーチ不足。

 

長いURLの文字数を削減してくれるURL短縮サービスはたくさんあるけど、世界中のどの方式よりも文字数を短縮できる方法を発明しました。

縮.jp

サイト名ですでにネタばれですが、これは各URLに割り振るIDをマルチバイト文字の組み合わせにすることで、文字数的には短いでしょ、というネタです。ついったーなんかは完全に文字数に依存なので有効かも。

IDに使用する文字は日本の常用漢字1945字です。シーケンシャルに割り振り、使い切ったら繰り上がるので、最初の370万件は2桁に収まり、最初の73億件も3桁に収まります。

注意事項としては

  • Firefox3.5, IE7, Chrome, iPhoneで正常動作を確認しました。逆に、Androidエミュレータ, DoCoMoやAUのブラウザ(フルブラウザ含む)は日本語ドメインに未対応のようです。
  • IDはシーケンシャルに割り振られるので使用済みのIDは容易に推測できます。
  • 「悪」などというイヤなIDが当たってしまったらリロードして再割当してください。「縮AGAIN」ボタンを押してください。
  • これはジョークサービスです。本サービスの利用に起因または関連する利用者の損害について管理者は何ら責任を負いません。

 

Entry: 国際化ドメイン名をHTMLに記述する際の記述方法

国際化ドメイン名をHTMLに記述する際の記述方法

Initial post: 2009.10.18 | Last modified: 2009.10.18

国際化ドメイン名をHTMLに記述する際の記述方法に関するメモ。間違いあったら指摘して。

(1) <a href="http://%E7%B8%AE.jp/">http://縮.jp/</a> (誤り)
(2) <a href="http://縮.jp/">http://縮.jp/</a>
(3) <a href="http://&#x7e2e;.jp/">http://縮.jp/</a>
(4) <a href="http://xn--jj0a.jp/">http://縮.jp/</a>

(1)パーセントエンコード。誤り。

(2)マルチバイト文字を直接記述する。一番正しくて手軽な記述。問題点は、UAがIDNに対応していないため通信時に正しくpunycode変換されないかもしれないこと。

(3)マルチバイト文字を数値文字参照で記述する。敢えてこう記述するメリットはあまりないけど、例えばマルチバイト文字を扱えないシステムとの相互運用を想定しているためにHTML内に日本語を直接記述したくない場合には、このように記述して構わない。

(4)punycode変換して記述する。リンクを正しく動作させるという点では一番安全かもしれない記述。ただし、エンドユーザーが知りうるURLが全てデコードされるかどうかはブラウザの実装次第。例えば「リンクをコピー」したときにpunycodeのまま出力されてしまってはIDNの利点が台無し。punycode変換はUAが通信時に行うことが推奨されているため、HTMLに記述する時点で行う必要はない。

Entry: 縮.jpに待望の「縮AGAIN」ボタンを搭載

縮.jpに待望の「縮AGAIN」ボタンを搭載

Initial post: 2009.10.08 | Last modified: 2009.10.16

縮.jp。7月に作って放置してたら10月になって話題になった。人生で一番集客した。そんな訳で、ネタを必要以上に膨らませるべく、システムを改修しました。見た目的にはあまり変化ありませんが、中身はほぼ丸々変わってます。特筆すべき変化は。。

待望の「縮AGAIN」ボタンを搭載

生成された短縮URLが気に入らなかった場合、このボタンを押すと別のURLを生成してくれます。なお、縮AGAINした場合、古いURLはしばらくして解放されます。

既設の短縮URLがあれば、短い順にご提案

今までは湯水のように新規作成してましたが、縮ボタンを押した際、まずは既設の短縮URLを提示するようになりました。縮AGEINすると短い順に既設URLが表示され、なくなれば新規作成されます。

転送時、末尾のゴミを可能な限り取り除く

Twitterでhttp://縮.jp/上これすげーみたいなつぶやきが多発していたので、こういう場合はhttp://縮.jp/上を検知して転送します。Perlでいうと、

$id =~ s/¥P{Han}.*//; 

となっています。漢字を表す正規表現なんて初めて知りました。

ちなみに、ファイルベースからPostgresqlに移行

パフォーマンスいいかなと思って、1件1ファイルなどというデータ管理をしてたんですが、IDがシーケンシャルに固定されるとか、逆引きできないとか、色々問題あったのでPostgresqlにしました。なお、ファイルベースのキャッシュの仕組みを導入したので転送時のパフォーマンスは以前と変わらない。はず。

APIにも変更あり

offsetというパラメータが新設されました。これは前述の既設URLの再利用と関連するもので、任意の既設URLを取り出すためのパラメータです。offsetを十分に大きくすると自動的に新規作成されます。通常は指定しないでください。

 

既知の問題

  • 元URLがbit.ly等ですでに短縮済みだった場合、iPhoneで転送されない。確かソフトバンクの仕様でiPhoneで多段リダイレクトできない。
  • Twitter用のいくつかのGreaseMonkeyスクリプトやアドオンで縮.jpへ飛べない。FirefoxのJavascriptのバグっぽい挙動が原因と思われる。対処法はこちら
  • Twitter周辺サービスで日本語ドメインが誤ってパーセントエンコードされているケースも見かけます。サーバサイドでの処理に問題があるのではないか(憶測)。
  • iPhoneの多くのアプリで開けない。おそらくアプリからwebkitだかのAPI的なものに渡すURLをpunycode変換してないのではないか(憶測)。対処法はこちらの12月15日の記事くらいしか見当たらなかった。

 

今後の予定

  1. 任意のIDを指定可能に
  2. 不人気文字を避ける仕組み
  3. 元URLがすでに短縮URLだった場合に展開してから短縮
  4. スパム対策

 

Entry: 開発小ネタその2(改)

開発小ネタその2(改)

Initial post: 2009.10.14 | Last modified: 2009.10.14

開発小ネタ集その2のネタ、なんか違和感あると思ったらゼロがなかったんだ。という訳で書き直した。66進数。縮.jpは差し詰め、1945進数か。

use strict;
use warnings;

### 文字リスト
our @ascii = qw(
    0 1 2 3 4 5 6 7 8 9
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
    _ + - =
);

my $num1 = str2num("0");     ### 0
my $num2 = str2num("=");     ### 65
my $num3 = str2num("10");    ### 66
my $num4 = str2num("A359");  ### 10363263
my $str = num2str(10363263); ### A359

### --------------
### 文字列を数値に変換
### --------------
sub str2num {

    my $num = 0;
    my @id_array = split(//, $_[0]);
    @id_array = reverse(@id_array);

    for (my $i = 0; $i < scalar @id_array; $i++) {
        for (my $j = 0; $j < scalar @ascii; $j++) {
            if ($ascii[$j] eq $id_array[$i]) {
                $num += (scalar @ascii ** $i) * $j;
                last;
            }
            if ($j == scalar @ascii - 1) {
                return;
            }
        }
    }

    return $num;
}

### --------------
### 数値を文字列に変換
### --------------
sub num2str {

    my $sho     = int($_[0] / scalar @ascii);
    my $upper   = ($sho > 0) ? &num2str($sho) : "";
    my $current = $ascii[($_[0] % scalar @ascii)];
    return $upper. $current;
}

Entry: 開発小ネタ集その2 - 任意の文字で数値を表現したりその逆だったり

開発小ネタ集その2 - 任意の文字で数値を表現したりその逆だったり

Initial post: 2009.10.10 | Last modified: 2009.10.10

開発小ネタ集その2。任意の文字の組み合わせで数値を表現したり、数値から文字に戻したりするPerlスクリプト。暗号化ではありませんので注意。str2numはもっといい方法ないかなあ。

use strict;
use warnings;

### 文字リスト
our @ascii = qw(
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
    1 2 3 4 5 6 7 8 9 0 _ + - =
);

my $num1 = str2num('a'); ### 1
my $num2 = str2num('='); ### 66
my $num3 = str2num('aa'); ### 67
my $num4 = str2num('aabcae'); ### 1271895443
my $str = num2str($num4);     ### aabcae

### --------------
### 文字列を数値に変換
### --------------
sub str2num {

    my $hash  = shift;
    my $num = 0;

    my @id_array = split(//, $hash);
    @id_array = reverse(@id_array);

    for (my $i = 0; $i < scalar @id_array; $i++) {
        for (my $j = 0; $j < scalar @ascii; $j++) {
            if ($ascii[$j] eq $id_array[$i]) {
                $num += (scalar @ascii ** $i) * ($j + 1);
                last;
            }
            if ($j == scalar @ascii - 1) {
                return;
            }
        }
    }

    return $num;
}

### --------------
### 数値を文字列に変換
### --------------
sub num2str {

    my $idx    = $_[0] - 1;
    my $result = '';
    my $sho    = int($idx / scalar @ascii);

    if ($sho > 0) {
        $result .= &num2str($sho);
    }

    return $result. $ascii[($idx % scalar @ascii)];
}

Entry: 縮 REST API

縮 REST API

Initial post: 2009.07.25 | Last modified: 2009.10.10

「縮 REST API」は、URL短縮/転送サービス「縮.jp」の機能を、アプリケーションから利用するためのインターフェースを提供します。

全てのAPIは下記のURL以下で提供されます。

http://api.xn--jj0a.jp/

現在サポートされている操作は、短縮URLの生成(generate.jsonへのPOST)と短縮URLの展開(lookup.jsonへのGET)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできるかもしれません。

 

GENERATE 

任意のURLを渡すことで、新規に短縮URLを生成します。

パラメータ

  • fUrl(必須)
    短縮対象のURL。http://かhttps://で始まる必要があります。
  • customId(オプション)
    任意の漢字の組み合わせで短縮URLの識別子を指定します。utf8でパーセントエンコードしてください。
  • offset(オプション)
    fUrlで指定したURLに対する既設の短縮URLがひとつ以上存在する場合、このパラメータに0以上の数値を指定することで任意の候補を選択することができます。既設URLは文字数の短い順に並んでいます。offsetが既設URLの数より大きいとき、新しいURLが生成されます。このパラメータは特殊な目的がない場合は指定しないでください。

 

リクエスト例

POST /generate.json

fUrl=http://example.com&id=%E4%B8%83

レスポンス例

{"result":{"fUrl":"http://example.com/","generated":"http://縮.jp/七"}}

 

LOOKUP

既存の短縮URLの識別子を受け取り、元のURLを返します。識別子はUTF-8でパーセントエンコードしてください。

パラメータ

  • id(必須)
    URLの識別子を指定します。
  • hash
    後方互換性のための、idの別名です。(廃止の予定)

リクエスト

GET /lookup.json?id=%E4%B8%83

レスポンス

{"result":{"fUrl":"http://example.com"}}

 

共通

HTTPレスポンスコード

正常/エラーに関わらず「200 OK」を返します。

エラー時のレスポンスボディ

下記の書式でJSONデータを返します。

{"error":"[エラーコード] [説明]"}

 

 エラーコード一覧

  • 400: generateの際、fUrlの書式が正しくない、customIdに不正な文字が含まれる、など。
  • 409: generateの際、customIdがすでに使用されている、など。
  • 404: lookupの際、指定のidが存在しない、など。

 

実装例

generate.jsonを利用して短縮URLを取得するPerlコードは以下のようになります。

use LWP::UserAgent;
use JSON::XS;

my $generated = _shuku('http://example.com');

sub _shuku {
    my $ua = LWP::UserAgent->new;
    my $res = $ua->post('http://api.xn--jj0a.jp/generate.json', {'fUrl' => $_[0]});
    my $obj = decode_json $res->content();
    my $decoded = $obj->{result}->{generated};
    utf8::decode($decoded);
    
    return $decoded;
}

 

Entry: Javascriptオフ対策、その2

Javascriptオフ対策、その2

Initial post: 2009.06.12 | Last modified: 2009.06.12

検索とタグクラウドもノースクリプトで利用できるようにした。ページナビゲーションはまだない。

JS用に用意した部品のDOMをNoscriptな人に見せないために、最初は<script></script>内に書けばいいのかと思ったら失敗。<script>タグは<noscript>タグのnotでは全然なかったのだ。考えたこともなかったけど、<script>タグ内にはスクリプトしか書いちゃいけない、とのこと。今さらながら。

スクリプト用のDOMはスクリプトで書くしかないのかと思い、document.writeの列挙が頭をよぎったものの、メンテナンス性に配慮してjQueryのappendだのprependだののDOM操作で何とかする。それでも、文字のエスケープの問題もあるし、本来HTMLで書くべきものをJavascriptで書き出すのはやはり手間がかかる。でもまあいいか。

と思っていたのが昨日の話で、今日、あっけない結論に達する。まあ、ここに書くまでもないことかも。

<div class="dom_for_script" style="display:none"></div>
<script type="text/javascript">
//<![CDATA[
$(".dom_for_script").show();
//]]>
</script>

Entry: ブックマーク