You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/01/20 15:39:09 UTC
svn commit: r498114 - in /tapestry/tapestry5/tapestry-core/trunk/src:
main/java/org/apache/tapestry/services/ main/java/org/apache/tapestry/test/
main/java/org/apache/tapestry/validator/
main/resources/org/apache/tapestry/internal/ site/apt/guide/ test...
Author: hlship
Date: Sat Jan 20 06:39:08 2007
New Revision: 498114
URL: http://svn.apache.org/viewvc?view=rev&rev=498114
Log:
Add MinLength validator.
Added:
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/MinLengthTest.java
Modified:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ValidationMessages.properties
tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ValidationTrackerImplTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=498114&r1=498113&r2=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Sat Jan 20 06:39:08 2007
@@ -149,6 +149,7 @@
import org.apache.tapestry.runtime.RenderCommand;
import org.apache.tapestry.translator.IntegerTranslator;
import org.apache.tapestry.translator.StringTranslator;
+import org.apache.tapestry.validator.MinLength;
import org.apache.tapestry.validator.Required;
/**
@@ -922,12 +923,14 @@
* Contributes the basic set of validators:
* <ul>
* <li>required</li>
+ * <li>minlength</li>
* </ul>
*/
public static void contributeFieldValidatorSource(
MappedConfiguration<String, Validator> configuration)
{
configuration.add("required", new Required());
+ configuration.add("minlength", new MinLength());
}
public static TranslatorSource buildTranslatorSource(Map<String, Translator> configuration)
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java?view=diff&rev=498114&r1=498113&r2=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java Sat Jan 20 06:39:08 2007
@@ -633,7 +633,8 @@
}
@SuppressWarnings("unchecked")
- protected final void train_handleResult(ComponentEventHandler handler, Object result, Component component, String methodDescription, boolean abort)
+ protected final void train_handleResult(ComponentEventHandler handler, Object result,
+ Component component, String methodDescription, boolean abort)
{
expect(handler.handleResult(result, component, methodDescription)).andReturn(abort);
}
@@ -663,7 +664,8 @@
return newMock(Inject.class);
}
- protected final void train_findFieldsOfType(ClassTransformation transformation, String type, String... fieldNames)
+ protected final void train_findFieldsOfType(ClassTransformation transformation, String type,
+ String... fieldNames)
{
expect(transformation.findFieldsOfType(type)).andReturn(Arrays.asList(fieldNames));
}
@@ -678,7 +680,8 @@
expect(validator.getMessageKey()).andReturn(messageKey).atLeastOnce();
}
- protected final void train_getMessageFormatter(Messages messages, String key, MessageFormatter formatter)
+ protected final void train_getMessageFormatter(Messages messages, String key,
+ MessageFormatter formatter)
{
expect(messages.getFormatter(key)).andReturn(formatter).atLeastOnce();
}
@@ -686,5 +689,25 @@
protected final MessageFormatter newMessageFormatter()
{
return newMock(MessageFormatter.class);
+ }
+
+ /** Have to put the result before the varargs. */
+ protected void train_format(MessageFormatter formatter, String result, Object... arguments)
+ {
+ expect(formatter.format(arguments)).andReturn(result);
+ }
+
+ protected final Field newFieldWithLabel(String label)
+ {
+ Field field = newField();
+
+ train_getLabel(field, label);
+
+ return field;
+ }
+
+ protected final void train_getLabel(Field field, String label)
+ {
+ expect(field.getLabel()).andReturn(label).atLeastOnce();
}
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java?view=diff&rev=498114&r1=498113&r2=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java Sat Jan 20 06:39:08 2007
@@ -10,15 +10,20 @@
{
public String getMessageKey()
{
- return "min-length";
+ return "minimum-string-length";
}
public void check(Field field, Integer constraintValue, MessageFormatter formatter, String value)
throws ValidationException
{
+ // Validators should ignore blank/null values.
+ // TODO: Could this be externalized so that the Validator doesn't need to check?
+
if (InternalUtils.isBlank(value))
return;
+ if (value.length() < constraintValue)
+ throw new ValidationException(formatter.format(constraintValue, field.getLabel()));
}
public Class<Integer> getConstraintType()
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ValidationMessages.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ValidationMessages.properties?view=diff&rev=498114&r1=498113&r2=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ValidationMessages.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ValidationMessages.properties Sat Jan 20 06:39:08 2007
@@ -14,3 +14,4 @@
required=You must provide a value for %s.
integer-format-exception=The input value '%s' is not parseable as an integer value.
+minimum-string-length=You must provide at least %d characters for %s.
Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt?view=diff&rev=498114&r1=498113&r2=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt Sat Jan 20 06:39:08 2007
@@ -11,11 +11,14 @@
Tapestry excels at creating forms and validating input. Input validation is declarative, meaning you simply tell Tapestry what validations
to apply to a given field, and it takes care of it on the server and (once implemented) on the client as well.
+ Finally, Tapestry is able to not only present the errors back to the user, but to decorate the fields and the labels for the
+ fields, marking them as containing errors (primarily, using CSS effects).
+
Form component
The core of Tapestry's form support is the
{{{../apidocs/org/apache/tapestry/corelib/components/Form.html}Form}} component. The Form component encloses (wraps around) all the
- other <field components> (components that create HTML form element for the client web browser) such as
+ other <field components> such as
{{{../apidocs/org/apache/tapestry/corelib/components/TextField.html}TextField}},
{{{../apidocs/org/apache/tapestry/corelib/components/TextArea.html}TextArea}},
{{{../apidocs/org/apache/tapestry/corelib/components/Checkbox.html}Checkbox}}, etc.
@@ -35,7 +38,7 @@
Next, all the fields inside the form are activated to pull values out of the
incoming request, validate them and (if valid) store the changes.
- <For Tapestry 4 Users: > Tapestry 5 does not use the fragile "form rewind" method
+ <For Tapestry 4 Users: > Tapestry 5 does not use the fragile "form rewind" approach
from Tapestry 4. Instead, a hidden field generated during the render stores
the information needed to process the form submission.
@@ -57,11 +60,11 @@
form. The tracker can be provided to the Form via the Form's tracker parameter,
but this is rarely necessary.
- The Form includes methods isValid() and getHasErrors(), which are used to
+ The Form includes methods <<<isValid()>>> and <<<getHasErrors()>>>, which are used to
see if the Form's validation tracker contains any errors.
In your own logic, it is possible to record your own errors. Form includes
- two different versions of recordError(), one of which specifies a
+ two different versions of method <<<recordError()>>>, one of which specifies a
{{{../apidocs/org/apache/tapestry/Field.html}Field}} (an interface implemented by
all form element components), and one of which is for "global" errors, unassociated
with any particular field.
@@ -127,4 +130,78 @@
}
}
+---+
+
+ Because of the the fact that a form submission is <two> requests (the submission itself, then a re-render of the page),
+ it is necessary to make the value stored in the _userName field persist between the two requests. This would be necessary
+ for the _password field as well, except that the
+ {{{../apidocs/org/apache/tapestry/corelib/components/PasswordField.html}PasswordField}} component never renders a value.
+
+ Note that the onSuccess() method is not public; event handler methods can have any visibility, even private. Package private
+ (that is, no modifier) is the typical use, as it allows the component to be tested, from a test case class in the same package.
+
+ The Form only emits a "success" event if the there are no prior validation errors. This means it is not necessary to
+ write <<<if (_form.getHasErrors()) return;>>> as the first line of the method.
+
+ Finally, notice how business logic fits into validation. The UserAuthenticator service is responsible for ensuring
+ that the userName and (plaintext) password are valid. When it returns false, we ask the Form component
+ to record an error. We provide the PasswordField instance as the first parameter; this ensures that the
+ password field, and its label, are decorated when the Form is re-rendered, to present the errors to the user.
+
+Configuring Fields and Labels
+
+ The template for page contains a minimal amount of Tapestry instrumentation:
+
++---+
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+ <head>
+ <title>Login</title>
+ </head>
+ <body>
+ <h1>Please Login</h1>
+
+ <form t:id="form">
+
+ <t:comp type="Errors"/>
+
+ <label t:type="Label" for="userName"/>
+ <input t:type="TextField" t:id="userName" t:validate="required,minlen=3" size="30"/>
+ <br/>
+ <label t:type="Label" for="password"/>
+ <input t:id="password" t:validate="required,minlen=3" size="30"/>
+ <br/>
+ <input type="submit" value="Login"/>
+ </form>
+ </body>
+</html>
++---+
+
+ The Tapestry Form component is responsible for creating the necessary URL for the form submission (this is Tapestry's
+ responsibility, not yours).
+
+ The
+ {{{../apidocs/org/apache/tapestry/corelib/components/Errors.html}Errors}} component must be placed inside a Form, it outputs
+ all of the errors for all the fields within the Form as a single list. It uses some simple styling to make the result more presentable.
+
+ Each field component, such as the TextField, is paired with a
+ {{{../apidocs/org/apache/tapestry/corelib/components/Label.html}Label}} component. The Label will render out
+ a \<label\> element connected to the field. This is very important for useability, especially for users with
+ visual disabilities. It also means you can click on the label text to move the cursor to the corresponding field.
+
+ The <<<for>>> parameter of the Label is the id of a component.
+
+ For the TextField, we provide a component id, userName. We could specify the <<<value>>> parameter, but the default is
+ to match the TextField's id against a property of the container, the Login page, if such a property exists.
+
+ As a rule of thumb, you should always give your fields a specific id (this id will be used to generate the <<<name>>> and <<<id>>> attributes
+ of the rendered tag). Being allowed to omit the value parameter helps to keep the template from getting too cluttered.
+
+ The <<<validate>>> parameter identifies what validations should occur for the field. This is a list of validator names. Validators are
+ configured within Tapestry, and the list of available validators is extensible. "required" is a name of one of the built-in validators,
+ that ensures that the submitted value is not the empty string. Likewise, "minlen" ensures that the value has the specified minimum length.
+
+ The <<<validate>>> parameter was placed within the Tapestry namespace using the <<<t:>>> prefix. This is not strictly necessary, as the template
+ is <well formed> either way. However, putting the Tapestry specific values into the Tapestry namespace ensures that the template will itself
+ be <valid>.
+
+
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ValidationTrackerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ValidationTrackerImplTest.java?view=diff&rev=498114&r1=498113&r2=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ValidationTrackerImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ValidationTrackerImplTest.java Sat Jan 20 06:39:08 2007
@@ -37,8 +37,8 @@
@Test
public void order_added_is_maintained()
{
- Field fielda = newField("fieldA");
- Field fieldb = newField("fieldB");
+ Field fielda = newFieldWithElementName("fieldA");
+ Field fieldb = newFieldWithElementName("fieldB");
replay();
@@ -61,7 +61,7 @@
@Test
public void record_input()
{
- Field field = newField("field");
+ Field field = newFieldWithElementName("field");
replay();
@@ -83,7 +83,7 @@
@Test
public void record_error_for_field()
{
- Field field = newField("field");
+ Field field = newFieldWithElementName("field");
replay();
@@ -126,9 +126,9 @@
@Test
public void data_survives_serialization() throws Exception
{
- Field fielda = newField("fieldA");
- Field fieldb = newField("fieldB");
- Field fieldc = newField("fieldC");
+ Field fielda = newFieldWithElementName("fieldA");
+ Field fieldb = newFieldWithElementName("fieldB");
+ Field fieldc = newFieldWithElementName("fieldC");
replay();
@@ -156,8 +156,8 @@
@Test
public void clear_removes_all()
{
- Field fielda = newField("fieldA");
- Field fieldb = newField("fieldB");
+ Field fielda = newFieldWithElementName("fieldA");
+ Field fieldb = newFieldWithElementName("fieldB");
replay();
@@ -180,7 +180,7 @@
verify();
}
- private final Field newField(String elementName)
+ private final Field newFieldWithElementName(String elementName)
{
Field field = newField();
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/MinLengthTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/MinLengthTest.java?view=auto&rev=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/MinLengthTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/MinLengthTest.java Sat Jan 20 06:39:08 2007
@@ -0,0 +1,72 @@
+package org.apache.tapestry.validator;
+
+import org.apache.tapestry.Field;
+import org.apache.tapestry.ValidationException;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.ioc.MessageFormatter;
+import org.testng.annotations.Test;
+
+public class MinLengthTest extends InternalBaseTestCase
+{
+ @Test
+ public void blank_value_is_ignored() throws Exception
+ {
+ Field field = newField();
+ MessageFormatter formatter = newMessageFormatter();
+
+ replay();
+
+ MinLength validator = new MinLength();
+
+ validator.check(field, 5, formatter, "");
+
+ verify();
+ }
+
+ @Test
+ public void long_enough() throws Exception
+ {
+ Field field = newField();
+ MessageFormatter formatter = newMessageFormatter();
+ String value = "Now the student has become the master.";
+
+ replay();
+
+ MinLength validator = new MinLength();
+
+ validator.check(field, value.length(), formatter, value);
+
+ verify();
+
+ }
+
+ @Test
+ public void short_value() throws Exception
+ {
+ String label = "My Field";
+ Field field = newFieldWithLabel(label);
+ MessageFormatter formatter = newMessageFormatter();
+ String value = "Now the student has become the master.";
+ String message = "{message}";
+ Integer constraint = value.length() + 1;
+
+ train_format(formatter, message, constraint, label);
+
+ replay();
+
+ MinLength validator = new MinLength();
+
+ try
+ {
+ validator.check(field, constraint, formatter, value);
+ unreachable();
+ }
+ catch (ValidationException ex)
+ {
+ assertEquals(ex.getMessage(), message);
+ }
+
+ verify();
+
+ }
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java?view=diff&rev=498114&r1=498113&r2=498114
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java Sat Jan 20 06:39:08 2007
@@ -25,7 +25,7 @@
@Test
public void null_value()
{
- Field field = newField("My Field");
+ Field field = newFieldWithLabel("My Field");
MessageFormatter formatter = newMessageFormatter();
train_format(formatter, "{message}", "My Field");
@@ -49,7 +49,7 @@
public void blank_value()
{
MessageFormatter formatter = newMessageFormatter();
- Field field = newField("My Field");
+ Field field = newFieldWithLabel("My Field");
train_format(formatter, "{message}", "My Field");
@@ -79,20 +79,5 @@
new Required().check(field, null, formatter, "not null");
verify();
- }
-
- /** Have to put the result before the varargs. */
- protected void train_format(MessageFormatter formatter, String result, Object... arguments)
- {
- expect(formatter.format(arguments)).andReturn(result);
- }
-
- protected final Field newField(String label)
- {
- Field field = newField();
-
- expect(field.getLabel()).andReturn(label).atLeastOnce();
-
- return field;
}
}