sironekotoroの日記

Perl で楽をしたい

Minilla で作ったモジュールにデータフォルダを入れる

全国の銀行・支店コードを取得・表示する ZenginCode というものがあります。

github.com

これには!なんと!!Perl 版がないので(しょんぼり)自分で作って使ったりしています。

きっかけは経理業務で必要に迫られ・・・必要に迫られ・・・楽をする必要に迫られ・・・楽がしたい・・・

github.com

モジュールの中にデータフォルダを作りたかった

ZenginCode の Ruby 版だと、gem(Perlでいうところのモジュール)の中に全銀コードのデータが格納されており、そのまま利用が可能です。

自分の Perl モジュールは Minilla で作っているのですが、Ruby 版と同じ様にモジュール内にデータファイルを入れる方法がわかりませんでした。

結果、銀行コードの一覧のみを別にダウンロードして、モジュールはそこを参照しにいく、という構成で作りました。

Minillaのモジュールの中にデータフォルダ作れます

が、同じような問題にあたり、そして解決した人がいることを発見!

qiita.com

こちらの方はポケモンGoのデータを用いて、同じようなことをしている・・・!

そして、minilla でモジュール内にデータを置いておく方法についてはしっかり Web+DB での連載でも言及されておりました。あぁ。

gihyo.jp

ソースコード以外のファイルをモジュールに同梱したい場合があります。たとえば,テンプレートファイルや辞書ファイルなどです。そういったファイルはshareディレクトリに配置します。

その場合,CPANにアップロードしてからも正しく動作させるために,コード内からそれらのファイルに直接アクセスするのではなく,File::Shareなどのモジュールを通してアクセスして開発する必要があります。

さっそくチャレンジ・・・File::Share ってなに?

File::Shareなどのモジュールを通してアクセスして開発する必要があります。

File::Share 初めて聞きますね。ってことで Google 翻訳片手で見てみます。

metacpan.org

・・・ダメだよくわからん。でも大丈夫、Perl の場合は SYNOPSYS が充実してるので、まずはそれをコピって実行。

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

use File::Share ':all';

my $dir = dist_dir('Foo-Bar');
my $file = dist_file('Foo-Bar', 'file.txt');

で、こんなエラー

Failed to find share dir for dist 'Foo-Bar' at /Users/sironekotoro/.plenv/versions/5.28.1/lib/perl5/site_perl/5.28.1/File/Share.pm line 37.
[Finished in 0.2s with exit code 2]

ううん、わからないので Google 翻訳で

File :: Shareはshare、対応するモジュールが開発パスからロードされたことを認識した場合、ローカルディレクトリを探します。

ううーん。

このモジュールの元となった File::ShareDir も見てみたんですがよくわからず・・・で色々試行錯誤した結果です。

File::Share とは

File::Share とは汎用的なファイルのパス解決ツールではなく、モジュール作成時に使うもののようです。

標準モジュールではないので、cpanm File::Share などでインストールが必要です。

まぁ、汎用的なやつなら File::Find とか Path::Tiny とかあるものね。

やってみる

  1. $ minil new Sironekotoro::File としてモジュールの雛形を作る

  2. 作ったモジュールのフォルダの中に share フォルダを作成する

  3. share フォルダの中に test.txt を作成する。中には適当に hoge とか書いておく

  4. モジュールの中に test.pl を作成する。名前の通りテスト用

この時点で Sironekotoro-File フォルダの中身はこうなってます。

$ tree
.
├── Build.PL
├── Changes
├── LICENSE
├── META.json
├── README.md
├── cpanfile
├── lib
│   └── Sironekotoro
│       └── File.pm
├── minil.toml
├── share
│   └── test.txt
└── t
    └── 00_compile.t

4 directories, 10 files

手を入れるのは lib/Sironekotoro/File.pmtest.pl です。

まずは lib/Sironekotoro/File.pm 、わかりやすいように warn を仕掛けておきます。

package Sironekotoro::File;
use 5.008001;
use strict;
use warnings;

use File::Share 'dist_dir';

our $VERSION = "0.01";

sub new {
    my $class = shift;
    my $self  = bless {}, $class;
    return $self;
}

sub read {
    my $self = shift;

    my $dir = dist_dir('Sironekotoro::File');

    warn '$dir: ' . $dir;

    my $data = "$dir/test.txt";

    warn '$data: ' . $data;


    # ファイルを読み込む処理
    my $str;
    open my $FH , '<', $data;
    for my $line (<$FH>){
        chomp $line;
        $str .= $line;
    }
    close $FH;

    return $str;
}


1;

次にテスト用の test.pl

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

use lib qw/lib/;
use Sironekotoro::File;

my $obj = Sironekotoro::File->new();

print "The text of the file's contents\n";
print $obj->read . "\n";

これで $ perl test.pl とすると、無事、モジュール内のファイルを読んでくれます。

そして、モジュールに仕掛けた warn の効果でこのような出力になります。

$ perl test.pl
The text of the file's contents
$dir: /Users/sironekotoro/Dropbox/perl/temp/Sironekotoro-File/share at lib/Sironekotoro/File.pm line 21.
$data: /Users/sironekotoro/Dropbox/perl/temp/Sironekotoro-File/share/test.txt at lib/Sironekotoro/File.pm line 25.
hoge

これで、モジュールの中の $dir には、モジュール内の share ディレクトリ までのフルパスが入っていることがわかりました。

同様に $data にはファイルまでのフルパスです。

なるほど、これであればモジュールからファイルを参照できますね。

Zengin::Perl 鋭意改良・・・します

ということで、やりたかったことを実現する方法を見つけました。

空き時間になんとか良い感じにしていきたいですね。

あと、最近やっと Mouse の良さ的なものに気づいたので、Mouse 使ったオブジェクト指向にしていきたいなぁ・・・

なお、全銀データをモジュール内に持つ場合、モジュールを定期的にアップデートする必要が出てきますねぇ・・・さてどうするか。考えるの楽しいですね。