=pod

=head1 NAME

Form::Tiny::Manual::Cookbook - common advanced tasks with Form::Tiny

=head1 RECIPES

=head2 Corinna compatibility

Perl core OO system named Corinna (experimental) is not currently compatible
with bless-style objects. Since it uses different than hashref storage for its
objects, it is unlikely that it will ever be compatible. It is recommended to
stick to Moo/se when using Form::Tiny.

=head2 Object::Pad compatibility

L<Object::Pad>, the playground for Corinna, can be used together with Form::Tiny:

	use Object::Pad;

	class ParentForm :repr(HASH)
	{
		use Form::Tiny -nomoo;

		form_field 'f1';
	}

	class ChildForm isa ParentForm :repr(HASH)
	{
		use Form::Tiny -nomoo;

		form_field 'f2';
	}

You must use C<:repr(HASH)> on the class for it to be able to store Moo
properties, as well as use C<-nomoo> flag in Form::Tiny import. Other than
that, it should work the same as with any other OO system.

=head2 Mixing form fields with class fields

It is possible and often very helpful to have both form and class fields inside
the same form class:

	use Form::Tiny;
	use Types::Standard qw(Str);

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

	form_field 'username' => (
		type => Str,
		required => 1,
	);

	form_field 'password' => (
		type => Str,
		required => 1,
	);

	form_cleaner sub {
		my ($self, $data) = @_;

		# load user from the storage service using $data->{username}
		my $user = ...;
		if ($user->validate_password($data->{password})) {
			# set the user for the class, will be available after validation
			$self->user($user);
		}
		else {
			$self->add_error(password => 'invalid password');
		}
	};

While doing so, make sure not to override any of the L<Form::Tiny::Form>
symbols (Moo/se should complain about it when it happens).

=head2 I18N

I<Form::Tiny> has no system for internationalization. That being said, it makes
any custom error messages for fields possible with the C<message> argument to
C<form_field>. You can translate your messages there:

	form_field 'translated' => (
		type => SomeType,
		message => do_translate('That value is no good'),
	);

For a bit more roboust solution, you can include untranslated strings in
C<message> and translate errors in C<after_error> hook:

	form_field 'translated_later' => (
		type => SomeType,
		message => 'That value is no good',
	);

	form_hook after_error => (
		my ($self, $error) = @_;

		$error->set_error(
			do_translate($error->error)
		);
	);

This will also translate any error you add manually in other hooks.

=head2 Creating a role plugin

It is possible to create a single package plugin that will mix in a role into
the form class:

	package Form::Tiny::Plugin::MyRolePlugin;

	use strict;
	use warnings;

	use parent 'Form::Tiny::Plugin';

	sub plugin
	{
		my ($self, $caller, $context) = @_;

		return {
			roles => [__PACKAGE__],
		}
	}

	use Moo::Role;

	sub some_method
	{
		...
	}

	1;

The late import of L<Moo::Role> prevents the C<plugin> method from being mixed
in along with C<some_method>.

=head2 Empty forms

Form::Tiny tries hard not to force you to finalize building your form like
Moose does with C<make_immutable>. Form definition is built incrementally with
each DSL call. One limitation of this is that if your form contains nothing,
there's no place to process plugins and inheritance.

In rare occasions where you actually want to have an empty form, you have to
call C<form_meta> method on the package before going out of scope, for example:

	package ChildForm;

	use Form::Tiny;
	extends 'ParentForm';

	__PACKAGE__->form_meta;

=head1 SEE ALSO

=over

=item * L<Form::Tiny::Manual>, the main reference

=back

