ラベル Perl の投稿を表示しています。 すべての投稿を表示
ラベル Perl の投稿を表示しています。 すべての投稿を表示

2009/10/04

Catalystアプリと設定ファイルを共通化

作ろうとしているやつがちょうどCatalystで作り直そうかと思っていたのでひとまず、設定ファイルをCatalystから切り離して、Catalyst以外のアプリからも読み込めるようにしました。

まず、作成するアプリは下記のようなライブラリ構成です。

  • MyApp::API   アプリケーションのロジック部です

  • MyApp::Web   Catalyst関連のものです

  • MyApp::Utils  ユーティリティ。Catalystから切り離す。

  • MyApp::Config  設定ファイルに関するクラス

  • MyApp::Log   ログを出力するクラス



ともあれ、catalyst.plでひな形を作ります。
$ catalyst.pl MyApp::Web


まずlib/MyApp/Config.pmを下記のように作成します。
package MyApp::Config;

use strict;
use warnings;
use Config::Multi;
use MyApp::Utils;
use base 'Class::Singleton';

our $FILES ;
sub _new_instance {
my $cm = Config::Multi->new(
{
dir => MyApp::Utils::path_to('conf')->stringify ,
app_name => 'myapp' ,
extension => 'yml'
});
my $config = $cm->load();
$FILES = $cm->files;
return $config;
}
sub files {
return $FILES;
}

1;

参考サイトのコピペです。すいませんすいません。
ここで設定したapp_nameとextensionが設定ファイル名に使用されます。
上記設定だとmyapp_web.ymlになります。

次にMyApp::Utils。
package MyApp::Utils;

use warnings;
use strict;
use Path::Class::Dir;
use Path::Class::File;
use FindBin;

sub home {
return $ENV{MYAPP_HOME} || Path::Class::Dir->new( $FindBin::Bin, './../' );
}

sub path_to {
my ( @path ) = @_;
my $path = Path::Class::Dir->new( &home , @path );
warn $path;
if ( -d $path ) { return $path }
else { return Path::Class::File->new( &home, @path ) }
}

1;

これも参考サイトのコピペです。すいませんすいません。
このメソッド、ログのところでも少し使ったので今後もたぶん使う&拡張することになるかと思います。

つぎに、Config用のCatalystプラグインです。
MyApp/Pluginディレクトリを作成して、Config.pmファイルをつくります。
Catalyst 5.8でやったのでNEXTじゃないやつ(名前なんだっけ?)を使いました。
package MyApp::Plugin::Config;

use strict;
use warnings;
use MyApp::Config;
use MRO::Compat;

our $VERSION ='0.01';

sub setup {
my $c = shift;
my $config = MyApp::Config->instance();

if( $c->debug ) {
my $files = MyApp::Config->files();
for my $file ( @{$files} ) {
$c->log->debug( 'Load Config ' . $file );
}
}

$c->config( $config ) ;
$c->next::method( @_ );
}

1;


そしてCatalystアプリ本体のlib/MyApp/Web.pmに下記を追加します。
our $VERSION = '0.01';
use Catalyst qw/+MyApp::Plugin::Config/;


設定ファイルをconf/myapp_web.ymlに作成します。
---
test: hogehoge


これでCatalystから設定ファイルの内容が拾えればOKです。
lib/MyApp/Web/Controller/Root.pmを下記のように修正して、テストサーバーを上げて画面にhogehogeと表示されれば成功です。
sub index :Path :Args(0) {
my ( $self, $c ) = @_;

# Hello World
$c->response->body($c->config->{test});
}

sub default :Path {
my ( $self, $c ) = @_;
$c->response->body( 'Page not found' );
$c->response->status(404);
}


Catalyst以外からも下記のように利用する事ができます。
ちなみにbin/test.plのような名前で作成しました。
#!/usr/bin/perl
use strict;
use warnings;
use FindBin;

use lib($FindBin::Bin . '/../lib');

use MyApp::Log;

my $config = NaNaCa::Config->instance();
print "$config->{test}\n";


これでひとまずConfigをCatalystから分離することができました。
理解しきれていないこと(特に初めて使ったクラス群)が多いので、ほとんど参考サイトをコピペさせてもらいました。

