2013年7月28日日曜日

gitのhooksを活用する


gitフックについて

どのような種類のフックがあるかは、ここで確認

コミット前にテストを実行

.git/hooks/pre-commitに好きなスクリプトを置いておくと実行されます。
以下のコードでcommit時にproveが走るようになります。


#!/usr/bin/env perl
use strict;
use warnings;

chomp( my $project_dir = `git rev-parse --show-toplevel` );
system("prove", "-I${project_dir}/lib", "${project_dir}/t") == 0 or die 'Failed test';

gitのバージョンが古いためshow-toplevelオプションが無い場合は、
git-dirオプションで.gitディレクトリへのパスが取得できるので、そこから目的のパスを作るといいです。

コミット前にコードチェックorフィルター

my @modified_files = system('git diff-index --cached --name-only HEAD');

で変更ファイル一覧が取れるので、あとはお好きにできますね。




gitでPATHの先頭に’/usr/libexec/git-core:/usr/bin:’が追加される



pre-commitにperlスクリプトを置いておいたら、perlbrewではなくてシステムperlが使われていて、
調べてみるとタイトルのようなことになっていました。

xcodeがインストールしてくるgitはアップルのパッチが当たっているようです。
参考ツイート
コード



git --version
git version 1.7.12.4 (Apple Git-37)



通常のgitを使うか、perlbrewであれば環境変数からパスを組み立てることで解決できます。





2013年7月16日火曜日

テストがどこにあるのかわからない対策

perlではt/以下にテストファイルを置いておくのですが、肥大化してくるとテストがどこにあるのか分からなくなります。
対策として、1クラス1テストファイルにしてライブラリと同じディレクトリ階層を作る、があります。
しかし面倒くさいですし、そのファイルにあることは分かっても、どのテストが自分の確認したいテストなのかが、すぐには分からないことがあります。

命名規則を決めてgrep

  • メソッド名の完全な名前をsubtestに書く
  • メソッド内の各種条件はsubtestをネスト
メリット
  • 一意に検索できる
  • 俺々カバレッジが作りやすい
テスト対象コード

package Hoge;

sub new {
    ....
}
sub search {
    ....
}
テストコード

use Test::More;

subtest 'Hoge::search' => sub {
    subtest 'limit' => sub {
        my $hoge_obj =  Hoge->new;
        my @res = $hoge_obj->search( limit => 20 );
        is(scalar @res, 20);
    };
    subtest '....' => sub {
        ....
    };
};

done_testing;
おれおれカバレッジ
この命名規則だとsubtestをオーバーライドすることでテストカバレッジが簡単に取れます。
既存カバレッジモジュールで、あるメソッドのテストを実行したときに中で異なるメソッドを呼び出してるだけで、そのメソッドもカバレッジに追加されて、むーん、ってときに作ってました。
みんなどうやってるんだろう
この話をしたところRSpecぽいですねって言われた。


Kyoto.pm 05に参加してきました

人生初の遠距離pm参加をノリできめて発表してきました。
ツイッターアカウントは知ってるけど話したことはない、って人が多くて来てよかったなーと思いました。

いつも通り、全体的なことは他の人が書くだろー、ってことで自分の話をすると、
EPUB::Parserってモジュールを書いたので、それについて書いた動機と、それの設計などをざっくり言った感じです。
僕的にはコードの設計を教えて欲しいなって気持ちだったんですが、発表って形式でいきなり出しても難しい感じでした。

余談ですが、EPUB::Filterというモジュールも書こうとしてて、
EPUB::Parserのスクリプトが返す{ "title" : "タイトル" } ってjsonを書き換えて、Filterのスクリプトに食わすとEPUBの中身が書き変わるというものです。
けど、Filterって銘打つならHTML::Filterっぽくせにゃいかんのかなー、そこまでは、、、って感じです。

発表資料: EPUBのパーサーとビルダー

作成動機
  • 電子書籍をweb上で作成、販売するサイトを運用
  • 出版社から頂くデータがEPUB3化
  • EPUB3仕様に完全対応した既存モジュールがない
  • 都度アップデートする必要があるなら得意なPerl製モジュールでないとキツい
発表動機
  • 設計やメソッド名について意見が欲しい
  • パーサーの話が主
EPUB::Parser
github
  • サイトで提供しているインポート機能のために作成
  • 現在EPUB3のみ対応
  • インポート時に必要となるデータの整形を吸収(パスの書き換え等)
  • XML::LibXMLを使用
  • ドキュメント追加中

