<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>はらぺこ日誌 &#187; Perl</title>
	<atom:link href="https://blog.harapeko.jp/tag/perl/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.harapeko.jp</link>
	<description>株式会社はらぺこ 公式ブログ</description>
	<lastBuildDate>Mon, 30 Oct 2017 14:32:56 +0000</lastBuildDate>
	<language>ja</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.2</generator>
	<item>
		<title>C++11用のクラスひな形生成スクリプトを作ってみた</title>
		<link>https://blog.harapeko.jp/2014/08/20/cppgen/</link>
		<comments>https://blog.harapeko.jp/2014/08/20/cppgen/#comments</comments>
		<pubDate>Wed, 20 Aug 2014 08:10:44 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[C++11]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[ツール]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=357</guid>
		<description><![CDATA[C++ で開発する上で、クラスを一つ一つ実装するたびに毎回同じようなことを書かされるのは冗長なので、だいたいこ [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>C++ で開発する上で、クラスを一つ一つ実装するたびに毎回同じようなことを書かされるのは冗長なので、だいたいこんな感じになるよねという範囲のひな形を生成するスクリプトを perl で記述してみました。</p>
<ul>
<li><a href="https://github.com/murachi/samples-for-blog/tree/master/perl/cppgen">cppgen.pl</a></li>
</ul>
<p><a href="http://search.cpan.org/~asw/Getopt-Compact-0.04/lib/Getopt/Compact.pm">Getopt::Compact</a> モジュールを使用しています。ご利用の際にはそちらも合わせてインストールしてあげてください。<br />
<span id="more-357"></span><br />
ツールの特徴としては以下のとおりです。</p>
<ul>
<li>名前空間とクラス名を指定すると、それらから類推される名前でファイル名や include guard シンボル名を設定し、ひな形を生成する。これらの指定は必須。
<ul>
<li>名前空間は 1階層にしか対応してません…。</li>
</ul>
</li>
<li>doxygen フォーマット (javadoc 風) でコメントを付加。
<ul>
<li>-s オプションでクラスの概要 (<code>@brief</code>) を指定可能。省略した場合はクラス名を使用。</li>
</ul>
</li>
<li>クラスの public 継承、仮想クラス、インタフェースクラスに対応。</li>
<li>Pimpl に対応 (構造体及びメンバ変数名は固定です…)。</li>
<li>Boost.noncopyable に対応。</li>
<li>コピーコンストラクタ、ムーブコンストラクタを自力実装するようにプレースホルダを設ける機能に対応。</li>
</ul>
<p>使い方ですが、例えば以下のようにすると、</p>
<pre>
$ perl cppgen.pl -c Observer -d hoge -s 'オブザーバーインタフェース' -in
$ perl cppgen.pl -c Subject -d hoge -s 'サブジェクト抽象クラス' -vpn
$ perl cppgen.pl -c ConsoleView -d hoge -s 'コンソールビュー抽象クラス' -e Subject -p
$ perl cppgen.pl -c StaticText -d hoge -s 'コンソールに静的にテキストを表示するビュークラス' -e ConsoleView -p
$ perl cppgen.pl -c DateTime -d hoge -s '日時を扱うクラス' -pCM
$ perl cppgen.pl -c WatchController -d hoge -s '時計コントローラ' -e Observer -p
$ perl cppgen.pl -c ConsoleManager -d hoge -s 'コンソール操作管理クラス' -pn
$ ls
console-manager.cpp  console-view.h  date-time.h      static-text.h  watch-controller.cpp
console-manager.h    cppgen.pl       observer.h       subject.cpp    watch-controller.h
console-view.cpp     date-time.cpp   static-text.cpp  subject.h
</pre>
<p>以下のような構成でファイルが生成されます。</p>
<ul>
<li>Observer クラス &#8230; noncopyable なインタフェースクラス (observer.h のみ)</li>
<li>Subject クラス &#8230; Pimpl を持つ noncopyable な抽象クラス (subject.h, subject.cpp)</li>
<li>ConsoleView クラス &#8230; Subject を継承し、 Pimpl を持つ抽象クラス (console-view.h, console-view.cpp)
<ul>
<li>他のクラスを継承するクラスは強制的に抽象クラスになります (デストラクタが virtual になる)。当たり前ですが…。</li>
</ul>
</li>
<li>StaticText クラス &#8230; ConsoleView クラスを継承し、 Pimpl を持つ抽象クラス (static-text.h, static-text.cpp)</li>
<li>DateTime クラス &#8230; Pimpl を持ち、コピーコンストラクタ、ムーブコンストラクタのプレースホルダを持つクラス (date-time.h, date-time.cpp)</li>
<li>WatchController クラス &#8230; Observer クラスを継承し、 Pimpl を持つ抽象クラス (watch-controller.h, watch-controller.cpp)</li>
<li>ConsoleManager クラス &#8230; Pimpl を持つ noncopyable なクラス (console-manager.h, console-manager.cpp)</li>
</ul>
<p>詳しくは <code>-h</code> オプションでヘルプを見るか、ソースを参照してください <tt>(^_^;</tt> 。</p>
<hr />
2014/8/22 &#8211; <strong>追記</strong><br />
テンプレートクラスに対応しました。</p>
<pre>
$ perl gencpp.pl -c DomNodeFromId -d hoge <strong style="color: red;">-t 'class NodeType'</strong> -ns 'ID を指定して DOM ノードを検索するラッパーオブジェクトクラス'
</pre>
<p>とかやると、以下の内容のヘッダファイル dom-node-from-id.h のみが生成されます。</p>
<pre>
/**
    @file   dom-node-from-id.h
    @brief  ID を指定して DOM ノードを検索するラッパーオブジェクトクラス定義  
*/
#ifndef HOGE_DOM_NODE_FROM_ID_H
#define HOGE_DOM_NODE_FROM_ID_H


namespace hoge {

/**
    @brief  ID を指定して DOM ノードを検索するラッパーオブジェクトクラス  
*/
template <strong style="color: red;">&lt;class NodeType&gt;</strong>
class DomNodeFromId {
    /// コピーコンストラクタ (使用禁止)
    DomNodeFromId(DomNodeFromId const&amp;) = delete;
    /// コピー代入演算子 (使用禁止)
    DomNodeFromId &amp; operator=(DomNodeFromId const&amp;) = delete;
    /// ムーブコンストラクタ (使用禁止)
    DomNodeFromId(DomNodeFromId &amp;&amp;) = delete;
    /// ムーブ代入演算子 (使用禁止)
    DomNodeFromId &amp; operator=(DomNodeFromId &amp;&amp;) = delete;

public:
    /**
        @brief  デフォルトコンストラクタ  
    */
    DomNodeFromId()
    {
        //TODO: デフォルト生成を行う処理の詳細をここに記述
    }
    /**
        @brief  デストラクタ  
    */
    ~DomNodeFromId() = default;
};

}   //namespace hoge

#endif  //HOGE_DOM_NODE_FROM_ID_H
</pre>
<p>見ての通り、 -n オプションと併用すると Boost.noncopyable を使用する代わりに、コピーやらムーブやらのあれこれを <code>= delete</code> しまくるコードが挿入されます。それから、デフォルトコンストラクタのプレースホルダはクラス定義内に確保され、デストラクタは <code>= default</code> で定義されます。</p>
<p>この他、 -CM オプションあたりが併用可能となっていますが、抽象クラスになるようなオプション (-vie) や、 Pimpl モード (-p) との併用はできません。</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2014/08/20/cppgen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perl の正規表現はリストコンテキストでキャプチャがないと (1) を返す</title>
		<link>https://blog.harapeko.jp/2011/09/01/perlre-list-nocapture/</link>
		<comments>https://blog.harapeko.jp/2011/09/01/perlre-list-nocapture/#comments</comments>
		<pubDate>Thu, 01 Sep 2011 01:20:24 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[正規表現]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=158</guid>
		<description><![CDATA[Perl で、 my @cap = $uri{$mkey} =~ /$ex_item-&#62;{match}{ [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Perl で、</p>
<pre>
my @cap = $uri{$mkey} =~ /$ex_item-&gt;{match}{$mkey}{pattern}/    or next EX_MATCH_LOOP;
@vars{@{$ex_item-&gt;{match}{$mkey}{vars}}} = @cap
    if exists $ex_item-&gt;{match}{$mkey}{vars} &amp;&amp; ref $ex_item-&gt;{match}{$mkey}{vars} eq 'ARRAY';
</pre>
<p>なんてコードを書いていて (何をやろうとしているのかは想像にお任せします… 別にプロプライエタリなコードじゃないし、いちいち擬似コード的に書き直すのも面倒なのでそのまま持って来ちゃいましたｗ)、ふと、ここでやっている正規表現のパターンにキャプチャが含まれていなかった場合、マッチするのに <code>or</code> の後に処理が流れてしまうことはないのかと疑問に思いました。<br />
<span id="more-158"></span><br />
結論としては、上記のコードに問題はないようです (まだテストしてないけど)。以下のように、スモールケースで試してみると、リストコンテキストではパターンにキャプチャがない場合でも空リストを返すのではなく、 <code>(1)</code>、すなわち値 <code>1</code> のみを持つリストを返してくれることが分かります。</p>
<pre>
$ perl -e '$x = "foo bar baz"; @s = $x =~ /bar/ or print "no match?\n"; print join(", ", @s), "\n"'
1
$ 
</pre>
<p>もちろん、キャプチャがある場合には、キャプチャリングされた値がリストとして返されます。</p>
<pre>
$ perl -e '$x = "foo bar baz"; @s = $x =~ /(b)(ar)/ or print "no match?\n"; print join(", ", @s), "\n"'
b, ar
$ 
</pre>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2011/09/01/perlre-list-nocapture/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perl モジュールと同じ場所にある設定ファイルを読み込む</title>
		<link>https://blog.harapeko.jp/2011/08/30/perl-inc-path/</link>
		<comments>https://blog.harapeko.jp/2011/08/30/perl-inc-path/#comments</comments>
		<pubDate>Tue, 30 Aug 2011 00:34:47 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=156</guid>
		<description><![CDATA[ブログがあまりにもほったらかしすぎなので、今後は極力些細な内容でも積極的にエントリを上げていこうと思います。  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>ブログがあまりにもほったらかしすぎなので、今後は極力些細な内容でも積極的にエントリを上げていこうと思います。</p>
<p>Perl での開発で、自分で作っているモジュールのファイルと同じ場所にあるファイルを設定ファイルとして読み込みたい場合があります。その設定ファイルも Perl モジュールとして書いてしまうのはやり方としては悪くないのですが、単なる定数値の羅列としてではなく、 YAML などで書きたくなるような若干複雑な構造のファイルもあるので、その場合、ファイルの場所を特定する方法が必要になります。<br />
<span id="more-156"></span><br />
読み込まれたモジュールファイルの場所は、 %INC 特殊変数に記録されています。キーに、モジュールのファイル名を指定すれば、そのファイルの場所を取得できます。</p>
<pre>
%perl -Mstrict -e 'print qq/$INC{"strict.pm"}\n/'
/usr/local/lib/perl5/5.8.9/strict.pm
%
</pre>
<p>モジュールファイルがカレントディレクトリにある場合、その値はキーと同じファイル名のみになるようです。</p>
<p>%INC はそのモジュールファイルの読み込みを開始する時点で既にセットされるようなので、自身のモジュールファイル内であれば、たとえ BEGIN 節であっても参照できます。</p>
<pre>
package Hoge;

BEGIN {
    print "$INC{'Hoge.pm'}\n";
}

1;
</pre>
<p>設定ファイルの場所を特定して読み込むのは、以下の要領で書けば実現できるでしょう。</p>
<pre>
package Hoge;
use strict;
use YAML;

BEGIN {
    my $conf_path = ($INC{'Hoge.pm'} =~ (m!^((?:.+/)?)Hoge.pm$!)[0] . '/conf.yml';
    my $yaml = LoadFile($conf_path);

    # ...処理...
}

# ...

1;
</pre>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2011/08/30/perl-inc-path/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CentOS5 で最新の DBD::mysql を導入する</title>
		<link>https://blog.harapeko.jp/2011/01/11/centos5-cpan-update/</link>
		<comments>https://blog.harapeko.jp/2011/01/11/centos5-cpan-update/#comments</comments>
		<pubDate>Tue, 11 Jan 2011 03:38:49 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[CentOS]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[サーバー管理]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=151</guid>
		<description><![CDATA[あけましておめでとうございます。またしてもブログがおざなりになってしまいました… orz 直接お金になる仕事で [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>あけましておめでとうございます。またしてもブログがおざなりになってしまいました… orz</p>
<p>直接お金になる仕事ではないのですが、ここしばらく、某所の BBS を開発するため、 Perl をいじっております。正直、 .NET や JavaScript 、ケータイアプリの開発なんてことばかりやっておりますと、たまにいじる Perl が楽しくて楽しくて、ついついこっちばかりやってしまう、という日々がしばらく続いております。いかんですな。</p>
<p>で、こちらの開発がとりあえずキリの良いところまで進んだので、一旦お蔵入りにしようと思うのですが (といっても年単位で寝かせるわけではないですが)、その前にこれに関連する技術的なメモをしたためておこうかと思うのであります。</p>
<p>作っているのは、さくらの共有レンタルサーバーサービス上で動作する CGI なのですが、レンタルサーバの性質上、 CPAN から最新のモジュールを引っ張ってくることが出来るので、割とモダンチックなスタイルで開発できるのが強みだったりします。</p>
<p>ただ、レンタルサーバ上で動作確認する場合、 500 エラーになってもエラーログをすぐには参照できないので (さくらにメールで問い合わせすることは可能らしいですが…)、弊社サーバー上にも設置し、動作確認するようにしました。</p>
<p>弊社サーバーはさくらの VPS を利用しており、 CentOS5 で動いています。基本的にはソフトウェアの導入は yum から行っているのですが、その場合、 DBD::mysql を始めとした多くの CPAN モジュールが、かなり古いバージョンで導入されることになります。特に、 <strong>DBD::mysql は 3.0007 と極めて古く、Unicode を扱うのに必要不可欠な接続時オプション mysql_enable_utf8 が使えない</strong>といった弊害がありました。他にも、 DateTime がタイムゾーンを理解できないなど、いろいろと弊害があったため、 CPAN から最新版をインストールしようかと試みたのですが、そのままではほとんどのモジュールについてコンパイルが通らないなど、cpan コマンドからではインストールが出来ませんでした。<br />
<span id="more-151"></span></p>
<h3>CPAN 自体を最新版に</h3>
<p>まず、cpan コマンドとして動作する Bundle::CPAN モジュール自体が古いので、それを最新にしてしまいましょう。</p>
<pre>
$ sudo cpan -i Bundle::CPAN
</pre>
<p>さらに、導入済みのモジュールを最新版に一括して更新してしまいましょう。こちらは cpan コマンドからではうまくいかなかったので、 perl から。</p>
<pre>
$ sudo perl -MCPAN -e 'CPAN::Shell-&gt;install(CPAN::Shell-&gt;r)'
</pre>
<h3>DBD::mysql のインストールには MySQL 自体のソースが必要</h3>
<p>ここまでやっても、DBD::mysql のインストールはやっぱり失敗していて、古いバージョンのままです。</p>
<pre>
$ perl -MDBD::mysql -e 'print "$DBD::mysql::VERSION\n"'
3.0007
$
</pre>
<p>結論から言うと、DBD::mysql を make する際に、MySQL 自体のヘッダファイルを参照しており、単に yum から MySQL だけを入れた状態ではこのヘッダファイルが導入されていないために、コンパイルに失敗してしまいます。そこで、 MySQL の開発用リソースを導入するパッケージ <strong>mysql-devel</strong> を事前に入れておく必要があります。</p>
<pre>
$ sudo yum install mysql-devel
$ sudo cpan -i DBD::mysql
</pre>
<p>これで、最新の DBD::mysql のインストールが成功するはずです。</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2011/01/11/centos5-cpan-update/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Windows で Boost セットアップ</title>
		<link>https://blog.harapeko.jp/2009/07/13/boost-setup-for-windows/</link>
		<comments>https://blog.harapeko.jp/2009/07/13/boost-setup-for-windows/#comments</comments>
		<pubDate>Mon, 13 Jul 2009 01:24:02 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[活動記録]]></category>
		<category><![CDATA[Boost]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[otoco]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[正規表現]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=85</guid>
		<description><![CDATA[Windows 環境で Boost ライブラリをセットアップしてみました。明日は Linux 環境でもセットア [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Windows 環境で Boost ライブラリをセットアップしてみました。明日は Linux 環境でもセットアップを行い、<a href="http://developer.harapeko.jp/trac/original/otoco/wiki/%E6%8A%80%E8%A1%93%E3%83%A1%E3%83%A2/Boost%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97">技術メモ</a>としてまとめる<a href="http://developer.harapeko.jp/trac/original/otoco/ticket/3">予定</a>です。</p>
<p>で、せっかくなので、試しに簡単なサンプルを作って動作確認をしてみました。</p>
<pre>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;

#include &lt;boost/regex.hpp&gt;

using namespace std;
using namespace boost;

void escapeHtml(const string &amp;src, string &amp;modified);

int main(int argc, char* argv[])
{
	for (int i = 1; i &lt; argc; i++)
	{
		ifstream fin(argv[i]);
		if (fin.bad() || fin.fail())
		{
			cerr &lt;&lt; argv[0] &lt;&lt; &quot;: Can't open &quot; &lt;&lt; argv[i] &lt;&lt; &quot;.&quot; &lt;&lt; endl;
			continue;
		}
		while (!fin.eof())
		{
			string line;
			getline(fin, line);
			string modified;
			escapeHtml(line, modified);
			cout &lt;&lt; modified &lt;&lt; endl;
		}
	}
	
	return 0;
}

void escapeHtml(const string &amp;src, string &amp;modified)
{
	modified.clear();
	sregex_iterator last_it;
	for (sregex_iterator it(src.begin(), src.end(), regex(&quot;[&lt;&gt;&amp;\&quot;]&quot;)); it != sregex_iterator(); it++)
	{
		modified += it-&gt;prefix().str();
		string sub = (*it)[0].str();
		switch (sub[0])
		{
			case '&lt;':	modified += &quot;&amp;lt;&quot;;		break;
			case '&gt;':	modified += &quot;&amp;gt;&quot;;		break;
			case '&amp;':	modified += &quot;&amp;amp;&quot;;	break;
			case '&quot;':	modified += &quot;&amp;quot;&quot;;	break;
			default:	assert(0);	break;
		}
		last_it = it;
	}
	modified += last_it == sregex_iterator() ? src : last_it-&gt;suffix().str();
}
</pre>
<p><a href="http://boost.cppll.jp/HEAD/libs/regex/doc/regex_iterator.html">Boost.Regex の regex_iterator</a> を用いたテストです。よーするに、コマンドに指定したファイルの HTML エスケープ処理を施してコンソールに書き出すプログラムです。</p>
<p>上記のプログラムを test.cpp などのファイル名で保存し、そのまま Visual Studio 2008 コマンドプロンプト上で</p>
<pre>
cl test.cpp
</pre>
<p>とやってみましたが、これだけではコンパイルできませんでした。どうやら、 Visual Studio のオプションダイアログでヘッダーファイルやライブラリの参照先ディレクトリを設定しても、その設定が参照されるのは Visual Studio の IDE からビルドを実行した場合のみのようです。あるいは、環境変数 INCLUDE や LIB を設定してあげればこれでコンパイルできるのかも知れません。その辺はまた追々調べてみますが、最終的には <code>.configure</code> に <code>--boost-prefix</code> といったようなオプションを設ける、といった形の対応になるのではないかとも思います。</p>
<p>ちなみに、これと同等の (しかもより確実に動作する) プログラムを Perl で記述すると、以下の通りになります (この書き方はしかし Unix 風環境限定ですが)。</p>
<pre>
#!/usr/bin/perl -p
s/[&lt;&gt;&amp;&quot;]/'&amp;'.{qw(&lt; lt &gt; gt &amp; amp &quot; quot)}->{$&amp;}.';'/eg;
</pre>
<p>うあー、やっぱりスクリプト言語は便利だなー (汗。</p>
<hr />
<div>2009年  7月 28日 火曜日 16:56:07 JST &#8211; <strong>追記</strong></div>
<p>egtra 様、ご指摘感謝です。仰る通り、 <code>string::getline()</code> を使った方がスマートなので、その通りに修正させて頂きました。</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2009/07/13/boost-setup-for-windows/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>mod_perl であほプログラムを作ってみた。</title>
		<link>https://blog.harapeko.jp/2008/12/29/mod-perl-aho/</link>
		<comments>https://blog.harapeko.jp/2008/12/29/mod-perl-aho/#comments</comments>
		<pubDate>Mon, 29 Dec 2008 12:41:34 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[mod_perl]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=21</guid>
		<description><![CDATA[はじめに 自社開発で作ろうとしていた物が実は mod_perl に対応した Web アプリなのですが、そういえ [&#8230;]]]></description>
				<content:encoded><![CDATA[<h3>はじめに</h3>
<p>自社開発で作ろうとしていた物が実は mod_perl に対応した Web アプリなのですが、そういえば mod_perl ってまともに弄ったこと無かったなぁとか思ったので、風邪も引いて頭も回らないことだし少し脱線するかとばかりに遊んでみました。</p>
<p>世間一般においてはちっともはやらない mod_perl 。かろうじて拾う話題もせいぜい Apache::Registry を用いた既存 CGI の高速化とかその程度の話で、本格的に Perl で Apache モジュールを作ろうみたいな話になると本を買うか英語で情報を探すしかなかったりするのが現状のようです (あるいはおいらの探し方が悪いのかも知れませんが…)。参考にしたのは mod_perl の公式サイトにあるドキュメントでした。</p>
<ul>
<li><a href="http://perl.apache.org/docs/2.0/index.html">mod_perl: mod_perl 2.0 Documentation</a></li>
</ul>
<p>これ全体をぼえ～っと眺めてみても何から手をつければいいのかよくわからないのですが、<a href="http://perl.apache.org/docs/2.0/user/config/config.html#Examples" title="mod_perl: mod_perl 2.0 Server Configuration#Examples">この辺</a>を見てみるとサンプルを見つけることができたので、これを参考に作ってみることにします。</p>
<p><span id="more-21"></span></p>
<h3>動かす場所 (URI) を決める</h3>
<p>とりあえず弊社の環境では test という名のサブドメインを設け、 <code>http://test.harapeko.jp/aho</code> にアクセスするとプログラムが実行できるということにしてみました。もちろん、サブドメインを設けるには、named (BIND) の設定追加と、 Apache 側の設定が必要です。</p>
<p>サブドメインとか面倒な人は、この辺は読み飛ばしちゃってください。BIND の設定については、ここでは省きます。</p>
<p>Apache 側の設定ですが、あとでここに mod_perl に関する設定も書き加えるので、 <code>mod_perl.c</code> モジュールが見える場所に記述するようにしてください (<code>mod_perl.c</code> を <code>LoadModule</code> している特別な設定ファイルがあるのであれば、その中に記述します)。</p>
<pre>
&lt;IfModule mod_perl.c&gt;

&lt;VirtualHost *:80&gt;
  ServerName test.harapeko.jp
  DocumentRoot /var/www/vhosts/test/htdocs
  ErrorLog logs/test-error_log
  CustomLog logs/test-access_log common
&lt;/VirtualHost&gt;

&lt;/IfModule&gt;
</pre>
<h3>mod_perl モジュールとしてあほプログラムを書く</h3>
<p>mod_perl モジュールとしてロードできるのは、<code>@INC</code> のパスが通ったディレクトリに置いてあるモジュールファイルだけです。つまり、通常は Cpan などから正規にインストールしたモジュールだけです。</p>
<p>ただ、 mod_perl の実行中は Apache の設定で <code>ServerRoot</code> に設定されているディレクトリにも <code>@INC</code> のパスが通るようなので、テスト目的でちょっとしたプログラムを書きたいだけならば、この配下にファイルを置いていけばいいでしょう。弊社の環境では <code>/etc/httpd</code> が <code>ServerRoot</code> に設定されているので、例えばモジュールのパッケージ名を <code>MpTest::AhoAho</code> にするのであれば、モジュールファイルは <code>/etc/httpd/MpTest/AhoAho.pm</code> とします。</p>
<p>以下、そのサンプルコードです。</p>
<pre>
package MpTest::AhoAho;

use strict;
use utf8;

use Encode;
use Apache2::RequestRec ();
use Apache2::RequestIO ();
use Apache2::Const -compile =&gt; qw( :common :http );

my $urlEncode;

sub handler {
	my $r = shift;
	$r-&gt;content_type( 'text/html; charset=UTF-8' );
	if ( $r-&gt;method eq 'GET' )
	{
		$r-&gt;print( encode( 'UTF-8', &lt;&lt;ENDLINE ) );
&lt;html&gt;&lt;head&gt;&lt;title&gt;あほあほ!!&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;あほあほ!!&lt;/h1&gt;
&lt;form method=&quot;POST&quot; action=&quot;${ \ $r-&gt;uri }&quot;&gt;
&lt;p&gt;どう思いますか? &amp;gt;&lt;input type=&quot;text&quot; name=&quot;hoge&quot; size=&quot;50&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;送信&quot;&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
ENDLINE
	}
	elsif ( $r-&gt;method eq 'POST' )
	{
		my $length = $r-&gt;headers_in-&gt;{ 'Content-Length' };
		if ( $length &gt; 1024 ){
			$r-&gt;status( Apache2::Const::HTTP_BAD_REQUEST );
			$r-&gt;print( encode( 'UTF-8', &lt;&lt;ENDLINE ) );
&lt;html&gt;&lt;head&gt;&lt;title&gt;話が長すぎじゃばっかも～～～ん!!!&lt;/title&gt;
&lt;body&gt;
&lt;h1&gt;話が長すぎじゃばっかも～～～ん!!!&lt;/h1&gt;
&lt;p&gt;1KB 以内に納めて来い!!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href=&quot;${ \ $r-&gt;uri }&quot;&gt;もう一回?&lt;/a&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
ENDLINE
			return Apache2::Const::OK;
		}
		my $buf;
		$r-&gt;read( $buf, $length ) == $length	or die '???';
		my $request = $urlEncode-&gt;( $buf );
		my $comment = &quot;$request-&gt;{ hoge } としか言えない貴様はやっぱりあほだ!!!&quot;;
		$r-&gt;print( encode( 'UTF-8', &lt;&lt;ENDLINE ) );
&lt;html&gt;&lt;head&gt;&lt;title&gt;あほあほあほ!!!&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;$comment&lt;/h1&gt;

&lt;p&gt;
ENDLINE
		for ( 1 .. 1000 )
		{
			$r-&gt;print( encode( 'UTF-8', &quot;$comment &quot; ) );
		}
		$r-&gt;print( encode( 'UTF-8', &lt;&lt;ENDLINE ) );
&lt;/p&gt;

&lt;hr&gt;
&lt;p&gt;&lt;a href=&quot;${ \ $r-&gt;uri }&quot;&gt;もう一回?&lt;/a&gt;&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;
ENDLINE
	}
	return Apache2::Const::OK;
}

$urlEncode = sub {
	my $url = shift;
	my %map;
	for my $pair ( split /&amp;/, $url )
	{
		my ( $name, $val ) = split /=/, $pair;
		$name =~ tr/+/ /;
		$name =~ s/%([\da-f][\da-f])/pack 'H2', $1/ieg;
		$name = decode( 'UTF-8', $name );
		$val =~ tr/+/ /;
		$val =~ s/%([\da-f][\da-f])/pack 'H2', $1/ieg;
		$val = decode( 'UTF-8', $val );
		$map{ $name } = $val;
	}
	return \%map;
};

1;
__END__
</pre>
<h3>URI アクセスに応じてモジュールが動くように設定する</h3>
<p>プログラムができたら、それを URI に関連づけます。先の手順でサブドメインの追加を行っていれば、その <code>&lt;VertualHost&gt;</code> ディレクティブの中に、以下のように設定を追加します。</p>
<pre>
&lt;VirtualHost *:80&gt;
  ServerName test.harapeko.jp
  DocumentRoot /var/www/vhosts/test/htdocs
  ErrorLog logs/test-error_log
  CustomLog logs/test-access_log common

  # 追加 --ここから
  PerlModule APR::Table  # 要らないかも。後述
  PerlModule MpTest::AhoAho
  &lt;Location /aho&gt;
    SetHandler modperl
    PerlResponseHandler MpTest::AhoAho
  &lt;/Location&gt;
  # 追加 --ここまで
&lt;/VirtualHost&gt;
</pre>
<p>これで設定完了です。 <code>apache2ctl configtest</code> (または <code>/etc/init.d/httpd configtest</code> 等) で設定ファイルの文法を確認したら、 <code>apache2ctl restart</code> (または <code>/etc/init.d/httpd restart</code> 等) で再起動しましょう。</p>
<h3>実際に動いている物サンプル</h3>
<p><a href="http://test.harapeko.jp/aho" title="あほあほ!!">実際に動いているサンプルです</a>。もうなんて言うか<strong>激しくごめんなさい</strong>。 orz</p>
<h3>解説</h3>
<p>それでは解説です。まず、 Apache 側の設定ファイル中でキモになる記述は以下の部分です。</p>
<pre>
  PerlModule MpTest::AhoAho
  &lt;Location /aho&gt;
    SetHandler modperl
    PerlResponseHandler MpTest::AhoAho
  &lt;/Location&gt;
</pre>
<p><code>PerlModule</code> はモジュールをロードする宣言で、この行は実は無くても動きます。が、書いておくと Apache の起動時にロードしてコンパイルまで済ませておいてくれるので、多分早くなるんだと思います (多分ってなんだよ＞ヲレ)。</p>
<p><code>PerlResponseHandler</code> は、Apache がクライアントにレスポンスを返す際に呼び出すハンドラモジュールを指定する宣言です。つまり、大雑把に言うと、ここで指定したモジュールの <code>handler</code> メソッドが、 URI アクセス時に呼び出されます。上記のサンプルではこの宣言は <code>&lt;Location /aho&gt;</code> ディレクティブ内に記述されているので、「<code>/aho</code> の URI にアクセスしてきた場合は、応答時に <code>MpTest::AhoAho-&gt;handler</code> を実行する」という意味になります。</p>
<p><code>SetHandler</code> は <code>PerlResponseHandler</code> などのハンドラを指定する宣言を書く場所では必ず書いておく必要のある宣言です。引数は <code>modperl</code> と <code>perl-script</code> の 2種類があって、後者はレガシーなスクリプトとの互換性を重視するためか、プログラム中でそのまま使える <code>print</code> 関数や <code>%ENV</code> などにアクセスできるようになります。今回は敢えてそれができない <code>modperl</code> の方を選んでみました。</p>
<p>次にプログラムですが、内容的には <code>GET</code> アクセス時は質問ページを、 <code>POST</code> アクセス時は罵倒ページを表示するというただそれだけの物です。但し POST したデータが長すぎる (1KB を超える) 場合には自前でエラーを表示するようにしています (ちゃんと <code>400 Bad Request</code> を返しているので、IE で試すと IE 謹製のエラー画面が表示されるはずです)。</p>
<p><code>use Apache2::*</code> シリーズはいずれもほぼ必須と考えていいと思います。<code>Apache2::RequestRec</code> と <code>Apache2::RequestIO</code> は、ハンドラ内で最初に受け取るリクエストオブジェクトの各メソッドにアクセスできるようにするためのモジュールで、前者はステータス値や HTTP ヘッダー、環境変数などへのアクセス用、後者はコンテンツの入出力用です。例えば <code>$r-&gt;method</code> や <code>$r-&gt;uri</code>、 <code>$r-&gt;headers_in</code> などを参照したり、<code>$r-&gt;content_type</code> で出力用の <code>Content-type:</code> ヘッダーを設定したりするには <code>Apache2::RequestRec</code> が、 <code>$r-&gt;print</code> でコンテンツを出力したり、 <code>$r-&gt;read</code> で POST を入力したりするには <code>Apache2::RequestIO</code> がそれぞれ必要になります。</p>
<p><code>Apache2::Const</code> モジュールは各種定数の定義です。引数リストで使用する定数を宣言できますが、その最初に <code>'-compile'</code> と入れておくと、定数名をグローバル名前空間に展開しません (その場合は、 <code>Apache2::Const::<em>HOGE_FUGA</em></code> としてアクセスします)。</p>
<p>Apache はこのモジュールの <code>handler</code> 関数を呼び出し、最初の引数にリクエストオブジェクトを渡します。そして、自前の処理を実行しつつ、リクエストオブジェクトの各メソッドを適切に呼んであげてから、 <code>Apache2::Const::OK</code> を返してあげればいい、という流れです。あとは、<a href="http://perl.apache.org/docs/2.0/api/index.html" title="mod_perl: mod_perl 2.0 API">API のリファレンス</a>とにらめっこしつつ読みこなしていただければ、やっていることは大体解ると思います。</p>
<h3><code>$r-&gt;headers_in</code> メソッド呼び出しでエラーが発生する場合</h3>
<p>ところで、 Apache 側の設定で、以下の行について説明していませんでしたが、</p>
<pre>
  PerlModule APR::Table  # 要らないかも。後述
</pre>
<p>この宣言は、 <code>$r-&gt;headers_in</code> メソッドにアクセスした際、以下のようなエラーが発生してしまうのを回避するためのものです。自宅で宅内サーバーにしているマシン@debian では発生しなかったのですが、会社サーバー@centos では発生するので、念のため付記しておきます。</p>
<pre>
[Mon Dec 29 18:47:51 2008] [error] [client 218.219.204.253] Can't locate object
method "FETCH" via package "APR::Table" at /etc/httpd/MpTest/AhoAho.pm line 32.\
n, referer: http://test.harapeko.jp/aho
</pre>
<p>この解決法については、以下のサイトを参考にしました。</p>
<ul>
<li><a href="http://mail-archives.apache.org/mod_mbox/perl-modperl/200409.mbox/%3CPine.LNX.4.44.0409211243080.6717-100000@cartman.nederhost.net%3E">Re: [mp2] APR::Table FETCH method not found (reported when using $r-&gt;headers_in-&gt;{Cookie}</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2008/12/29/mod-perl-aho/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