ログもLog4perlで出力しつつ、Catalystから切り離そうとしたんですがやっぱり同じようにSingletonにした方が良いもんなんでしょうか。
特に多くを望んでいないので、Catalyst::Log::Log4perlという便利なものを使い、同じ設定ファイルを読ませてアプリの方はLog4perlを使うって方法はどうだろうと思っています。
次回はLogについて書きたいと思います。

参考サイト

2009/04/13

XSからlibcのputs関数を呼び出す

前回はCのコードをxsコードに書いて、それを実行するということをやりました。
今回は外部のCライブラリを呼び出してみたいと思います。
一番簡単そうな例として、libcのputs関数を呼び出してみます。

まずはXSライブラリのひな形を作成します。
$ h2xs -A -n libcXS
perl Makefile.PL


次にXSソース(libcXS.xs)を編集します。
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#include "stdlib.h"

MODULE = libcXS PACKAGE = libcXS

void
puts(value)
char * value

追記したのは、include部にstdlib.hを追加したのと前回も追加した関数の戻り値などの定義部分です。
前回のようCODEセクションを定義してコードを書かなくても、既存のライブラリ呼び出しの場合は定義だけでOKです。

最後にmakeして完了です。

実際にこのモジュールを使ったPerlコードを使った例が次のものです。
標準出力にhogehogeと表示されるはずです。
#!/usr/bin/perl

use strict;
use ExtUtils::testlib;
use libcXS;

libcXS::puts("hogehoge");


だんだんわかってきたような気がしてきていますが、きちんと理解するためにはもう少し時間がかかりそうです。
ひとまず、perldoc perlxsとモダンPerl入門を改めて読んでみます。

----
追記:
なぜか、#include "stdlib.h"を消しても動きます…
前段階でincludeされてるのかなぁ、それとも別の原因…?
今度は違うライブラリでやってみたいと思います。

2009/04/07

XSを使ってみる

PerlからC APIを呼ぶ仕組みでXSというものがあります。
これにより、Cで書いた部分の処理に関しては高速になったり、既存のCで書かれたライブラリが使用できるようになるらしいです。
ちょっとおもしろそうなので、練習に動かしてみました。

モダンPerl入門に詳しく書いてあったのですが、ちょっとやることが多すぎたので下記のサイトの例をやってみました。
http://d.hatena.ne.jp/higepon/20050615/1118829090

手順は上記のサイトに書いてある通りに行いました。
$ h2xs -A -n MyTestXS ←XSのひな形を作成
$ cd MyTestXS/
$ perl Makefile.PL ← Makefileを作成
[MyTestXS.xsを編集]
$ make


MyTestXS.xsは次のように編集しました。
ちなみにMODULE = MyTestXSまでは自動的に作成されてました。
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"


MODULE = MyTestXS PACKAGE = MyTestXS

int
increment2(value)
int value
CODE:
RETVAL = value + 2;
OUTPUT:
RETVAL

追記した部分は、まず戻り値の型、関数名、引数名と型を指定します。
CODEより後ろには実際のCのコードを記述します。
OUTPUTに戻り値を指定します。

makeして失敗がなければ、XSモジュール作成完了です。
では、実際に使ってみつつ、ベンチマークもやってみました。
下記のようなコードを書きました。これも参考サイトと同じです。
#!/usr/bin/perl
use strict;

use ExtUtils::testlib;
use MyTestXS;

use Benchmark;

my $count = 5000000;
timethese($count,
{
'XS' => 'xs_test();',
'Perl' => 'perl_test();',
});

sub xs_test {
MyTestXS::increment2(100);
}

sub perl_test {
increment2(100);
}

sub increment2 {
$_[0] + 2;
}

これを実際に私のMacBookで実行してみると下記のような結果になりました。
macbook:MyTestXS hiroki$ perl ./test.pl 
Benchmark: timing 5000000 iterations of Perl, XS...
Perl: 5 wallclock secs ( 5.95 usr + 0.08 sys = 6.03 CPU) @ 829187.40/s (n=5000000)
XS: 3 wallclock secs ( 3.46 usr + 0.04 sys = 3.50 CPU) @ 1428571.43/s (n=5000000)

XSで書いた方が2倍近くパフォーマンスが良いです。

私としては、はやく動かすというよりも既存のCライブラリをPerlから使いたいと思ってるので、引き続き調べます。
モダンPerl入門を見ると詳細に書いてあるようで。

