Hatena::Groupsubtech

NaN days

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

2014-06-02

ブログを移動しています

19:14 | ブログを移動しています - NaN days を含むブックマーク はてなブックマーク - ブログを移動しています - NaN days

みなさんこんにちは。motemen です。

実は以前からはてなブログのほうにブログを移行しております。新しい記事はそちらのほうに書きますので、もろもろよしなにお願いいたします。こちらのほうに書いている記事を移行する予定は現在のところありません。

http://motemen.hatenablog.com/

はてなブログ便利です。よろしくお願いいたします。

oqunooquno2014/06/02 21:58motemenさんこんにちは。oqunoです。

2013-08-14

なぜかときどき落ちるテストをリカバリーする undead

| 19:26 | なぜかときどき落ちるテストをリカバリーする undead - NaN days を含むブックマーク はてなブックマーク - なぜかときどき落ちるテストをリカバリーする undead - NaN days

動的にポートを確保しているとか PhantomJS を使っているテストが、手元では大丈夫なのに Jenkinsなぜか(!)ときどき(!)落ちるという現象に際し、毎回ログを見て「ああこのエラーなら問題ないからやり直そう……」というのも馬鹿らしいので、標準(エラー)出力を監視しつつ Perl を実行し、特定の出力を吐いて異常終了していたらもう一度やり直すラッパーを書きました。

https://github.com/motemen/undead

使い方

.undeadrc.pl に以下のような内容を記述して、

{
    pattern => {
        stderr => [
            qr/^PhantomJS has crashed\./,
        ]
    },
    count => 20,
};

起動します。

% undead perl -le 'die "PhantomJS has crashed." if rand() < 0.9; print "alive!"'

指定された出力に引っかかって死んだ時に、指定した回数までコマンドの実行をやり直してくれます。

# [undead] err - PhantomJS has crashed. at -e line 1.
# [undead] Retry 'perl -le die "PhantomJS has crashed." if rand() < 0.9; print "alive!"' (19 remains)
# [undead] err - PhantomJS has crashed. at -e line 1.
# [undead] Retry 'perl -le die "PhantomJS has crashed." if rand() < 0.9; print "alive!"' (18 remains)
# [undead] err - PhantomJS has crashed. at -e line 1.
# [undead] Retry 'perl -le die "PhantomJS has crashed." if rand() < 0.9; print "alive!"' (17 remains)
# [undead] err - PhantomJS has crashed. at -e line 1.
# [undead] Retry 'perl -le die "PhantomJS has crashed." if rand() < 0.9; print "alive!"' (16 remains)
# [undead] err - PhantomJS has crashed. at -e line 1.
# [undead] Retry 'perl -le die "PhantomJS has crashed." if rand() < 0.9; print "alive!"' (15 remains)
# [undead] err - PhantomJS has crashed. at -e line 1.
# [undead] Retry 'perl -le die "PhantomJS has crashed." if rand() < 0.9; print "alive!"' (14 remains)
alive!

undeadperl

"undead command args" として起動する以外に、実行スクリプトの名前を "uneadprog" (例えば undeadperl)に変えれば、prog を起動するようになります。

% undeadperl -le '…'

prove で使うときは HARNESS_PERL を指定すると便利です。

% HARNESS_PERL=./script/undeadperl prove …

これで(だいぶ雑に)CI を安定化させられますね!

2013-02-19

テストが失敗したときに再試行できるようにするモジュールを書きました

| 13:22 |  テストが失敗したときに再試行できるようにするモジュールを書きました - NaN days を含むブックマーク はてなブックマーク -  テストが失敗したときに再試行できるようにするモジュールを書きました - NaN days

Selenium を使ったときなどテストの状態が外部に依存しているような場合、状態が確実に変化するのを待つために sleep などしてみているけれどやりたいのはそういうことじゃないんだ! という訳で、テストが失敗したら少しウェイトを入れて再試行するモジュールを書きました。

motemen/Test-Retry ? GitHub


無意味な例ですが、

use Test::More;
use Test::Retry max => 3, delay => 0.1; # exports retry_test(&)
my $x = 0;

retry_test {
    is $x++, 2, '$x++ == 2';
};

retry_test のブロック内でのテストが失敗しそうな場合、デフォルトでは 0.5 秒待機して最大 5 回リトライします。テストが成功する場合にはそのまま次のへ実行を移し、最大試行数を越えた場合は失敗します。

