読者です 読者をやめる 読者になる 読者になる

バージョン番号の比較

3.8.9と3.8.2.3の比較とかマイナー番号がなんかいっぱいあるときの比較。

versionモジュールを使う。

use version;

warn "true"  if version->new('3.8.9') >= version->new('3.8.2.3');
warn "false" if version->new('3.8.9') >= version->new('3.8.9.1');

PPIとTest::LocalFunctions、Compiler::Lexer周辺について調べたこと

帰省中にTwitterを眺めていたら話題になっていて、知らなかった事だったので調べてみました。

MANIFESTファイルとMANIFEST.SKIPファイル

MANIFESTファイルは、パッケージに含めるファイルの一覧です。
MANIFEST.SKIPファイルには、パッケージに含めたくないファイルの一覧です。

ExtUtils::Manifest

ExtUtils::manifestは、MANIFESTファイルを書いたりチェックしたりするためのツールです。

use ExtUtils::Manifest qw(skipcheck maniread);
use Data::Dumper;

# MANIFEST.SKIPファイルの指定によりスキップされるファイルチェック
warn skipcheck();

# MANIFESTファイルに記述されているリストを読み込む
warn Dumper maniread();

PPI

PPIは、Pure PerlPerlスクリプトの構造を解析することを目的としたプロジェクトです。

use PPI::Document;
use PPI::Dumper;

my $filename = $ARGV[0];

my $doc = PPI::Document->new( $filename );
my $dumper = PPI::Dumper->new($doc);
warn $dumper->string();


以下の様なモジュールに使われているそうです。

Test::LocalFunctions

Test::LocalFunctionsは、MANIFESTファイルに記述されているモジュールを対象に、使われていないローカル関数を検出するモジュールです。
内部ではPPIが使われています(Bで再実装するかもということです)。

use Test::LocalFunctions;

all_local_functions_ok();

Compiler::Lexer

Compiler::Lexerは、Perlの字句解析器モジュールです。C++で実装されており、高速に動くようです。

use Compiler::Lexer;
use Data::Dumper;

# 解析対象のperlスクリプトファイル名を読み込む
my $filename = $ARGV[0];

# スクリプト内容を読み込む
open my $fh, $filename or die "Error";
my $code = do { local $/; <$fh> };

# 解析器を生成
my $lexer = Compiler::Lexer->new($filename);

# トークン化
my $tokens = $lexer->tokenize($code);
print Dumper $tokens;

# シンタックスのレベル単位でグループ化
print Dumper $lexer->get_groups_by_syntax_level($tokens, Compiler::Lexer::SyntaxType::T_Stmt);

# 使用しているモジュール・プラグマの一覧
print Dumper $lexer->get_used_modules($code);

現在は、
mixiのコピペ検出ツールで使われていたり、
続・技術的負債の把握と改善を促すために - mixi Engineers' Blog

PPIで書かれていたモジュールが、Compiler::Lexerで再実装されたりしています。

Test::MojoのUserAgentを変更する

Test::Mojoないで使われているMojo::UserAgentのnameを変更する。

$t->ua->name('Test UserAgent');

Mojo::Uploadのmove_toは必ずしもmvではない

Mojo::Uploadのmove_toの実態は、メンバ変数のassetのmove_toに委譲しています。

sub move_to {
  my $self = shift;
  $self->asset->move_to(@_);
  return $self;
}

このassetは「Mojo::Asset::Memory」か「Mojo::Asset::File」のインスタンスが入ります。
どちらのインスタンスになるかは、アップロードされたファイルの容量によって決まります。

Mojo::Asset::Fileのmove_toの実装は、以下のように期待通りのmove。

  # Move file and prevent clean up
  my $from = $self->path;
  move($from, $to) or croak qq{Can't move file "$from" to "$to": $!};
  return $self->path($to)->cleanup(0);


しかし、Mojo::Asset::Memoryは、以下のようにメモリーからファイルに書き出す処理となっています。

  spurt $self->{content}, $to;

spurtはMojo::Utilで実装されている関数で、内部ではsyswriteを呼び出しています。


したがって、書きだしている途中でファイルが読み込まれるアプリだと不具合が出ます。
これに対応するために、以下のようにM::A::Fileに一回書きだすようにします。

  unless ($upload->asset->is_file) {
      my $file = Mojo::Asset::File->new;
      $file->add_chunk($self->slurp);
      $upload->asset($file);
  }

  $upload->move_to($to);


この他に、Mojo::Asset::Fileが来るもんだと思って、$upload->asset->handleなどでファイルハンドルを得ようとするとエラーになったりするので注意。
is_fileで確かめるようにしたほうが良い。

ドメインを取得する

$self->req->url->to_absを使う。

say $req->url->to_abs->userinfo;
say $req->url->to_abs->host;
say $req->url->to_abs->path;


Mojo::Message::Request - HTTP request - metacpan.org

Ukigumoで構築するCI環境

UkigumoでCI環境を構築します。
今回構築した環境では、

  • proveでテスト実行
  • Ikachanでテスト通知

を行うことにしています。

Ukigumo-Serverの設置

gitでcloneしてきます。

$ git clone git://github.com/ukigumo/Ukigumo-Server.git

READMEに従って依存モジュールをインストールします。

$ curl -L http://cpanmin.us | perl - Carton
$ carton install

サーバの実行もREADME通りです。

$ carton exec perl -- local/bin/plackup -- port 5555 app.psgi

Ikachanの設置

cpanmでインストールします。

$ cpanm App::Ikachan


以下のコマンドでikachanを起動します。

$ ikachan -S {irc_server_ip} -N {nickname}


Ukigumoでは、「ikachanのURL」と「チャンネル名」を使用します。

Ukigumo-Client

cpanmでインストールします。

$ cpanm Ukigumo::Client


インストールするとukigumo-client.plが使えるようになるので、このスクリプトを使用してテストを実行します。

オプションは以下のものを使います。

  • repos
  • server_url
    • Ukigumo-ServerのURLを指定します。
  • command
    • proveコマンドを記述します。

以上のことをふまえて、以下の様なコマンドを打ってみる。

$ ukigumo-client.pl --repo={git_repository} --server_url={ukigumo-server-url} --command="prove -Ilib t/" --ikachan_url="http://127.0.0.1:4979/" --ikachan_channel=#ci


問題なく行けたら、cronにこのコマンドを設定します。
perlbrewなどに考慮するため、以下のようなシェルファイルを準備して実行するようにします。

#!/bin/sh

export HOME=/home/myapp
source ~/perl5/perlbrew/etc/bashrc
perlbrew use 5.14.2

ukigumo-client.pl --repo={git_repository} --server_url={ukigumo-server-url} --command="prove -Ilib t/" --ikachan_url="http://127.0.0.1:4979/" --ikachan_channel=#ci

タブをスペースに変換する

再帰的に「.pl」「.pm」のファイルのインデントをタブからスペースに変換する。

find . -name "*.pl" -exec perl -i.bak -ple 's{^(\t+)}{q( ) x (length($1)*4)}e' {} \;
find . -name "*.pm" -exec perl -i.bak -ple 's{^(\t+)}{q( ) x (length($1)*4)}e' {} \;