問題はCatalyst::Plugin::FormValidator::Simple::AutoのYAMLのprofileのフォーマットにあるのではないか

Catalyst::Plugin::FormValidator::Simple::AutoでマルチカラムのUNIQUEチェックをする場合 - libnitsuji.soの続き。
いろいろ考えてるうちに問題はYAMLの書き方じゃなくてほかのところにある気がしてきました。
つまりハッシュをキーにするという考えがまずおかしいのではないかと。
そこでFormValidator::Simpleのprofileについて追ってみました。
Catalyst::Plugin::FormValidator::Simple::AutoのprofileのYAMLは、

    # profiles.yml
    action1:
      param1:
        - NOT_BLANK
        - ASCII
        - [ 'LENGTH', 4, 10 ]
      param2:
        - NOT_BLANK

のように、アクションとパラメータ名がハッシュのキーになって、各バリデーションルールが配列の要素になってます。
これを見る限りだと、「ああ、FormValidator::Simpleってのはチェックするパラメータ名 => ルールの配列というハッシュをプロファイルとして期待しているのかな」と思ってしまいます。
実際にコードを見ても、

sub prepare {
    my $c = shift->NEXT::prepare(@_);

    if ( my $profile = $c->config->{validator}{profiles}{ $c->action->reverse } ) {
        $c->validator_profile( $c->action->reverse );
        $c->form(%$profile);
    }

    $c
}

となっていて$c->formにハッシュを渡しています。
これはたぶん、Autoではエラーメッセージとの兼ね合いもあってハッシュの方が対応付けやすいからこうなってるのかなーと予想します。

ただ、一方でCatalyst::Plugin::FormValidator::Simpleのソースを見ると、

sub form {
    my $c = shift;
    if ($_[0]) {
        my $form = $_[1] ? [@_] : $_[0];
        $c->{validator}->check($c->req, $form);
    }
    return $c->{validator}->results;
}

となっていて、引数が複数ある場合は配列リファレンスとしてcheck()に渡してます。
FormValidator::SimpleではFormValidator::Simple::Profileの_init()でprofileを読み込んでいるんだけど、配列から値を二つずつとりだして、みたいに処理をしているのでやっぱり配列を指定するってのが正しいのでしょう。

sub _init {
    my($self, $prof) = @_;
    for (my $i = 0; $i <= $#{$prof}; $i += 2) {
        my ($key, $constraints) = ($prof->[$i], $prof->[$i + 1]);
        my $record = FormValidator::Simple::Profile::Record->new;
        $record->set_keys($key);
        $record->set_constraints($constraints);
        $self->append($record);
    }
}

というわけで、FormValidator::SimpleやCatalyst::Plugin::FormValidator::SimpleのSYNOPSISではいかにもハッシュっぽく見えるため勘違いしそうですが実は=>は単なるカンマの役目であり、実際にはハッシュではなく配列として処理されるのだということがわかります。そのため、

「ああ、FormValidator::Simpleってのはチェックするパラメータ名 => ルールの配列というハッシュをプロファイルとして期待しているのかな」と思ってしまいます。

というのは単なる思い込みにすぎないということがわかりました。
つまり、どうすればいいかというと、以下のようにすればいいのではないかと。

admin/category/do_edit:
  - name
  - [ NOT_BLANK, ['LENGTH', 0, 200] ]
  - {unique: ['id', 'name']}
  - ['DBIC_UNIQUE', '__c_model(DBIC::Category)__', '!id', 'name']

でもこれだとCatalyst::Plugin::FormValidator::Simple::Autoで期待しているフォーマットとは違うのでまだ動きません。
そこで、FormValidator::Simple::ProfileManager::YAMLというモジュールを見るとまさにこのようなフォーマットになってるので、次はその辺を攻めてみればいんじゃないかなーと思っております。

    • -

余談:
そもそもハッシュリファレンスをハッシュのキーにするってのがなんか引っかかる。

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use YAML;

my $hash = {
  {unique => ['id', 'name']} => ['DBIC_UNIQUE', '__c_model(DBIC::Category)__', '!id', 'name']
};
foreach my $key (keys %$hash) {
  if (ref $key) {
    if (ref $key eq 'HASH') {
      print "$key is a hash reference\n";
    }
  }
  else {
    print "$key is not a reference\n";
  }

  print "$key: $hash->{$key}\n";
}

print Dump $hash;
print Dumper $hash;

こんなプログラムを書いて実行すると結果はこうなります。

HASH(0x86b4180) is not a reference
HASH(0x86b4180): ARRAY(0x86b4534)
---
HASH(0x86b4180):
  - DBIC_UNIQUE
  - __c_model(DBIC::Category)__
  - '!id'
  - name
$VAR1 = {
          'HASH(0x86b4180)' => [
                                 'DBIC_UNIQUE',
                                 '__c_model(DBIC::Category)__',
                                 '!id',
                                 'name'
                               ]
        };

キーに指定したハッシュリファレンスはHASH(0x86b4180)という文字列に変換されてキーになってます。
YAMLどうこうって話じゃなくてハッシュリファレンスをハッシュのキーにした時点でこうなるんだね。
ってこんなのPerlの基礎かな・・。

    • -

Catalyst::Plugin::FormValidator::Simple::Auto - Smart validation with FormValidator::Simple - metacpan.org
Catalyst::Plugin::FormValidator::Simple - Validator for Catalyst with FormValidator::Simple - metacpan.org
FormValidator::Simple::ProfileManager::YAML - YAML profile manager for FormValidator::Simple - metacpan.org