view release on metacpan or search on metacpan
compound field from a form that does not contain the parent field.
* Fixed a bug that caused input_value() in on/off groups to treat any
reference as an array reference. (Reported by Derek Watson)
0.546 (01.15.2007) - John Siracusa <siracusa@gmail.com>
* Fixed a bug that prevented params() from correctly cascading to
nested forms.
* Added a field_value() method to the Compound field class.
(Suggested by Guillermo Roditi)
* Added a cascade parameter to validate(), which defaults to true.
(Suggested by Guillermo Roditi)
0.545 (12.08.2006) - John Siracusa <siracusa@gmail.com>
* Added param_exists_for_field() method to Rose::HTML::Form.
* Added positive() and negative() methods to integer fields.
* Fixed a bug that caused validation to fail for certain compound
fields with labels. (Reported by Guillermo Roditi)
* Improved error messages for empty, split datetime fields.
* Added class mapping for the "int" and "integer" field types.
lib/Rose/HTML/Form.pm view on Meta::CPAN
$qs .= join($sep, map { $param . '=' . uri_escape($_, UNSAFE_URI_CHARS) } @$values);
}
return $qs;
}
sub validate
{
my($self, %args) = @_;
$args{'cascade'} = 1 unless(exists $args{'cascade'});
my $fail = 0;
my $cascade = $args{'cascade'};
if($cascade)
{
foreach my $form ($self->forms)
{
next if($form->is_empty && $form->empty_is_ok);
$Debug && warn "Validating sub-form ", $form->form_name, "\n";
unless($form->validate(%args))
{
$self->add_error($form->error) if($form->error);
lib/Rose/HTML/Form.pm view on Meta::CPAN
=item B<validate [PARAMS]>
Validate the form by calling L<validate()|Rose::HTML::Form::Field/validate> on each field and L<validate()|/validate> on each each L<sub-form|/"NESTED FORMS">. If any field or form returns false from its C<validate()> method call, then this method r...
If this method is about to return false and the L<error|Rose::HTML::Object/error> attribute of this form is not set, then it is set to a generic error message.
PARAMS are name/value pairs. Valid parameters are:
=over 4
=item C<cascade BOOL>
If true, then the L<validate()|/validate> method of each sub-form is called, passing PARAMS, with a C<form_only> parameter set to true. The default value of the C<cascade> parameter is true. Note that all fields in all nested forms are validated re...
=item C<form_only BOOL>
If true, then the L<validate|Rose::HTML::Form::Field/validate> method is not called on the fields of this form and its sub-forms. Defaults to false, but is set to true when calling L<validate()|/validate> on sub-forms in response to the C<cascade>...
=back
Examples:
$form = Rose::HTML::Form->new;
$form->add_field(foo => { type => 'text' });
$subform = Rose::HTML::Form->new;
$subform->add_field(bar => { type => 'text' });
$form->add_form(sub => $subform);
# Call validate() on fields "foo" and "sub.bar" and
# call validate(form_only => 1) on the sub-form "sub"
$form->validate;
# Same as above
$form->validate(cascade => 1);
# Call validate() on fields "foo" and "sub.bar"
$form->validate(cascade => 0);
# Call validate(form_only => 1) on the sub-form "sub"
$form->validate(form_only => 1);
# Don't call validate() on any fields or sub-forms
$form->validate(form_only => 1, cascade => 0);
=item B<validate_field_html_attrs [BOOL]>
Get or set a boolean flag that indicates whether or not the fields of this form will validate their HTML attributes. If a BOOL argument is passed, then it is passed as the argument to a call to L<validate_html_attrs()|Rose::HTML::Object/validate_htm...
=item B<was_submitted>
Returns true id L<params exist|/param_exists_for_field> for any L<field|/fields>, false otherwise.
=item B<xhtml_hidden_fields>
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
'error_class',
],
);
#
# Class data
#
use Rose::Class::MakeMethods::Generic
(
inheritable_hash => 'default_locale_cascade',
);
use Rose::Class::MakeMethods::Generic
(
inheritable_scalar =>
[
'default_locale',
'_auto_load_messages',
'_auto_load_locales',
],
);
__PACKAGE__->default_locale('en');
__PACKAGE__->default_locale_cascade('default' => [ 'en' ]);
#
# Class methods
#
sub default_variant { DEFAULT_VARIANT }
#
# Object methods
#
sub init_localized_messages_hash { {} }
sub init_locale_cascade
{
my($self) = shift;
my $class = ref($self) || $self;
return $class->default_locale_cascade;
}
sub locale_cascade
{
my($self) = shift;
my $hash = $self->{'locale_cascade'} ||= ref($self)->init_locale_cascade;
if(@_)
{
if(@_ == 1)
{
return $hash->{$_[0]};
}
elsif(@_ % 2 == 0)
{
for(my $i = 0; $i < @_; $i += 2)
{
$hash->{$_[$i]} = $_[$i + 1];
}
}
else { croak "Odd number of arguments passed to locale_cascade()" }
}
return wantarray ? %$hash : $hash;
}
sub init_locale
{
my($self) = shift;
my $class = ref($self) || $self;
return $class->default_locale;
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
my $args = $args{'args'} || $message->args;
my $locale = $args{'locale'} || $message->locale || $self->locale;
my $id = $message->id;
my $variant = $args{'variant'} ||=
$self->select_variant_for_message(id => $id,
args => $args,
locale => $locale);
my $locale_cascade = $self->locale_cascade($locale) ||
$self->locale_cascade('default') || [];
foreach my $try_locale ($locale, @$locale_cascade)
{
my $variant_cascade =
$self->variant_cascade(locale => $try_locale,
variant => $variant,
message => $message,
args => $args) || [];
foreach my $try_variant ($variant, @$variant_cascade)
{
my $text =
$self->get_localized_message_text(
id => $id,
locale => $try_locale,
variant => $try_variant,
from_class => $calling_class);
$parent = $first_parent;
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
'two' => [ 'plural', DEFAULT_VARIANT ],
'few' => [ 'plural', DEFAULT_VARIANT ],
'many' => [ 'plural', DEFAULT_VARIANT ],
'plural' => [ DEFAULT_VARIANT ],
);
# Trying to avoid repeated anonymous array generation that
# might(?) result from using literal [] below
my @None;
sub variant_cascade
{
my($self, %args) = @_;
return $Variant_Cascade{$args{'variant'}} ||
\@None;
}
sub localized_message_exists
{
my($self, $name, $locale, $variant) = @_;
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
If no explicit C<variant> is specified, the L<select_variant_for_message|/select_variant_for_message> method is called to select an appropriate variant. The default implementation of this method returns the L<default variant|/default_variant> most o...
This leads to the primary intended use of variants: pluralization. English has relatively simple pluralization rules, but other languages have special grammar for not just singular and plural, but also "dual," and sometimes even "many" and "few." T...
L<http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html>
with the exception that C<plural> is used in place of C<other>. (Variants are a general purpose mechanism, whereas the context of pluralization is implied in the case of the CLDR terms. A variant named C<other> has no apparent connection to plurali...
The default implementation of L<select_variant_for_count|/select_variant_for_count> (sanely) makes no judgements about "few" or "many," but does return C<zero> for a C<count> of 0, C<one> for 1, C<two> for 2, and C<plural> for all other values of C<c...
But since English has no special pluralization grammar for two items, how is this expected to work in the general case? The answer is the so-called "L<variant cascade|/variant_cascade>." If the desired variant is not available for the specified mes...
The default implementation of L<variant_cascade|/variant_cascade> follows simple English-centric rules, cascading directly to C<plural> except in the case of the C<one> variant, and appending the L<default variant|/default_variant> to the end of all ...
(Incidentally, there is also a L<locale cascade|/locale_cascade>. The L<localize_message|/localize_message> method uses a nested loop: for each locale, for each variant, look for message text. See the L<localize_message|/localize_message> documenta...
Here's an example using variants. (Please forgive the poor translations. I don't speak French. Corrections welcome!) First, the message text:
[% LOCALE en %]
FIELD_ERROR_TOO_MANY_DAYS = "Too many days."
FIELD_ERROR_TOO_MANY_DAYS(one) = "One day is too many."
FIELD_ERROR_TOO_MANY_DAYS(two) = "Two days is too many."
FIELD_ERROR_TOO_MANY_DAYS(few) = "[count] days is too many (few)."
FIELD_ERROR_TOO_MANY_DAYS(many) = "[count] days is too many (many)."
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
...
$id = FIELD_ERROR_TOO_MANY_DAYS; # to make for shorter lines below
$field->locale('en');
$field->error_id($id, { count => 0 });
# No explicit variant given. The select_variant_for_count() called
# and returns variant "zero". No "zero" variant found for this
# message in locale "en", so the variant_cascade() containing
# ('plural', 'default') is considered, in that order. A "plural"
# variant is found.
print $field->error; # "0 days is too many."
$field->error_id($id, { count => 2 });
# No explicit variant given. The select_variant_for_count() called and
# returns variant "two". That message variant is found in locale "en"
print $field->error; # "Two days is too many."
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
# Explicit variant given. That message variant is found in locale "en"
print $field->error; # "3 days is too many (few)."
$field->locale('fr');
$field->error_id($id, { count => 0 });
# No explicit variant given. The select_variant_for_count() called
# and returns variant "zero". No "zero" variant found for this
# message in locale "fr", so the variant_cascade() containing
# ('plural', 'default') is considered, in that order. A "plural"
# variant is found.
print $field->error; # "0 jours est un trop grand nombre."
$field->error_id($id, { count => 3, variant => 'few' });
# Explicit variant given. No "few" variant found for this message
# in locale "fr", so the variant_cascade() containing ('plural',
# 'default') is considered, in that order. A "plural" variant is
# found.
print $field->error; # "3 jours est un trop grand nombre."
I hope you get the idea. Remember that what's described above is merely the default implementation. You are fully expected to override any and all public methods in the localizer in you L<private library|Rose::HTML::Objects/"PRIVATE LIBRARIES"> to ...
And even if you don't plan to use the variant system at all, you might want to override L<select_variant_for_message|/select_variant_for_message> to unconditionally return the L<default variant|/default_variant>, which will eliminate the special trea...
=head3 CUSTOMIZATION
The implementation of localized message storage described above exists primarily because it's the most convenient way to store and distribute the localized messages that ship with the L<Rose::HTML::Objects> module distribution. For a real applicatio...
The easiest way to do this is to create your own L<Rose::HTML::Object::Message::Localizer> subclass and override the L<get_localized_message_text|/get_localized_message_text> method, or any other method(s) you desire, and provide your own implementat...
You must then ensure that your new localizer subclass is actually used by all of your HTML objects. You can, of course, set the L<localizer|Rose::HTML::Object/localizer> attribute directly, but a much more comprehensive way to customize your HTML ob...
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
=over 4
=item B<auto_load_messages [BOOL]>
Get or set a boolean value indicating whether or not localized message text should be automatically loaded from classes that call their localizer's L<load_all_messages|/load_all_messages> method. The default value is true if either of the C<MOD_PERL...
=item B<default_locale [LOCALE]>
Get or set the default L<locale|/locale> used by objects of this class. Defaults to "en".
=item B<default_locale_cascade [PARAMS]>
Get or set the default locale cascade. PARAMS are L<locale|/"LOCALES">/arrayref pairs. Each referenced array contains a list of locales to check, in the order specified, when message text is not available in the desired locale. There is one specia...
default => [ 'en' ]
That is, if message text is not available in the desired locale, C<en> text will be returned instead (assuming it exists).
This method returns the default locale cascade as a reference to a hash of locale/arrayref pairs (in scalar context) or a list of locale/arrayref pairs (in list context).
=item B<load_all_messages [PARAMS]>
Load all localized message text from the C<__DATA__> section of the class specified by PARAMS name/value pairs. Valid PARAMS are:
=over 4
=item B<from_class CLASS>
The name of the class from which to load localized message text. Defaults to the name of the class from which this method was called.
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
=item B<names [ NAME | ARRAYREF | REGEX ]>
Only load text for the specified messages. Pass either a single message NAME, a reference to an array of names, or a regular expression that matches the names of the messages you want to load.
=back
=item B<locale [LOCALE]>
Get or set the L<locale|/"LOCALES"> of this localizer. This locale is used by several methods when a locale is not explicitly provided. The default value is determined by the L<default_locale|/default_locale> class method.
=item B<locale_cascade [PARAMS]>
Get or set the locale cascade. PARAMS are L<locale|/"LOCALES">/arrayref pairs. Each referenced array contains a list of locales to check, in the order specified, when message text is not available in the desired locale. There is one special locale...
This method returns the locale cascade as a reference to a hash of locale/arrayref pairs (in scalar context) or a list of locale/arrayref pairs (in list context).
=item B<localize_message PARAMS>
Localize a message, returning the appropriately localized and processed message text. Valid PARAMS name/value pairs are:
=over 4
=item B<args HASHREF>
A reference to a hash of L<message arguments|Rose::HTML::Object::Message::Localized/args>. If omitted, the C<message>'s L<args|Rose::HTML::Object::Message::Localized/args> are used.
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
=item B<message MESSAGE>
The L<Rose::HTML::Object::Message>-derived message object. This parameter is required.
=item B<variant VARIANT>
The message L<variant|/"VARIANTS">. If omitted, the L<select_variant_for_message|/select_variant_for_message> method is called, passing the C<message> L<id|Rose::HTML::Object::Message/id>, C<args>, and C<locale>.
=back
This method performs a nested loop to search for localized message text: for each locale (including any L<locale_cascade|/locale_cascade>), for each variant (including any L<variant_cascade|/variant_cascade>), for each parent L<field|Rose::HTML::Form...
=item B<message_for_error_id PARAMS>
Given an L<error|Rose::HTML::Object::Errors> id, return the corresponding L<message_class|/message_class> object. The default implementation simply looks for a message with the same integer id as the error. Valid PARAMS name/value pairs are:
=over 4
=item B<error_id ID>
The integer error id. This parameter is required.
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
=item B<text TEXT>
The localized message text.
=item B<variant VARIANT>
The message variant, if any. See the L<LOCALIZED TEXT|/"LOCALIZED TEXT"> section above for more information about variants.
=back
=item B<variant_cascade [PARAMS]>
Return a reference to an array of L<variant|/VARIANTS> names under which to look for localized text, assuming the requested variant is not available in the context specified in PARAMS name/value pairs. Valid params are:
=over 4
=item B<args HASHREF>
A reference to a hash of L<message arguments|Rose::HTML::Object::Message::Localized/args>.
=item B<locale LOCALE>
lib/Rose/HTML/Object/Message/Localizer.pm view on Meta::CPAN
The L<Rose::HTML::Object::Message>-derived message object.
=item B<variant VARIANT>
The originally requested message L<variant|/"VARIANTS">.
=back
The default implementation looks only at the C<variant> parameter and returns references to arrays containing the following variant lists based on it:
variant variant cascade
------- ---------------
zero plural, default
one default
two plural, default
few plural, default
many plural, default
plural default
The array references returned should be treated as read-only.
t/form-nested.t view on Meta::CPAN
is($form->field('person_address.person.age')->internal_value, '10', 'person_address_dog age 1');
is($form->field('person_address.person.gender')->internal_value, 'm', 'person_address_dog gender 1');
is($form->field('person_address.person.bday')->internal_value->strftime('%Y-%m-%d'), '1983-01-02', 'person_address_dog bday 1');
is($form->field('person_address.address.street')->internal_value, '1 Main St.', 'person_address_dog street 1');
is($form->field('person_address.address.city')->internal_value, 'Smithtown', 'person_address_dog city 1');
is($form->field('person_address.address.state')->internal_value, 'NY', 'person_address_dog state 1');
is($form->field('person_address.address.zip')->internal_value, '11787', 'person_address_dog zip 1');
ok($person_address_form->validate, 'validate() 1');
ok($person_address_form->validate(cascade => 0), 'validate() 2');
$person_address_form->field('person_address.address.zip')->input_value(666);
ok(!$person_address_form->validate, 'validate() 3');
ok($person_address_form->validate(cascade => 0), 'validate() 4');
$person_address_form->field('person_address.address.zip')->input_value(11787);
$person = $form->person_from_form;
is(ref $person, 'MyPerson', 'person_from_form 3');
is($person->name, 'John', 'person name 3');
is($person->age, '10', 'person age 3');
is($person->gender, 'm', 'person gender 3');
is($person->bday->strftime('%Y-%m-%d'), '1983-01-02', 'person bday 3');
t/form-nested.t view on Meta::CPAN
my $subform = Rose::HTML::Form->new;
$subform->add_field(bar => { type => 'text' });
$form->add_form(sub => $subform);
#local $Rose::HTML::Form::Debug = 1;
# Call validate() on fields "foo" and "sub.bar" and
# call validate(form_only => 1) on the sub-form "sub"
$form->validate;
#print STDERR "---\n";
# Same as above
$form->validate(cascade => 1);
#print STDERR "---\n";
# Call validate() on fields "foo" and "sub.bar"
$form->validate(cascade => 0);
#print STDERR "---\n";
# Call validate(form_only => 1) on the sub-form "sub"
$form->validate(form_only => 1);
#print STDERR "---\n";
# Don't call validate() on any fields or sub-forms
$form->validate(form_only => 1, cascade => 0);
# no warnings 'redefine';
# *MyAddressForm::validate = sub
# {
# my($self) = shift;
# $self->field('street')->error('Blah');
# $self->Rose::HTML::Form::validate(@_);
# };
$form = MyPersonAddressForm->new;
t/form-repeatable.t view on Meta::CPAN
type => 'submit',
value => 'Create Family',
},
);
}
# sub validate
# {
# my ($self) = shift;
#
# my $ok = $self->SUPER::validate(cascade => 0);
# return $ok unless ($ok);
#
# foreach my $parentform ( $self->form('parents')->forms )
# {
# next if ( $parentform->is_empty );
#
# unless ( $parentform->validate )
# {
# $self->add_error( 'Invalid parent: ' . $parentform->error );
# $ok = 0;
t/object-l10n.t view on Meta::CPAN
# MyObject
$o = MyObject->new;
$o->error_id(MYOBJ_ERR1, { a => 'A', b => 'B' });
is($o->error->as_string, 'This is my object msg 1: B, A', 'MYOBJ_ERR1 en');
$o->locale('xx');
is($o->error->as_string, "C'est mon object\nmsg 1: B, A", 'MYOBJ_ERR1 xx');
MyObject->localizer->locale_cascade(
{
'en-us' => [ 'en' ],
'en-uk' => [ 'en' ],
'default' => [ 'en' ],
});
$o->locale('en-us');
is($o->error . '', 'This is my object msg 1: B, A', 'MYOBJ_ERR1 en-us');
# MyObject2
MyObject2->localizer->locale_cascade(
{
'en-us' => [ 'en' ],
'en-uk' => [ 'en' ],
'default' => [ 'en' ],
});
$o = MyObject2->new;
$o->error_id('MYOBJ_ERR2', { a => 'A', b => 'B' });