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;
     }
 }