2009/02/17

モダンPerl入門 デザインパターン実装の練習 -Iterator-

モダンPerl入門の2章では、各種デザインパターンをPerlで実装した例が出ています。
あまりデザインパターンが得意ではないので、頭の整理と練習がてら自分で書いてみたいと思います。
今日はIterator。

Iteratorは何かデータを順番に取り出していく処理を抽象化するためのパターンです。
何かのデータとは、配列であったりファイルであったり、データベースであったりという感じです。

何かの連続したデータをひとつずつ取り出すときはIteratorを使った方が良さそうということなようです。
例えば、従来は配列で持っていたデータをある日ファイルに持つようになりましたと。
そんなとき、Iteratorを使っていない場合は該当箇所をすべて直す必要があります。
しかし、Iteratorを使っていればデータを取り出すという処理が抽象化されているので、使用するオブジェクトを変更するだけで良いのです。
よかったね、よかったね、ということになると。
# あってるか不安…


書籍の例では配列とリンクリスト(Data::LinkedListという仮想のモジュールを使用)、ファイル用のIteratorを実装していました。
ひとまず、軽く実践できそうな配列とファイル(File::IOを使用)の実装をしてみました。

package MyIterator;
use Moose::Role;

requires qw(next);

no Moose::Role;

package MyIterator::Array;
use Moose;
use MooseX::AttributeHelpers;

with 'MyIterator';

has 'array' => (
metaclass => 'Collection::Array',
is => 'rw',
isa => 'ArrayRef',
required => '1',
trigger => sub {
my ($self, $list) = @_;
$self->size(scalar @$list);
},
provides => {
get => 'array_get'
},
);

has 'size' => (
is => 'rw',
isa => 'Int',
default => 0,
);

has 'current' => (
metaclass => 'Number',
is => 'rw',
isa => 'Int',
default => 0,
provides => {
add => 'current_add'
}
);

__PACKAGE__->meta->make_immutable;

no Moose;

sub next {
my $self = shift;
if ($self->current >= $self->size){
return ();
}

my $i = $self->current;
$self->current_add(1);
return $self->array_get($i);
}

1;

package MyIterator::File;
use Moose;

with 'MyIterator';

has 'filepath' => (
is => 'rw',
isa => 'Str',
required => 1,
);

has 'fh' => (
is => 'rw',
);

__PACKAGE__->meta->make_immutable;

no Moose;

use IO::File;

sub BUILD {
my $self = shift;

my $io = IO::File->new();
$io->open($self->filepath(), 'r') or die $!;
$self->fh($io);

return $self;
}

sub DESTROY{
my $self = shift;
$self->fh()->close()
}

sub next {
my $self = shift;

my $fh = $self->fh();
my $line = <$fh>;

return $line ? $line : ();
}

1;

package main;

my $iterator = MyIterator::Array->new(array => ['a', 'b', 'c']);
while (my $result = $iterator->next()) {
chomp $result;
print "$result\n";
}

$iterator = MyIterator::File->new(filepath => '/Users/hiroki/Documents/perl/iterator.pl');
while (my $result = $iterator->next()) {
chomp $result;
print "$result\n";
}


MyIteratorが今回作成するIteratorのインターフェイス。
インターフェイスはMoose::Roleを使うと簡単に実現できます。
今回はnextメソッドを必ず実装するようにしました。

MyIterator::Arrayが配列用のIterator。
データが入った配列を格納するarray、配列のサイズを格納するsize、読み込み中の現在の位置を表すcurrentのプロパティがあります。
arrayプロパティのtriggerで1回だけsizeの初期化を行うようになっています。
あとはnextメソッドが実装されています。
なるほどー、MooseとMooseX::AttributeHelpersを使うとこんなに便利なのか。

MyIterator::Fileがファイル用のIterator。
これは自分で書いた。
まず、ファイルハンドルの初期化をBUILDメソッドで行いました。
BUILDメソッドはコンストラクタで初期化された後に呼び出されるので、プロパティなどに値が入っているのは保証されています(required => 1にした場合)。
あとはnextメソッドを実装しました。
returnのところが三項演算子を使っていてちょっとトリッキーですね(これは本に書いてあった)。

