Though such generic usage is possible, this family of modules is primarily intended as a framework for creating a resuable set of form and field widgets. On the Perl side, these objects are treated as abstract entities that can be fed input and will produce output in the form that is most convenient for the programmer (e.g., pass a DateTime object to a date picker field to initialize it, and get a DateTime object back from the field when asking for its value).

Fields may be simple (one standard HTML form field per Perl field object) or compound (a field object that serializes to an arbitrary number of HTML tags, but can be addressed as a single logical field internally). Likewise, forms themselves can be nested.

Each field has its own customizable validation, input filter, output filter, internal value (a plain value or a Perl object, whichever is most convenient), output value (the value shown when the field is redisplayed), label, associated error, and any other metadata deemed necessary. Each field can also be serialized to the equivalent set of (X)HTML "hidden" fields.

Forms are expected to be initialized with and return an object or list of objects that the form represents. For example, a registration form could be initialized with and return a UserAccount object.

All labels, errors, and messages used in the bundled form and field widgets are localized in several languages, and you may add your own localized messages and errors using the provided localization framework.

Users are encouraged to create their own libraries of reusable form and field widgets for use on their site. The expectation is that the same kind of field appears in multiple places in any large web application (e.g., username fields, password fields, address forms, etc.) Each field encapsulates a set of values (e.g., options in a pop-up menu), labels, validation constraints, filters, and error messages. Similarly, each form encapsulates a set of fields along with any inter-field validation, error messages, and init-with/object-from methods. Nesting forms and fields preserves this delegation of responsibility, with each higher level having access to its children to perform inter-form/field tasks.

The classes that make up the Rose::HTML::Objects distribution can be used as-is to build forms, fields, and other HTML objects. The provided classes may also be subclassed to change their behavior. When subclassing, however, the interconnected nature of these classes may present some surprises. For example, consider the case of subclassing the Rose::HTML::Form::Field::Option class that represents a single option in a select box or pop-up menu.

What you'll get is an error message like this: "Can't locate object method 'bark' via package 'Rose::HTML::Form::Field::Option' - ..." That's because $option is a plain old Rose::HTML::Form::Field::Option object and not one of your new My::HTML::Form::Field::Option objects that can bark().

Well, one solution is to convince all the Rose::HTML::* classes that might contain option objects to use your new My::HTML::Form::Field::Option subclass instead of the standard Rose::HTML::Form::Field::Option class. But globally altering the behavior of the standard Rose::HTML::* classes is an extremely bad idea. To understand why, imagine that you did so and then tried to incorporate some other code that also uses Rose::HTML::* classes. That other code certainly doesn't expect the changes you've made. It expects the documented behavior for all the classes it's using, and rightfully so.

That's the problem with making class-wide alterations: every piece of code using those classes will see your changes. It's "anti-social behavior" in the context of code sharing and reuse.

The solution is to subclass not just the single class whose behavior is to be altered, but rather to create an entirely separate namespace for a full hierarchy of classes within which you can make your changes in isolation. This is called a "private library," and the Rose::HTML::Objects class contains methods for creating one, either dynamically in memory, or on disk in the form of actial *.pm Perl module files.

Let's try the example above again, but this time using a private library. We will use the the make_private_library class method to do this. The reference documentation for this method appears below, but you should get a good idea of its functionality by reading the usage examples here.

First, let's create an in-memory private library to contain our changes. The make_private_library method accepts a hash of class name/code pairs containing customizations to be incorporated into one or more of the classes in the newly created private library. Let's use the My:: prefix for our private library. Here's a hash containing just our custom code:

Note that the code is provided as a string, not a code reference. Be sure to use the appropriate quoting mechanism (a single-quoted "here document" in this case) to protect your code from unintended variable interpolation.

Now we have a full hierarchy of My::-prefixed classes, one for each public Rose:: class in the Rose::HTML::Objects distribution. Let's try the problematic code from earlier, this time using one of our new classes.

Success! Of course, this dynamic in-memory class creation is relatively heavyweight. It necessarily has to have all the classes in memory. Creating a private library on disk allows you to load only the classes you need. It also provides an easier means of making your customizations persistent. Editing the actual *.pm files on disk means that your changes can be tracked on a per-file basis by your version control system, and so on. We can still use the %code hash from the in-memory example to "seed" the classes; the make_private_library method will insert our custom code into the initial *.pm files it generates.

To create a private library on disk, we need to provide a path to the directory where the generated files will be placed. The appropriate directory hierarchy will be created below it (e.g., the path to the My::HTML::Form Perl module file will be My/HTML/Form.pm, starting beneath the specified modules_dir). Let's do it:

And it works. Note that if the call to make_private_library that creates the Perl module files on disk was in the same file as the code above, the My::HTML::Form::Field::PopUpMenu class would have to be required rather than used. (All use statements are evaluated at compile time, but the My::HTML::Form::Field::PopUpMenu class is not created until the make_private_library call is executed, which happens at runtime in this example.)

One final example. Suppose you want to add or override a method in all HTML object classes within your private library. To facilitate this, the make_private_library method will create a mix-in class which will be placed at the front of the inheritence chain (i.e., the first item in the @ISA array) of all generated subclasses. Given a prefix of My:: as in the example above, this custom class will be called My::HTML::Object::Custom. It comes pre-populated with an initial set of private-library-wide information such as the object_type_class mapping and the default_localizer (all of which will be populated with your My::* subclasses, naturally). Simply add your own methods to this module:

package My::HTML::Object::Custom;
...
sub chirp
{
print "tweet!\n";
}

Now the chirp() method will appear in all other HTML object classes in your private library.

