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

正規表現でテンプレートを書き換えるModule::SetupのPlugin

catstarterと同じような事をやりたかったのでpatchを作ってみました。ちょっとテストしてないのであれですが、ひとまず第一弾ということで。

Index: lib/Module/Setup/Distribute.pm
===================================================================
--- lib/Module/Setup/Distribute.pm	(revision 22075)
+++ lib/Module/Setup/Distribute.pm	(working copy)
@@ -67,8 +67,8 @@
         $context->call_trigger( template_process => $options );
         $options->{template} = delete $options->{content} unless $options->{template};
     }
-    $options->{dist_path} =~ s/____var-(.+?)-var____/$options->{vars}->{$1} || $options->{vars}->{config}->{$1}/eg;
     $context->call_trigger( replace_distribute_path => $options );
+    $self->replace_dist_path($context, $options);
 
     if ($is_dir) {
         $options->{dist_path} = Module::Setup::Path::Dir->new($options->{dist_path});
@@ -81,4 +81,12 @@
     $is_dir ? $options->{dist_path}->mkpath : $context->write_file($options);
 }
 
+sub replace_dist_path {
+    my($self, $context, $options) = @_;
+    $options->{dist_path} =~ s/____var-(.+?)-var____/$options->{vars}->{$1} || $options->{vars}->{config}->{$1}/eg;
+
+    my $replace_rule = $options->{vars}->{config}->{config}->{module_path};
+    $options->{dist_path}  =~ s/$replace_rule/$self->{module_path}/ge if $replace_rule;
+}
+
 1;
Index: lib/Module/Setup/Plugin/RegExpReplacer.pm
===================================================================
--- lib/Module/Setup/Plugin/RegExpReplacer.pm	(revision 0)
+++ lib/Module/Setup/Plugin/RegExpReplacer.pm	(revision 0)
@@ -0,0 +1,27 @@
+package Module::Setup::Plugin::RegExpReplacer;
+use strict;
+use warnings;
+use base 'Module::Setup::Plugin';
+
+use Module::Setup::Path::File;
+
+sub register {
+    my ( $self, ) = @_;
+    $self->add_trigger( template_process => \&template_process );
+}
+
+sub template_process {
+    my ( $self, $opts ) = @_;
+    return unless $opts->{template};
+    my $template = delete $opts->{template};
+    my $replacement_rule
+        = $opts->{vars}->{config}->{config}->{replacement_rule} || {};
+    for my $key ( keys %$replacement_rule ) {
+        $replacement_rule->{$key} =~ s/\$module/$self->distribute->module/ge;
+        $template =~ s/$key/$replacement_rule->{$key}/ge;
+    }
+    $opts->{template} = $template;
+    $opts->{content}  = $template;
+}
+
+1;
Index: lib/Module/Setup/Plugin/Template.pm
===================================================================
--- lib/Module/Setup/Plugin/Template.pm	(revision 22075)
+++ lib/Module/Setup/Plugin/Template.pm	(working copy)
@@ -15,8 +15,9 @@
 sub template_process {
     my($self, $opts) = @_;
     return unless $opts->{template};
-    my $template = delete $opts->{template};;
+    my $template = delete $opts->{template};
     $TEMPLATE->process(\$template, $opts->{vars}, \my $content);
+    $opts->{template} = $content;
     $opts->{content} = $content;
 }
 
---
author: Dann
email: 'techmemo {at} gmail.com'
plugins:
  - Config::Basic
  - VC::Git
  - Template 
  - RegExpReplacer
config:
  module_path: MyApp
  replacement_rule:
    MyApp: $module
    hoge: hoge

これでTT用のリプレーサ適用したあとで、正規表現のリプレーサも適用します。
コード部分は正規表現使っておきかえて、PODなどの文書の部分はTT用の変数で書いてといったことが可能になります。

これだとテンプレートを編集するときに、いちいちsyntax errorにならないので、テンプレートの編集が簡単になるんじゃないかなと思ってます。

正規表現でファイルの中身を置き換える形式だと、テンプレートの動作を100%完全に保証することにはならないですが、テンプレートそのものが複雑にならなければ、正規表現で十分動く形を保てるんじゃないかなと思ってます。

仕様としては、configのところを追加。YAMLへの追加場所がちょっとどこにすべきか悩ましいところですが。。

余談

templateテストするのとしばらく両方やってみようかなぁと。自分の場合、vimでsyntaxチェックやってるので、template作り終わった後でsyntaxエラーとかあるとテンプレート作るのが面倒になってしまうなぁというだけだったりするんですが.

templateのテストしていく方法だと、テンプレートを作っていくのに少し時間はかかるけれど、templateを作った後は、template作ったあとのアプリが動作することが保証される。

一方、正規表現の場合は、間違ってマッチした文字列を置き換えてしまう危険性があって、テンプレートから作ったアプリが完全に動く保証がない。

どちらも一長一短といった感じで、もう少し実際にテンプレート作るのやってみて考えようかなぁと。

テストも書いてみた

basicなものですが、テストも書いてみました。

use Module::Setup::Test::Utils;
use Test::More tests => 2;

default_dialog;

module_setup { target => 1, flavor_class => '+t::Flavor::RegExpReplacer', plugins => [
    'RegExpReplacer',
] }, 'TemplateModule';
ok -f target_dir('TemplateModule')->file('regexp.txt');
like target_dir('TemplateModule')->file('regexp.txt')->slurp, qr/TemplateModule/;
package t::Flavor::RegExpReplacer;
use strict;
use warnings;
use base 'Module::Setup::Flavor';
1;

__DATA__

---
config:
  plugins:
    - RegExpReplacer
  config:
    module_path: MyApp
    replacement_rule:
      MyApp: $module
      myapp: hoge

---
file: regexp.txt
template: |
  MyApp

微妙なところ

  • module_pathを置き換えるところが、RegExpReplacerではなく、Distributeでやってしまっているので統一感がないところ。どうしたほうがいいのかがいまいちわからず。