で、package main以降が実際に走るコード。
最初は引数で渡した配列を順に表示してる処理です。
次が、ファイルから1行ずつ読み込んで表示する処理です。
どちらもオブジェクト作成の方法は違いますが、値を取り出す処理は一緒です。
なので、$iteratorにはMyIteratorを実装したものが入っていれば同じ処理で値が取り出せます。

次回以降はまた違うデザインパターンを実装してみます。

2009/02/15

MacPortsでMooseをインストール

昨日頼んでいた「モダンPerl入門」が届いたので早速読み進めてみました。
目次を読んだだけでも、最近のPerl関連の書籍にはないような内容が満載。
最近Catalystのことをネットで探しても若干古い情報しかなかったし…。
2章のPerlでデザパタの実装例を紹介しているのは、デザパタの理解が明らかに足りない私にとってはとてもうれしい。
とりあえず、1章と3章と4章を読んでみた。

1章でMooseが紹介されてますね。
名前だけは知ってたんですが、特に放置しててどのようなものかはわからなくて…orz
だいぶ前に流行が過ぎてて今更感はあるかもしれないですが、とりあえず使ってみることにしました。

そこでMacPortsにあったのでインストールしてみたのですが、ちょっと一筋縄ではいかなかったです。
きちんと記録をとってなかったのでちゃんと書けないのですが、ひとまずやったことは次のようなことです。
  • Perlを5.8.8から5.8.9に
  • p5-scalar-list-utilsを強制インストール


最初にはまったのはp5-test-simpleでなぜかエラーが出て先に進みませんでした。
私の環境ではPerl5.8が5.8.8_3がactiveになっていたので5.8.9_2に変更したところp5-test-simpleがインストールできました。


$ port installed | grep perl
perl5.8 @5.8.8_3+darwin_9 (active)
perl5.8 @5.8.9_2
$ sudo port deactivate perl5.8
---> Deactivating perl5.8
$ sudo port install perl5.8
---> Activating perl5.8 @5.8.9_2
---> Cleaning perl5.8
$ port installed | grep perl
perl5.8 @5.8.8_3+darwin_9
perl5.8 @5.8.9_2 (active)
$ sudo port install p5-test-simple
---> Activating p5-test-simple @0.86_0
---> Cleaning p5-test-simple


次にp5-scalar-list-utilsのインストールでなぜかこけました。
deactivateしてactivateしてみてもどうにもならなかったので-fオプションをつけて強制インストールしてしまいました。
どうやらwarningが出ているようですが、already existsということだけみたいでした。

sudo port -f install p5-scalar-list-utils
---> Installing p5-scalar-list-utils @1.19_1
---> Activating p5-scalar-list-utils @1.19_1
Warning: File /opt/local/lib/perl5/5.8.9/darwin-2level/auto/List/Util/Util.bs already exists. Moving to: /opt/local/lib/perl5/5.8.9/darwin-2level/auto/List/Util/Util.bs.mp_1234672795.
Warning: File /opt/local/lib/perl5/5.8.9/darwin-2level/auto/List/Util/Util.bundle already exists. Moving to: /opt/local/lib/perl5/5.8.9/darwin-2level/auto/List/Util/Util.bundle.mp_1234672795.
Warning: File /opt/local/lib/perl5/5.8.9/darwin-2level/List/Util.pm already exists. Moving to: /opt/local/lib/perl5/5.8.9/darwin-2level/List/Util.pm.mp_1234672795.
Warning: File /opt/local/lib/perl5/5.8.9/darwin-2level/Scalar/Util.pm already exists. Moving to: /opt/local/lib/perl5/5.8.9/darwin-2level/Scalar/Util.pm.mp_1234672795.
Warning: File /opt/local/share/man/man3/List::Util.3pm.gz already exists. Moving to: /opt/local/share/man/man3/List::Util.3pm.gz.mp_1234672795.
Warning: File /opt/local/share/man/man3/Scalar::Util.3pm.gz already exists. Moving to: /opt/local/share/man/man3/Scalar::Util.3pm.gz.mp_1234672795.
---> Cleaning p5-scalar-list-utils


これであとはsudo port install p5-mooseすれば無事インストールできます。
インストールだけでも少し時間がかかってしまいました。

いつも思うのですが、このブログは環境構築ものの記事ばっかりでその先がないですね…
反省ですね。