package Dancer::Plugin::Form;
use warnings;
use strict;
use Dancer ':syntax';
use Dancer::Plugin;
my %forms;
=head1 NAME
Dancer::Plugin::Form - Dancer form handler for Template::Flute template engine
=head1 VERSION
Version 0.0061
=cut
our $VERSION = '0.0061';
=head1 SYNOPSIS
Display template with checkout form:
get '/checkout' => sub {
my $form;
$form = form('checkout');
template 'checkout', {form => $form};
};
Retrieve form input from checkout form:
post '/checkout' => sub {
my ($form, $values);
$form = form('checkout');
$values = $form->values();
};
Reset form after completion to prevent old data from
showing up on new form:
$form = form('checkout');
$form->reset;
=cut
register form => sub {
my $name = '';
my $object;
if (@_ % 2) {
$name = shift;
}
else {
$name = 'main';
}
$object = Dancer::Plugin::Form->new(name => $name, @_);
return $object;
};
register_plugin;
=head1 DESCRIPTION
C<Dancer::Plugin::Form> is used for forms with the L<Dancer::Template::TemplateFlute>
templating engine.
Form fields, values and errors are stored into and loaded from the session key C<form>.
=head1 METHODS
=head2 new
Creates C<Dancer::Plugin::Form> object.
=cut
sub new {
my ($class, $self, %params);
$class = shift;
$self = {fields => [], errors => [], valid => undef, pristine => 1};
bless $self;
%params = @_;
if (exists $params{name}) {
$self->{name} = $params{name};
}
else {
$self->{name} = 'main';
}
if (exists $params{action}) {
$self->action($params{action});
}
# try to load form data from session
$self->from_session();
return $self;
}
=head2 name
Get form name:
$form->name
=cut
sub name {
my $self = shift;
return $self->{name};
}
=head2 action
Set form action:
$form->action('/checkout');
Get form action:
$action = $form->action;
=cut
sub action {
my ($self, $action) = @_;
if ($action) {
$self->{action} = $action;
}
return $self->{action};
}
=head2 fill
Fill form values:
$form->fill({username => 'racke', email => 'racke@linuxia.de'});
=cut
sub fill {
my ($self);
$self = shift;
if (@_) {
if (@_ == 1) {
%{$self->{values}} = %{$_[0]};
}
else {
%{$self->{values}} = @_;
}
}
return $self->{values};
}
=head2 values
Get form values as hash reference:
$values = $form->values;
Set form values from a hash reference:
$values => $form->values(ref => \%input);
=cut
sub values {
my ($self, $scope, $data) = @_;
my (%values, $params, $save);
if (! defined $scope) {
$params = params('body');
$save = 1;
}
elsif ($scope eq 'session') {
$params = $self->{values};
}
elsif ($scope eq 'body' || $scope eq 'query' ) {
$params = params($scope);
$save = 1;
}
elsif ($scope eq 'ref') {
$params = $data;
$save = 1;
}
else {
$params = '';
}
for my $f (@{$self->{fields}}) {
$values{$f} = $params->{$f};
if ($save && defined $values{$f}) {
# tidy form input first
$values{$f} =~ s/^\s+//;
$values{$f} =~ s/\s+$//;
}
}
if ($save) {
$self->{values} = \%values;
return \%values;
}
return \%values;
}
=head2 valid
Determine whether form values are valid:
$form->valid();
Return values are 1 (valid), 0 (invalid) or
undef (unknown).
Set form status to "valid":
$form->valid(1);
Set form status to "invalid":
$form->valid(0);
The form status automatically changes to
"invalid" when errors method is called with
error messages.
=cut
sub valid {
my $self = shift;
my $valid = shift;
if (defined $valid) {
Dancer::Logger::debug("Setting valid for $self->{name} to $valid.");
$self->{valid} = $valid;
# record changes in user's session
$self->to_session;
}
return $self->{valid};
}
=head2 errors
Set form errors:
$form->errors({username => 'Minimum 8 characters',
email => 'Invalid email address'});
Get form errors as hash reference:
$errors = $form->errors;
=cut
sub errors {
my ($self, $errors) = @_;
my ($key, $value, @buf);
if ($errors) {
if (ref($errors) eq 'HASH') {
while (($key, $value) = each %$errors) {
push @buf, {name => $key, label => $value};
}
$self->{errors} = \@buf;
}
$self->{valid} = 0;
}
return $self->{errors};
}
=head2 errors_hashed
Returns form errors as array reference filled with a hash reference
for each error.
=cut
sub errors_hashed {
my ($self) = @_;
my (@hashed);
for my $err (@{$self->{errors}}) {
push (@hashed, {name => $err->[0], label => $err->[1]});
}
return \@hashed;
}
=head2 failure
Indicates form failure by passing form errors.
$form->failure(errors => {username => 'Minimum 8 characters',
email => 'Invalid email address'});
You can also set a route for redirection:
return $form->failure(errors => {username => 'Minimum 8 characters'},
route => '/account');
Passing parameters for the redirection URL is also possible:
return $form->failure(errors => {username => 'Minimum 8 characters'},
route => '/account',
params => {layout => 'mobile'});
Please ensure that you validate input submitted by an user before
adding them to the C<params> hash.
=cut
sub failure {
my ($self, %args) = @_;
$self->{errors} = $args{errors};
# update session data about this form
$self->to_session();
session(form_errors => '<ul>' . join('', map {"<li>$_->[1]</li>"} @{$args{errors} || []}) . '</ul>');
session(form_data => $args{data});
if ($args{route}) {
redirect uri_for($args{route}, $args{params});
}
return;
}
=head2 fields
Set form fields:
$form->fields([qw/username email password verify/]);
Get form fields:
$fields = $form->fields;
=cut
sub fields {
my ($self);
$self = shift;
if (@_) {
$self->{fields} = shift;
}
return $self->{fields};
}
=head2 pristine
Determines whether a form is pristine or not.
This can be used to fill the form with default
values and suppress display of errors.
A form is pristine until it receives form
field input from the request or out of the
session.
=cut
sub pristine {
return $_[0]->{pristine};
};
=head2 reset
Reset form information (fields, errors, values, valid) and
updates session accordingly.
=cut
sub reset {
my $self = shift;
$self->{fields} = [];
$self->{errors} = [];
$self->{values} = {};
$self->{valid} = undef;
$self->{pristine} = 1;
$self->to_session;
return 1;
}
=head2 from_session
Loads form data from session key 'form'.
Returns 1 if session contains data for this form, 0 otherwise.
=cut
sub from_session {
my ($self) = @_;
my ($forms_ref, $form);
if ($forms_ref = session('form')) {
if (exists $forms_ref->{$self->{name}}) {
$form = $forms_ref->{$self->{name}};
$self->{fields} = $form->{fields} || [];
$self->{errors} = $form->{errors} || [];
$self->{values} = $form->{values} || {};
$self->{valid} = $form->{valid};
while (my ($key, $value) = each %{$self->{values}}) {
if (defined $value) {
$self->{pristine} = 0;
last;
}
}
return 1;
}
}
return 0;
}
=head2 to_session
Saves form name, form fields, form values and form errors into
session key 'form'.
=cut
sub to_session {
my ($self) = @_;
my ($forms_ref);
# get current form information from session
$forms_ref = session 'form';
# update our form
$forms_ref->{$self->{name}} = {name => $self->{name},
fields => $self->{fields},
errors => $self->{errors},
values => $self->{values},
valid => $self->{valid},
};
# update form information
session 'form' => $forms_ref;
}
=head1 AUTHOR
Stefan Hornburg (Racke), C<< <racke at linuxia.de> >>
=head1 BUGS
Please report any bugs or feature requests to C<bug-dancer-template-templateflute at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Dancer-Template-TemplateFlute>. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Dancer::Plugin::Form
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Dancer-Template-TemplateFlute>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/Dancer-Template-TemplateFlute>
=item * CPAN Ratings
L<http://cpanratings.perl.org/d/Dancer-Template-TemplateFlute>
=item * Search CPAN
L<http://search.cpan.org/dist/Dancer-Template-TemplateFlute/>
=back
=head1 ACKNOWLEDGEMENTS
=head1 LICENSE AND COPYRIGHT
Copyright 2011-2014 Stefan Hornburg (Racke).
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.
=cut
1; # End of Dancer::Plugin::Form