Hatena::Groupsubtech

NaN days

ブログを移転しています。最新の記事は motemen.hatenablog.com へどうぞ

2012-11-28

最近変更されたブランチを列挙する

| 19:25 | 最近変更されたブランチを列挙する - NaN days を含むブックマーク はてなブックマーク - 最近変更されたブランチを列挙する - NaN days

週明けになるとアレ?先週なにしてたっけという気分になるので git-recent-branches というスクリプトを書いて、ここ数日間で変更のあったブランチを一覧しています。

git-rev-parse でコマンドラインオプションを解析してみたかったので、シェルスクリプトになってます。

こんな感じに、7 日以内に更新されたブランチとその最新コミットを一覧できる (node のリポジトリを見てみた):

% git recent-branches --no-merged --days=7 --date=iso     
2012-11-27 18:21:05 -0800 remotes/origin/streams2-net-perf-wip - streams2: Set 'readable' flag on Readable streams [isaacs]
2012-11-27 23:52:49 +0900 remotes/origin/v0.8 - doc: Fix missing link target to 'https.request()' [Ryunosuke SATO]
2012-10-25 13:49:32 -0700 remotes/origin/v0.8.15-release - 2012.11.26, Version 0.8.15 (Stable) [isaacs]

ちなみに一番下の日付が 10-25 なのは、その前のコミットが 11 月だったので一覧に出てきたみたいです。

オプションは以下の通り。実装は単に git-log にいろいろフォーマットを与えているだけです。

  • --no-merged を指定すると現在のブランチにマージ済みのものは表示されない
  • --days=n で何日以内のブランチを見るかを指定
  • --date=format は日付のフォーマット

2012-10-16

みんなで一緒にウェブ上の音楽を聞ける polka というウェブアプリケーションを作りました

| 13:49 | みんなで一緒にウェブ上の音楽を聞ける polka というウェブアプリケーションを作りました - NaN days を含むブックマーク はてなブックマーク - みんなで一緒にウェブ上の音楽を聞ける polka というウェブアプリケーションを作りました - NaN days

Teto というのを作って、これはニコニコ動画の音声をストリーミングして配信する、というようなことをしていたのですが、いかんせん仕組みが複雑で簡単に使える感じじゃないな〜という問題があり、また、オフィスのみんなでひとつのキューを共有しつつ同時に同じ音楽を聞けたらいいな、という要求を、音声の読み込みや再生処理をすべてクライアントサイドで行うことにして実装したのが polka です。

これからは node.js だよねーノードジェスジェス!と思って node.js で何か書いてみようと思って作りはじめたのが最初でした。

https://github.com/motemen/polka

概要

polka はローカルで起動するウェブアプリケーションです。再生キューをひとつだけ持ち、接続してきたブラウザで同時に同じ音楽を聴くことができます。

実装は node.js + socket.io くらいの感じです。

使い方

2012-10-16 18:10 追記: node v0.8 くらいが必要みたいです!

いい感じにパッケージ化する方法がまだわかってないので github から clone して下さい。

% git clone git://github.com/motemen/polka.git
% npm install
% PORT=3000 npm start

で http://{IPアドレス}:3000/ に polka のサーバが立つので、あとはここにアクセスして貰えばよいです。

  • /queue で URL もしくは検索によってトラックを追加し、
  • /play をブラウザで開きっぱなしにしておくことで自動で再生が行われます。

仕組み

音声の再生には YouTubeSoundCloud の埋め込みプレーヤーを利用していて、polka 自体はキューと再生タイミングの管理だけを行っています。polka のページをブラウザで開くと自動で次々とプレーヤーが埋め込まれてゆき、再生されます。

ブラウザの中でも /play ページに最初に接続したものが master と呼ばれ、この人がほかの人 (echo) の再生のタイミングを管理します。master のブラウザでプレーヤーの再生が終了したタイミングで、他のブラウザも一斉に次のトラックへと進みます。

なので master になってる人が埋め込みに失敗したり (YouTube にはリファラを切ってると埋め込めない動画がある) 途中でポーズしたりすると次に進めなくて困るような場合がありますが、その時は /admin にアクセスして next ボタンをクリックすると強制的に次のトラックへと進みます。

対応しているソース

いまのところ埋め込めるのは以下の 2 サービスのみです。

ニコニコ動画の埋め込みプレーヤーは JavaScript によるコントロールができないっぽいので対応していません。ドワンゴさんよろしくお願いします!!

Patches welcome

……というのを社内に設置してみて、まあいろいろと変な挙動はあるかと思いますが、とりあえず一日は普通に使えた感じです。同僚にもいろいろと機能を追加してもらいました。プルリクエストお待ちしております。