上の例だと、以下のような出力になります。

# test '$x++ == 2' failing; retry (2 remaining)
#          got: '0'
#     expected: '2'
# test '$x++ == 2' failing; retry (1 remaining)
#          got: '1'
#     expected: '2'

ok 1 - $x++ == 2

また、いちいちブロックで囲んでらんないという人のために、既存のテスト関数を書き換えるような機能も提供しています。

use Test::Retry override => [ 'is' ];

# または以下; prototype ごと変わるので BEGIN で
BEGIN { Test::Retry->override('is') }

is { func_with_some_random_lag(), $expected };

テスト関数に渡される引数を返すブロックを渡すと、以下と同じ動作をします。

retry_test {
    is func_with_some_random_lag(), $expected;
};

たとえば Selenium::Remote::Driver を使ったテストだと

$driver->find_element(...)->click;

retry_test {
    is $driver->get_title, ...;
};

みたいな感じにして、ページ読み込みを待つ手間が省けます。

どうぞご利用ください。

2013-02-15

jQuery のメソッドチェーン呼び出しのコードを簡単に生成できる Perl モジュールを書きました

| 13:25 | jQuery のメソッドチェーン呼び出しのコードを簡単に生成できる Perl モジュールを書きました - NaN days を含むブックマーク はてなブックマーク - jQuery のメソッドチェーン呼び出しのコードを簡単に生成できる Perl モジュールを書きました - NaN days

PhantomJS 1.8 のリリースで まっとうな方法で Perl から PhantomJS の操作ができるようになった おかげで 去年の YAPC::Asia 2012 で発表した Wight はめでたくオワコン化しました……。だけれど Wight のオマケで作っていたモジュールは便利なので、別に分けてみました。

motemen/String-jQuery ? GitHub

SYNOPSIS にある通りですが、このモジュールがエクスポートする jQuery() という関数を起点にして、JavaScriptjQuery を使うようにしてメソッドチェーンを繋ぎ、最後に文字列化すると JavaScript における jQueryAPI 呼び出しとして使える式が得られます。Perl のコード中に文字列で JS を埋め込むよりは読みやすいはず……です。

以下の例を見るのが分かりやすいと思います。.'length' あたりがポイントですね( ╹◡╹)

jQuery();                           # => 'jQuery()'
jQuery('a');                        # => 'jQuery("a")'
jQuery(\'document');                # => 'jQuery(document)'
jQuery('a')->text();                # => 'jQuery("a").text()'
jQuery('a')->text('xxx');           # => 'jQuery("a").text("xxx")'
jQuery('a')->click(sub { e => 'return false' });
                                    # => 'jQuery("a").click(function (e) { return false })'
jQuery('a').'length';               # => 'jQuery("a").length'
jQuery->ajax({ method => 'POST' }); # => 'jQuery.ajax({"method:"POST"})'

Selenium::Remote::Driver を使うにしても、ページ内要素の調査を行うのに jQuery は便利だと思います(このモジュールが jQuery の埋め込みを行うわけではありません)。

my $result = $driver->execute_javascript(
    'return ' . jQuery('#content a')->attr('href')
);

似たようなモジュールには HTML::JQuery というのがありますが、こちらは script 要素全体を出力するもののようです。

2013-02-12

自分だけのオリジナルなフレーズをツイートできる userscript

| 13:24 | 自分だけのオリジナルなフレーズをツイートできる userscript - NaN days を含むブックマーク はてなブックマーク - 自分だけのオリジナルなフレーズをツイートできる userscript - NaN days

「これは新しい! ツイートすれば 100 fav 間違いなし!」なフレーズを思いついたとき、賢明な方々は

  1. 念のため一度 Google 先生にお伺いを立てる
  2. 一件もヒットしなければ満を持してツイート

という手順を踏んでいるかと思います。これをひと手間減らすのが今回ご紹介する userscript。インストールすると以下のように

f:id:motemen:20130212130926p:image

0 件の検索結果にツイートボタンが表示されます。あとはこのボタンをクリックしてツイートウィンドウを開くだけ。簡単ですね!

インストール

tweet-google-unique-query.user.jsリポジトリ)をダウンロードして、chrome://extensions に放り込むとよいです。Chrome でのみ確認していますが、Firefox などのブラウザでも使えるかもしれません。