The most important actor in the localization process is, predictably, the localizer, and the most important aspect of the localizer is the way in which it's accessed.

The general approach is that each object that is or contains something that needs to be localized has a localizer() method through which it accesses its localizer object. These methods check for a local localizer object attribute, and if one is not found, the method looks "up the chain" until it finds one. The chain may include parent objects or class hierarchies. Eventually, the assumption is that a localizer will be found and returned.

In the most granular case, this allows each localized object to have its own individual localizer. In the more common (and default) case, there is a single localizer object camped out at some higher point in the chain of lookups, and this localizer serves all objects.

The default localizer class, Rose::HTML::Object::Message::Localizer, reads localized message text from the __DATA__ sections of the Perl module files that make up the Rose::HTML::Objects distribution. This is done mostly because it's the most convenient way to include the "built-in" localized message text in this CPAN module distribution. (See the Rose::HTML::Object::Message::Localizer documentation for more information.) Localized message text is stored in memory within the localizer object itself.

You can change both the source and storage of localized message text by creating your own localizer subclass. The key, of course, is to ensure that your localizer subclass is used instead the default localizer class by all objects. Thankfully, the creation of a private library takes care of that, both creating a localizer subclass and ensuring that it is accessible everywhere.

Here's a simple example of a customized localizer that overrides just one method, get_localized_message_text, to add three stars *** around the built-in message text.

This is a silly example, obviously, but it does demonstrate how easy it is to alter the default behavior. A more useful example might be to look elsewhere for a message first, then fall back to the default mechanism. This requires actually unpacking the method arguments (as opposed to simply passing them on to the superclass call in the example above), but is otherwise not much more complex:

By overriding this and othr methods in the Rose::HTML::Object::Message::Localizer class, your localizer subclass could choose to entirely ignore the default mechanism for localized text storage and retrieval.

Here's an example of a new field subclass that uses localized messages and errors. It will use the default localized text mechanism to the sake of simplicity (i.e., text stored in __DATA__ sections of Perl modules). It's a "nickname" field intended to be used as part of a localized form that asks for user information. For the sake of demonstrating validation, let's say we've decided that nicknames may not contain space characters.

The first step is to define our message and error ids. These should be added to the generated My::HTML::Object::Messages and My::HTML::Object::Errors classes, respectively. You can do this during private library generation by adding to the code hash passed to the make_private_library call, or by editing the generated files on disk. (The relevant sections are indicated with comments that make_private_library will place in the generated *.pm files.) First, the message ids:

Now the error ids. Note that the error and message id numbers for each error message (just FIELD_ERROR_BAD_NICKNAME in this case) should be the same in order to take advantage of the default behavior of the message_for_error_id method.

Of course, you'll rarely instantiate a field in isolation. It will usually be part of a form. Similarly, you will rarely set the locale of a field directly. Instead, you will set the locale of the entire form and let the fields use that locale, accessed through the delegation chain searched when the locale method is called on a field object. Example:

Or you could set the locale on the localizer itself for a similar effect.

Also note the use of the label within the "bad nickname" error message. In general, incorporating (independently set, remember) labels into messages like this tends to lead to translation issues. (Is the label masculine? Feminine? Singular? Dual? Plural? Etc.) I've done so here to demonstrate that one localized message can be incorporated into another localized message, with both dynamically matching their locales based on the locale set higher up in the object hierarchy.

Create a comprehensive collection of Rose::HTML::* subclasses, either in memory or as *.pm files on disk, in order to provide a convenient and isolated location for your customizations. Please read the private libraries section above for more information.

A reference to a subroutine that takes a Rose::HTML::* class name as its argument and returns true if a subclass should be created for this class, false otherwise. The class name will also be available in $_. If this parameter is omitted, all classes are subclassed.

A reference to a hash containing code to be added to subclasses. The keys of the hash are the subclass class names (i.e., the names after the application of the rename code or the trim_prefix/prefix processing).

The value for each key may be either a string containing Perl code or a reference to a hash containing a code key whose value is a string containing Perl code and a filter key whose value is a reference to a subroutine used to filter the code.

The filter subroutine will be passed a reference to a scalar containing the full Perl code for a subclass and is expected to modify it directly. The Perl code will also be available in $_. Example:

This will create my_method() in the My::HTML::Object class and, with the __FOO__ removed, my_other_method() will be created in the My::HTML::Form class.

Note that the use of this parameter is optional. You can always add your code to the Perl module files after they've been generated, or add your code directly into memory after the classes have been created in_memory.

A reference to a subroutine used to filter the Perl code for all generated subclasses. This filter will run before any subclass-specific filter (see the code parameter above for an explanation). This subroutine will be passed a reference to a scalar containing the Perl code and is expected to modify it directly. The Perl code will also be available in $_.

The path to the directory under which all *.pm Perl module files will be created. The modules will be created in the expected tree structure. For example, the My::HTML::Object class will be in the file My/HTML/Object.pm beneath the modules_dir PATH. This parameter is ignored if the in_memory parameter is passed.

The class name prefix with which to replace the trim_prefix in all subclass class names. For example, a prefix value of My:: combined with the (default) trim_prefix of Rose:: would take a class named Rose::HTML::Whatever and produce a subclass named My::HTML::Whatever. You must pass this parameter or the rename parameter.

A reference to a subroutine that takes a Rose::HTML::* class name as its argument and returns an appropriate subclass name. The name argument is also available in the $_ variable, enabling code like this:

The prefix string to be removed from each Rose::HTML::* class name. This parameter is only relevant when the prefix parameter is passed (and the rename parameter is not). Defaults to Rose:: if this parameter is not passed.