EPUB::Parser設計
  • ファイル毎にクラス化しており、複数ファイルを参照する必要がある場合は責務を逸脱しないよう別の名前空間を用意(EPUB::Parser::ManagerとEPUB::Parser::Fileに分割)
  • 単一ファイルの中にもmanifestやspineなど重要な役割をもつノードがあるのでそれぞれクラス化(EPUB::Parser::File::**::Context::**)

 EPUB
 ├── Parser
 │   ├── File
 │   │   ├── Container.pm # META-INF/container.xml用クラス
 │   │   ├── Document.pm # 本文ファイル用
 │   │   ├── Navi
 │   │   │   ├── Context
 │   │   │   │   └── Toc.pm
 │   │   │   └── Context.pm
 │   │   ├── Navi.pm
 │   │   ├── OPF
 │   │   │   ├── Context
 │   │   │   │   ├── Guide.pm
 │   │   │   │   ├── Manifest.pm
 │   │   │   │   ├── Metadata.pm
 │   │   │   │   └── Spine.pm
 │   │   │   └── Context.pm
 │   │   ├── OPF.pm
 │   │   ├── Parser #各ファイル用パーサー. xpathのネームスペース設定とコンテキスト切り替えを行う
 │   │   │   ├── Container.pm
 │   │   │   ├── Document.pm
 │   │   │   ├── Navi.pm
 │   │   │   └── OPF.pm
 │   │   └── Parser.pm # 各ファイル用パーサーの親で、find,singleメソッドを持っている
 │   ├── Manager
 │   │   └── Pages.pm
 │   └── Util
 │       ├── Archive.pm         # zip内データをイテレータで返す
 │       ├── AttributePacker.pm # 任意の属性値を主キーとしたデータ構造を返す
 │       ├── Context.pm  # EPUB::Parser::File::**::Contextの共通メソッドエクスポート用.
 │       │               # 安易にUtilに置いてしまった
 │       ├── EpubLoad.pm
 │       └── ShortcutMethod.pm # EPUB::Parserにメソッドエクスポート
 └── Parser.pm
  • データ構造を作成するメソッド命名が難しい。ノードの任意の属性値を主キーとして、データ構造を返す処理など。
  • テストはsubtest名に完全なメソッド名を書いてgrepで特定できるようにしている
  • 面倒くさいと言われたのでショートカットメソッドを作った。$self->opf->metadata->titleが$self->titleに。しかしショートカットメソッドは作り出すとキリがなく、EPUB/Parser.pmが本質と関係ないコードで肥大化するので、EPUB/Parser/Util/ShortcutMethod.pmからエクスポートすることにした。
EPUB3::Builder(cpan化の予定無し)
github
  • 入力xhtmlのbody部をepub3用のテンプレートに流し込む
  • メタデータ等の作成
  • naviファイルは全ページ列挙してるだけ
  • テストでxmlを比較するにはXML::Compareを使った

jsでグラフ生成をためしてみたい
http://code.shutterstock.com/rickshaw/


2013年7月9日火曜日

proxy.pac on Mac

ローカルにpacファイル配信用サーバー立てる


#!/usr/bin/env python

import SimpleHTTPServer
import SocketServer

PORT = 8080

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
Handler.extensions_map['.pac'] = 'application/javascript'

httpd = SocketServer.TCPServer(("",PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()
を保存して、同ディレクトリにproxy.pacファイルを置いておく。


// ホスト名がhoge.comのときだけproxyする
function FindProxyForURL(url,host) {
  if (dnsDomainIs(host, "hoge.com")) {
    alert( 'proxy!: ' + url);
    return "PROXY 127.0.0.1:5000";
  }
  else {
    alert( 'direct: '  + url);
    return "DIRECT";
  }
}
で、サーバー起動.
url はhttp://localhost:8080/proxy.pac と仮定。

Firefoxに設定

メニューから、環境設定->詳細->ネットワーク->接続設定
自動プロキシ設定スクリプトURLを設定。
f:id:toku_bass:20130708163231p:image:w360
で終了。
pacファイルを更新したときにFirefox側も更新する方法は
自動プロキシ設定スクリプトURL横の再読み込みボタンを押す以外は今のところわからない
(面倒)

Firefoxでデバッグ


pacにalertを仕込んでおくと、エラーコンソールで確認できる。
メニューからツール->web開発->エラーコンソール

chromeに設定


chromeはosのほうの設定を読みにいくので、chromeの詳細設定で
ネットワーク項目のプロキシ設定の変更ボタンを押すと、osのネットワーク設定が開く。
(ここでボタンが押せない場合、chromeのproxy関係の拡張機能を無効にする)
自動プロキシ構成を有効にして、pacのurlを記入する。
f:id:toku_bass:20130708163232p:image:w360
pac更新後の反映は
chrome://net-internals/proxyservice#proxy
から可能。

chromeでデバッグ


よくわからない