参考リンク

  • Twitter Buttons | About
    • テキストをツイートするだけのボタンを作りたい(URL は要らない)ときは、hashtag 版にしてハッシュタグを指定しないといいみたいです(ひどい)。
  • User Scripts - The Chromium Projects
    • Greasemonkey 由来の @include を使うとインストール時にすべてのウェブサイトに対する権限を要求しようとするので、Chrome 向けのユーザースクリプトを作るときには @match を使うのがよいです。

2013-02-08

Kyoto.js #5 で日本の連休についての発表をしました #kyotojs

| 13:31 | Kyoto.js #5 で日本の連休についての発表をしました #kyotojs - NaN days を含むブックマーク はてなブックマーク - Kyoto.js #5 で日本の連休についての発表をしました #kyotojs - NaN days

先日開催された Kyoto.js #5 で LT をしてきました。

次の連休

今後1年間の3連休が確認できる 次の連休 というサイトとその実装について紹介しました。クライアントサイド JavaScript のみで動いていて、祝日データの取得には Google Calendar API を使っています。

jquery.partyhard

また、このサイトで使用している自作の jQuery プラグイン jquery.partyhard についての紹介もしました。

使い方は簡単で、以下のようにして呼び出すだけで document.body が PARTY HARD 状態になります。

$(document.body).partyhard();

使用例は http://motemen.github.com/jquery-partyhard/demo.html をご覧ください(ロードに少し時間がかかります、あといまちょっと怖い画像が出てくるかもしれないので http://motemen.github.com/jquery-partyhard/demo.html?cat%20gif で猫を見てもよいです)。

Kyoto.js の感想

隔週で開催されていて、こういったしょうもないことも気軽に紹介できる雰囲気がいいですね。京都近辺にいらっしゃる方は是非顔を出してみては?

発表の中では mechairoi さんの Emacs Lisp から Chrome を操作する拡張 が便利そうかつ面白かったです。

2013-02-07

GitHub のリポジトリを管理する小さいツール ghq

| 13:14 | GitHub のリポジトリを管理する小さいツール ghq - NaN days を含むブックマーク はてなブックマーク - GitHub のリポジトリを管理する小さいツール ghq - NaN days

この記事は古いです。お探しなのは ghq: リモートリポジトリのローカルクローンをシンプルに管理する - 詩と創作・思索のひろば の方かもしれません。

GitHub 上の面白そうなコードは手元に clone してから見るのが普通だと思いますがそれをこんな感じに整理してます。

  • ~/extrepo/@author/project に git clone する(~/extrepo/@motemen/ghq とか)
  • ~/extrepo/project からそこに symlink する(~/extrepo/ghq → ~/extrepo/@motemen/ghq

こうすると ~/extrepo/jquery などでアクセスできて楽です。「あの人のあのプロダクト」って思い出し方をしたいときには ~/extrepo/@motemen を辿っていけばよい。

tree するとこんな感じです。

.
|-- @cho45
|   |-- Config-ENV
:
|   |-- jsdeferred
|   `-- starter.pl
|-- @cloudhead
|   |-- http-console
|   `-- less.js
:
|-- Brownie -> @masaki/Brownie
|-- Class-Accessor-Lite -> @kazuho/p5-Class-Accessor-Lite
:
|-- webiblo -> @mizzy/webiblo
`-- ws -> @einaros/ws

ghq

そしてこれを簡単に行うためのオレオレツールが ghq です。以下のようにして使えます。

clone する
% ghq [-clone] https://github.com/motemen/polka

のようにすると ~/extrepo/@motemen/polka に git clone されて先に伸べたようなシンボリックリンクが作られます。

ディレクトリへ移動する
% ghq [-cd] polka

または

% ghq [-cd] @motemen/polka

とするとそのディレクトリへ cd します。

インストール

zsh のスクリプトで書いてます。

% git clone git://github.com/motemen/ghq.git ghq_dir

したのち

fpath=(ghq_dir/zsh $fpath)
autoload -U ghq
autoload -U compinit; compinit

と .zshrc に書くと、ghq というコマンドと補完(-cd したときなど)が有効になります。clone 先のルートディレクトリは git config ghq.root で変更できます(たぶん)。

あとすでに同名のリポジトリが存在しているときは remote に追加するようにしてみたんだけどそれは余計なお世話だったかもしれない……。