Catalyst::Controller::FormBuilder

簡単なものをつくるときには便利なんだけど、気を付けないと変なところでハマることがある。

たとえば設定ファイル。表面的にはYAMLなんだけど、

method: post
submit: Post
fields:
  name:
    label: Name
    size: 40
    required: 1
  sort_key:
    label: Sort
    size: 40
  status:
    type: select
    options: 0 = Draft, 1 = Publish
    label: Status
    required: 1
    validate: INT

optionsのところをよりYAMLらしく

    options:
      Draft: 0
      Publish: 1

とか書いたらうまくいかない(この辺はCGI::FormBuilderのマニュアルを読むのが吉)。

あと、いまの版はforwardした先で処理しようとするとうまくいかないように変わっているとか(これのせいでTypefaceを試そうとしたときに動かなくてえらく困った)、FBのデバッグを有効にしていると情報が多すぎてうざいとか、XHTMLしか吐いてくれないのが嫌すぎるとかで、うちでは間にこんなのと、

package MyApp::FormBuilder::Action;

use strict;
use warnings;
use base qw( Catalyst::Controller::FormBuilder::Action::TT );

package Catalyst::Controller::FormBuilder::Action;

no warnings 'redefine';

sub execute {
  my $self = shift;
  my ( $controller, $c ) = @_;

  return $self->NEXT::execute(@_)
    unless exists $self->attributes->{ActionClass}
      && $self->attributes->{ActionClass}[0] eq
         $controller->_fb_setup->{action};

  my $form = $self->_setup_form(@_);

  # FB's debug info is too noisy
  $CGI::FormBuilder::Util::DEBUG = 0;
  $form->{debug} = 0;

  $controller->_formbuilder($form);
  $self->NEXT::execute(@_);

  # Current Catalyst::Controller::FormBuilder::Action overrides
  # formbuilder object with previous one here, which removes any
  # changes we made in the forwarded actions. We don't want this
  # override.
  # $controller->_formbuilder($form);

  $self->setup_template_vars( @_ );
}

1;

こんなのをかまして使っている。

package MyApp::Base::FormBuilder;

use strict;
use warnings;
use base qw( Catalyst::Controller::FormBuilder );

__PACKAGE__->config(
  'Controller::FormBuilder' => {
    method_name => 'form',
    form_path   => [ MyApp->path_to( 'forms' ) ],
    action      => 'MyApp::FormBuilder::Action',
    messages    => 'auto',
  }
);

# dirty work to let it emit HTML, not XHTML

use CGI::FormBuilder::Util ();

package #
  CGI::FormBuilder::Util;

use strict;
use warnings;
no warnings 'redefine';

sub htmltag ($;@) {
    # called as htmltag('tagname', %attr)
    # creates an HTML tag on the fly, quick and dirty
    my $name = shift or return;
    my $attr = CGI::FormBuilder::Util::htmlattr($name, @_);     # ref return faster

    # see if we have a special tag name (experimental)
    (my $look = $name) =~ s#^(/*)##;
    $name = "$1$CGI::FormBuilder::Util::TAGNAMES{$look}" if $CGI::FormBuilder::Util::TAGNAMES{$look};

    my $htag = join(' ', $name,
                  map { qq($_=") . CGI::FormBuilder::Util::escapehtml($attr->{$_}) . '"' } sort keys %$attr);

    # Don't do this!
    # $htag .= ' /' if $name eq 'input' || $name eq 'link';  # XHTML self-closing
    return '<' . $htag . '>';
}

1;

あと、Task::Catalystの中のバージョン指定が間違っている(古いCatalyst::Plugin::FormBuilderのバージョンが残っている)ので、インストールするときにはちょっと注意が必要かも。ひとつひとつ入れていく分には問題ないですが。