2012-10-05

carton bundle を高速化する

| 21:17 | carton bundle を高速化する - NaN days を含むブックマーク はてなブックマーク - carton bundle を高速化する - NaN days

2013-02-08: Carton 本体に bundle 時にデフォルトで carton.lock を参照するような変更 が入りました。以下の内容は古くなってると思います


carton を使ってるプロジェクトで tarball をリポジトリに同梱している場合、依存モジュールが増えるたびに carton bundle することになるのだけど、モジュールの数が増えてくるとこれがすこぶる遅くなってしまう。なんでかというと (cpanfile や Makefile.PL を元に) 依存関係をチェックしなおしているからで、その結果 tarball 追加するだけのつもりが carton.lock まで更新されてしまった、というような事態にもなったりする。

これらの挙動を自分好みというか気楽なふうにするため、--locked というオプションを実装してみました。

https://github.com/motemen/carton/tree/bundle-locked

carton bundle --locked とすると、(cpanfile ではなく) carton.lock の内容をもとに tarball をダウンロードするようになります。こうすることで

  • carton.lock が更新されない
  • carton.lock に記録されているモジュールは依存関係などのチェックがすんでいるので、単純にダウンロードだけを行える

というメリットがあります。

ちなみに [2012-10-24: 以降の内容は bundle-locked-download ブランチに移動しました] cpanminus を起動するときに (本家にはない) --download というオプションが指定されていて、ふつうのバージョンを使っている場合には無視されるのですが、本家にあった pull-req (https://github.com/miyagawa/cpanminus/pull/118) に1コミット追加して (https://github.com/motemen/cpanminus/tree/download) 最新の master にマージしたもの (https://github.com/motemen/cpanminus) を使うと便利なオプションで、--download を指定すると

  • tarball の展開をしない (もともとの pull-req の内容)
  • すでに同名の tarball が存在している場合、ダウンロードしない

という挙動になり、IO が劇的に減るため、2 回目以降の carton bundle が高速化できます (数秒で完了するようになります)。

2012-10-03

Test::Deep で JSON をパーズした結果と比較できる Test::Deep::JSON というのを書きました

| 13:56 | Test::Deep で JSON をパーズした結果と比較できる Test::Deep::JSON というのを書きました - NaN days を含むブックマーク はてなブックマーク - Test::Deep で JSON をパーズした結果と比較できる Test::Deep::JSON というのを書きました - NaN days

すぐに使いたくなったので、さきほど shipit いたしました。https://metacpan.org/release/MOTEMEN/Test-Deep-JSON-0.01/

https://github.com/motemen/Test-Deep-JSON

適当な hashref があって、その一部が JSON 形式文字列になっているようなとき (HTTP API のテスト時など) に code() でなんとかするのもあらかじめパーズしておくのも面倒なので、こんなときに Test::Deep で使える関数を提供するモジュールを書きました。

SYNOPSIS を見ていただければ使い方がわかると思います。

use Test::Deep;
use Test::Deep::JSON;

cmp_deeply {
    mode => 'hoge',
    payload => '{"a":1}',
}, {
    mode => ignore(),
    payload => json({ a => 1 }),
};

こんな風に Test::Deep の expected 部に使えるクラスを作る際には、以下のことを知っておくと便利そうです:

  • use Test::Deep::Cmp する
    • すると (/^Test::Deep::/ なパッケージの場合) @ISA に Test::Deep::Cmp が入ります
  • $self->descend($got) を実装する
  • diagnostics か diag_message も実装しておくとよし
  • expected の実体は $self->{val} に保持しておく
    • と diag 時のメッセージとかによしなに使われるっぽい

テストのテストには Test::Tester が便利そうですね。ひとつの関数呼び出しで複数テストが走ってびっくりするけど。

2012-09-30

YAPC::Asia 2012 に参加して発表してきました #yapcasia

| 14:28 |  YAPC::Asia 2012 に参加して発表してきました #yapcasia - NaN days を含むブックマーク はてなブックマーク -  YAPC::Asia 2012 に参加して発表してきました #yapcasia - NaN days

発表者としての初参加だった去年はスイーツエリア勢だったので、今回本トークとして採用されて嬉しかったです。タイムテーブルを見ると同じ会場に名前を見たことある人しかいなくて、かなりびびってましたが……。

きのうの夜に京都に帰ってきて、一通り終わって、じゃっかん自分の中では燃え尽きたというか、脱力した感じがあります。


内容は以前にも紹介した Wight という、Perl から PhantomJS を操作できてスクレイピングやテストに使えるよ、というモジュールの使い方と実装の紹介でした。説明とか、話とか、もっと上手くできればいいなと思うのですが、まあ、精進していきます。とりあえず発表も終わったので、PhantomJS や Poltergeist の新しいバージョンへの対応などをぼちぼち進めていきたいと思います。



いち参加者としては

  • xaicron さん
  • antipop さん
  • Perl 今昔
  • miyagawa さん
  • mizzy さんのクロージング

が特によかったです。こういう場所で同業者の事例をいろいろと見聞きできるのは、よいですね。mizzy さんの話は、ぼく自身 Web で使う Perl に触れたのは blosxom が最初だったので、懐しくて、そういえば kan さんの LT で出てきた wema にも、そういうことを感じました。それから、miyagawa さんのスライドにちょこっと取り上げていただいて、これは非常に感激しました……。

今回は前夜祭からの参加だったのですが、懇親会も含めて、去年の YAPC で会ったきりの人とかとも話せてよかったです。まだ話せなかった人もいて、できれば後夜祭で……と思っていたのだけれど、台風の影響で帰ってきてしまったのが残念です。


いろいろと発表を聞いていて思ったのは、やはり他の言語のカンファレンスにも参加しておかないと分からないこともあるよなーということで、また来年!とかじゃなく、何か機会を見つけたらイベントに参加できるように身も心も軽くしておきたいということでした。

2012-09-20

コード中から簡単に plackup できるモジュールを書いた

| 19:50 | コード中から簡単に plackup できるモジュールを書いた - NaN days を含むブックマーク はてなブックマーク - コード中から簡単に plackup できるモジュールを書いた - NaN days


[2013-10-15] shipit しました。P が大文字になっています。


https://github.com/motemen/AnyEvent-plackup

使い方は簡単で、

use AnyEvent::plackup;
my $server = plackup(app => \&psgi_app, port => $port);
say $server; # => 'http://0.0.0.0:8290' # 文字列化すると URL に

とするだけです。ポートを指定しないと適当に空いているポートが選ばれます。

で、これだけだとただの Twiggy のラッパーなのですが、これが便利なのは app を指定せずに起動することができる点です。

my $server = plackup();

こういうことができるわけです。この使い方をした場合、呼び出し側でよしなにリクエストを捌く必要があります。

my $req = $server->recv;
$req->respond([ 200, [], [ 'OK' ] ]);

$server->recv を呼ぶと、リクエストが来るまでそこで処理がストップします (AnyEvent のループが回りはじめます)。やってくるリクエストオブジェクトは Plack::Request に respond というメソッドが生えたもので、これに raw PSGI response を返してやることで、クライアントにレスポンスを返すことができます。

これが何に使えるのかというと、OAuth アプリを書いてる際など、コールバックをとりあえず受け取るためだけのサーバを立てたいときに便利です。

my $plackup = plackup(port => 4000);
   $plackup->host('localhost'); # 0.0.0.0 だとリダイレクト先として許されないので…
my $client = Net::OAuth2::Client->new(
    $client_id,
    $client_secret,
    site               => 'https://accounts.google.com',
    authorize_path     => '/o/oauth2/auth',
    access_token_path  => '/o/oauth2/token',
    scope              => 'https://www.googleapis.com/auth/drive.readonly',
    access_token_param => 'oauth_token',
)->web_server(
    redirect_uri => "$plackup/oauth2callback",
);

system 'open', $client->authorize_url; # ここでブラウザが開く

my $code = $plackup->recv->parameters->{code}; # ブラウザで承認してリダイレクトされるとここに到達
my $token = $client->get_access_token($code);

ブラウザを勝手に開いちゃえばあとはリダイレクトで何とかしてくれる、という感じです。PIN みたいなのを手で入れたりする必要がないので便利です。

2012-09-17

Git リポジトリの Web サイト (GitHub とか) を簡単に開けるコマンドを作った

| 22:50 | Git リポジトリの Web サイト (GitHub とか) を簡単に開けるコマンドを作った - NaN days を含むブックマーク はてなブックマーク - Git リポジトリの Web サイト (GitHub とか) を簡単に開けるコマンドを作った - NaN days

git-browse-remote というコマンドです。

インストール

今回はじめて RubyGems としてシェアさせていただきましたので

gem install git-browse-remote

でインストールできます。git browse-remote というサブコマンドが使えるようになります。

できること

  • 現在のリポジトリをホストしている Web サイトをブラウザで開きます
  • ブランチやコミットを指定するといい感じに対応するページを開きます

使い方

とりあえずインストールしたらまずは以下を実行してください。

% git browse-remote --init
Writing config for github.com...
Mappings generated:
browse-remote.github.com.top https://{host}/{path}
browse-remote.github.com.ref https://{host}/{path}/tree/{short_ref}
browse-remote.github.com.rev https://{host}/{path}/commit/{commit}

これで GitHub 用の設定が ~/.gitconfig に記録されます。browse-remote.github.com.top みたいな設定項目が増えているはずです。

で、任意のローカルにある (origin が GitHub を向いている) リポジトリに cd して

% git browse-remote

とすると、GitHub のページが開くはずです。引数にコミットやブランチを指定することもできて、たとえば cpanminus をクローンしたディレクトリで

% git browse-remote 1.5000

のようにタグ (またはブランチ) を指定すると https://github.com/miyagawa/cpanminus/tree/1.5000 が、

% git browse-remote 215cb345

のようにコミットを指定すると https://github.com/miyagawa/cpanminus/commit/215cb345d4312c744e7a299a7a33dbc6db33df74 がブラウザで開かれます。無指定の場合は現在 (HEAD) のブランチ/コミットが参照されます。

開かれうるページは 3 種類あり、そのリポジトリのリモートの URL を参照して、ウェブ上のリポジトリビューワの、

  • master ブランチの場合はプロジェクトのトップ (--top)
  • その他のブランチ/タグの場合はそれに対応するブランチ/タグのページ (--ref)
  • それ以外であれば1コミットのページ (--rev)

を開きます。(括弧内はそのページを開くことを強制するコマンドラインオプションです)

さらに以下のように remote が複数ある場合、

% git config --get-regexp '^remote\..*\.url$'
remote.origin.url git://github.com/miyagawa/cpanminus.git
remote.motemen.url git@github.com:motemen/cpanminus.git
% git browse-remote motemen/master # もしくは motemen (motemen というタグやブランチがない場合)

で、motemen がフォークしたプロジェクトのページ (https://github.com/motemen/cpanminus) を開くことができます。

URL のカスタマイズ

git-browse-remote は指定されたブランチ/コミットからどういったページを開くかを決定し、それぞれに用意されたテンプレートを用いて URL を組み立てます。--init 時に表示されている内容ですが、例えば GitHub の場合は ~/.gitconfig に

  • browse-remote.github.com.top (プロジェクトのトップ)
  • browse-remote.github.com.ref (ブランチ/タグ)
  • browse-remote.github.com.rev (コミット)

という風に指定されています。社内リポジトリなど、特定のホストに対して独自のリポジトリビューワを使いたい場合には、git config browse-remote.my.host.top http://… のようにして (または .gitconfig を直接編集して) これをカスタマイズすることができます。テンプレートで使える変数は以下のとおり。{} 内が Ruby のコードとして評価されるかんたんテンプレートです。

  • host ("github.com")
  • path ("motemen/git-browse-remote")
    • ただの文字列ではなく、{path[-2..-1]} のようにスライスすることができます
  • ref ("refs/heads/master")
  • short_ref ("master")
  • commit ("04f7c64ba9d524cf311a673ddce5722b2441e2ea")

ちなみに GitHub Enterprise を使っているなど、配置が完全に GitHub と同じような場合には

% git browse-remote --init github.local=github 
Writing config for github.local...
Mappings generated:
browse-remote.github.local.top https://{host}/{path}
browse-remote.github.local.ref https://{host}/{path}/tree/{short_ref}
browse-remote.github.local.rev https://{host}/{path}/commit/{commit}

とすることで簡単に設定をおこなえます。一応 GitWeb 用に --init host=gitweb というのも作ってますが、GitWeb 常用してないので分からない……。

どうぞご利用ください。

2012-08-22

Kyoto.pm #2 で DBIx::Lite の紹介をしました #kyotopm

| 23:30 | Kyoto.pm #2 で DBIx::Lite の紹介をしました #kyotopm - NaN days を含むブックマーク はてなブックマーク - Kyoto.pm #2 で DBIx::Lite の紹介をしました #kyotopm - NaN days

Kyoto.pm Tech Talks 02を開催します! - kyotopm's blog

d:id:shiba_yu36 さんに「モテメンさん何か発表しませんか?」と打診されて「じゃあボクが集めたカッコイイ Perl のコードを大発表するよ!」と答えたのですが 1 週間前になっても 2 つ 3 つほどしかネタがなかったので諦めて最近使ってみている ORM の紹介をしました(自分が作ったわけではありません)。

DBIx::Lite はメソッドチェーンでクエリを組み立てるインターフェースのシンプルな OR マッパです。ロークラスやスキーマクラスの定義を要求せず、すぐに使いはじめられるのが特徴のようです。使いはじめるのは簡単ですが、いちど拡張しようとしはじめるといろいろと癖があって、少しはまるところがあるかもしれません。最近 ORM や DBI に思いを馳せることが多く、その一環での発表でした。