[Catalyst-commits] r14423 - in trunk/examples/CatalystAdvent/root/2012: . pen

jnapiorkowski at dev.catalyst.perl.org jnapiorkowski at dev.catalyst.perl.org
Sat Dec 22 21:49:15 GMT 2012


Author: jnapiorkowski
Date: 2012-12-22 21:49:15 +0000 (Sat, 22 Dec 2012)
New Revision: 14423

Added:
   trunk/examples/CatalystAdvent/root/2012/23.pod
   trunk/examples/CatalystAdvent/root/2012/24.pod
   trunk/examples/CatalystAdvent/root/2012/25.pod
Removed:
   trunk/examples/CatalystAdvent/root/2012/pen/html-formhandler-jquery-validation.pod
Log:
set three articles to publish status

Copied: trunk/examples/CatalystAdvent/root/2012/23.pod (from rev 14422, trunk/examples/CatalystAdvent/root/2012/pen/html-formhandler-jquery-validation.pod)
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/23.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/23.pod	2012-12-22 21:49:15 UTC (rev 14423)
@@ -0,0 +1,256 @@
+=head1 HTML::FormHandler & jQuery Validator
+
+=head1 OVERVIEW
+
+=head2 HTML::FormHandler
+
+L<HTML::FormHandler> maintains a clean separation between form construction
+and form rendering. It allows you to define your forms and fields in a number
+of flexible ways. Although it provides renderers for HTML, you can define
+custom renderers for any kind of presentation. HTML::FormHandler allows you to
+define form fields and validators. It can be used for both database and
+non-database forms, and will automatically update or create rows in a
+database. It can be used to process structured data that doesn't come from an
+HTML form.
+
+=head2 HTML::FormHandlerX::Form::JQueryValidator
+
+The L<HTML::FormHandlerX::Form::JQueryValidator> is a simple role which
+provides convinient mechanism to create a simple jQuery validation profile
+from our form definition.
+
+=head2 jQuery Validator
+
+L<jQuery Validator|https://github.com/jzaefferer/jquery-validation> makes
+clientside form validation easy and at the same time provides a lot of options
+to customize the validation plugin to your specific needs. The plugin comes
+with useful commonly used set of validation methods such as validation URL,
+email addresses, zip codes, phone numbers, credit card numbers and etc. All
+these methods come with default error messages which are available in
+more than 30 different languages.
+
+=head2 Simple Order Form
+
+Let's wrap all together and create a simple order form. First of all we start
+with our form defition. Our form looks pretty standard including First & Last
+name, Address, City, Zip Code, Country and Credit card information.
+
+  package MyApp::Form::Order;
+  use MooseX::Types::CreditCard qw/CardNumber CardSecurityCode CardExpiration/;
+  use HTML::FormHandler::Types ':all';
+  use HTML::FormHandler::Moose;
+  extends 'MyApp::Form::Base';
+  with 'HTML::FormHandler::Widget::Theme::Bootstrap';
+  
+  has '+item_class' => ( default => 'Order' );
+  
+  has_field 'first_name' => (
+      type             => 'Text',
+      required         => 1,
+      required_message => 'Please enter your first name.',
+      label            => 'First name',
+  );
+  
+  has_field 'last_name' => (
+      label            => 'Last name',
+      type             => 'Text',
+      required         => 1,
+      required_message => 'Please enter your last name.',
+  );
+  
+  has_field 'address' => (
+      label            => 'Address',
+      type             => 'Text',
+      required         => 1,
+      required_message => 'Please enter your last name.',
+  );
+  
+  has_field 'zip_code' => (
+      label            => 'Zip code',
+      type             => 'Text',
+      apply            => [ Zip ],
+      required         => 1,
+      required_message => 'Please enter valid zip code',
+  );
+  
+  has_field 'city' => (
+      label            => 'City',
+      type             => 'Text',
+      required         => 1,
+      required_message => 'Please enter city.',
+  );
+  
+  has_field 'country' => (
+      label            => 'Country',
+      type             => 'Select',
+      empty_select     => 'Choose country',
+      required         => 1,
+      required_message => 'Please enter your country.',
+  );
+  
+  has_field 'card_type' => (
+      label            => 'Credit card type',
+      type             => 'Select',
+  );
+  
+  has_field 'card_number' => (
+      label            => 'Credit card number',
+      type             => 'Text',
+      element_class    => [qw/creditcard/],
+      apply            => [ CardNumber ],
+      required         => 1,
+      required_message => 'Please enter valid credit card number.',
+  );
+  
+  has_field 'exp_date' => (
+      label            => 'Expiration',
+      type             => 'Date',
+      format           => '%d/%y', # example: 12/12
+      element_class    => [qw/input-mini date/],
+      element_attr     => { placeholder => 'Ex. 12/12', },
+      required         => 1,
+      required_message => 'Please enter valid expiration date',
+  );
+  
+  has_field 'cvc_code' => (
+      label            => 'Card Verification Code',
+      type             => 'Integer',
+      element_class    => [qw/input-mini number/],
+      apply            => [ CardSecurityCode ],
+      required         => 1,
+      required_message => 'Please enter CVC',
+  );
+  
+  has_field 'comment' => ( type  => 'Text' );
+  
+  has_field 'submit'  => ( type => 'Submit', value => 'Process order', element_class => ['btn'] );
+  
+  sub options_card_type {
+      my $self = shift;
+      my @options = map { $_ => $_ } qw(Visa MasterCard AmericanExpress Discover);
+      return \@options;
+  }
+  
+  sub options_country {
+      my $self = shift;
+      my @options = (
+          { value => 'US', label => 'United States' },
+          { value => 'DE', label => 'Germany' },
+          { value => 'FR', label => 'France' },
+          { value => 'BG', label => 'Bulgaria' },
+      );
+      return \@options;
+  }
+  
+  __PACKAGE__->meta->make_immutable;
+  no HTML::FormHandler::Moose;
+  1;
+
+Let's take a look at our form fields keywords. Keywods like type, label,
+required, required_message are mostly self-explanory. The apply keyword
+specify an array refference of constraints and coercion which are executed at
+validation time. The element_class defines an array of classes to include on
+the defined element. This is pretty convinient, because jQuery Validator
+allows us to specify the validation methods as classes.
+For our convinience we've created a small base class called MyApp::Form::Base.
+
+  package MyApp::Form::Base;
+  use utf8;
+  use HTML::FormHandler::Moose;
+  extends 'HTML::FormHandler';
+  
+  with 'HTML::FormHandlerX::Form::JQueryValidator';
+  
+  has_field validation_json => ( type => 'Hidden',  element_attr => { disabled => 'disabled' } );
+  
+  sub default_validation_json { shift->as_escaped_json }
+  
+  sub html_attributes {
+      my ( $self, $field, $type, $attr ) = @_;
+      if ($type eq 'label' && $field->{required}) {
+          my $label = $field->{label};
+          $field->{label} = "$label *" unless $label =~ /\*$/; # we append it once only
+      }
+      return $attr;
+  }
+  
+  1;
+
+Our Base form class have 2 purposes. First, we define a C<html_attributes>
+method, which appends a '*' to the label of each required field. Second, it
+adds a hidden field containing the jQuery Validation profile as encoded json.
+We don't want the validation_json to be submitted when we submit our form
+data and that's why we add an element attribute disable to the
+C<validation_json> field.
+
+
+Now let's display our form and enable the jQuery Validation Plugin.
+
+  # In our Controller
+  ...
+
+  use MyApp::Form::Order;
+  
+  has 'order_form' => ( isa => 'MyApp::Form::Order', is => 'rw',
+     lazy => 1, default => sub { MyApp::Form::Order->new } );
+  
+  BEGIN { extends 'Catalyst::Controller' }
+
+  sub order : Chained('base') PathPart('order') Args(0) {
+      my ( $self, $c ) = @_;
+      my $result = $self->order_form->run(
+          params => $c->req->parameters,
+          action => $c->uri_for($c->action, $c->req->captures ),
+      );
+      $c->stash( form => $result );
+      return unless $result->validated;
+      $c->res->redirect( $c->uri_for('/') );
+  }
+
+  1;
+
+Our template file contains just few lines. The HTML::FormHandler provides a
+render method, which renders the entire form for us!
+  
+  [% form.render %]
+
+  $(document).ready(function() {
+    if ($('#validation_json').length > 0) {
+      var validationJSON = JSON.parse(decodeURIComponent($('#validation_json').val()));
+      $('.form-horizontal').validate({
+          rules: validationJSON.rules,
+          messages: validationJSON.messages
+      });
+    }
+  });
+
+And that is all! We have nice looking form, both validated front-end and
+server-side and automatically integrated with DBIx::Class using the
+L<HTML::FormHandler::TraitFor::Model::DBIC> trait.
+
+And here is the final example: 
+
+=begin xhtml
+
+<img src="/calendar/static/images/2012/formhandler-jquery-validator.png" />
+
+=end xhtml
+
+=head1 Demo
+
+Please check the live demonstration: http://formhandler-perl.dotcloud.com/examples/order/
+
+=head1 For More Information
+
+For more information please check:
+L<HTML::FormHandler::Manual::Tutorial>,
+L<HTML::FormHandlerX::Form::JQueryValidator>, 
+L<jQuery Validator Plugin|https://github.com/jzaefferer/jquery-validation.git>
+
+=head1 Summary
+
+=head1 Author
+
+Dimitar Petrov <dcpetrov at cpan.org> dpetrov
+
+=cut

Added: trunk/examples/CatalystAdvent/root/2012/24.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/24.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/24.pod	2012-12-22 21:49:15 UTC (rev 14423)
@@ -0,0 +1,188 @@
+=head1 An alternative View Engine
+
+Do you remember the good old times where CGI scripts ware scattered with
+heredocs and print statements containing snippets of HTML? Some
+time passed by since then and in the meantime every Framework offers some
+ways for generating HTML using a View layer.
+
+When looking at the basic principles behind the various View implementations
+you can find various alternative approaches:
+
+=over
+
+=item pure HTML
+
+Some view engines are using pure HTML files without any extra content. The
+HTML markup might contain placeholder text or simply is empty. Generating the
+HTML for delivery to the browser means gluing the predefined template files
+together after filling certain parts with meaningful content. Another option
+could be repeating or deleting certain blocks and filling in some content
+during the repetition process.
+
+Working with engines like these typically is very much like using jQuery on
+the browser.
+
+Examples for view engines of this kind are:
+
+L<Catalyst::View::HTML::Zoom|https://metacpan.org/module/Catalyst::View::HTML::Zoom>,
+L<Template::Flute|https://metacpan.org/module/Template::Flute>
+
+=item mixed HTML and processing instructions
+
+Most templating engines work like this. Some kind of extra language is available
+to allow conditions, repetitions or filling-in content.
+
+Example for a view engine of this kind: 
+L<Catalyst::View::TT|https://metacpan.org/module/Catalyst::View::TT>,
+L<Catalyst::View::XSlate|https://metacpan.org/module/Catalyst::View::Xslate>
+
+=item different syntax to HTML
+
+Instead of typing in HTML syntax directly, some Engines offer a different
+syntax (or a DSL if you like) for generating HTML output. This is what we
+observe today.
+
+Examples for view engines of this kind are:
+L<Catalyst::View::Template::Declare|https://metacpan.org/module/Catalyst::View::Template::Declare>, 
+L<Markapl|https://metacpan.org/module/Markapl>, 
+L<Catalyst::View::ByCode|https://metacpan.org/module/Catalyst::View::ByCode>,
+L<Catalyst::View::Haml|https://metacpan.org/module/Catalyst::View::Haml>,
+L<Template::Caribou|https://metacpan.org/module/Template::Caribou>
+
+=item another language exporting to HTML
+
+Finally one could use a completely different markup and generate HTML from it.
+Examples could be
+L<Text::Markdown|https://metacpan.org/module/Text::Markdown>, POD as well as
+any other type of widget library that might generate HTML from its content.
+
+=back
+
+Today, we will look into Catalyst::View::ByCode.
+
+=head1 What is Catalyst::View::ByCode?
+
+Basically, Catalyst::View::ByCode offers a subroutine for every known HTML Tag.
+The subs are available inside every template. The only exceptions are tags
+whose names would otherwise collide with Perl's built-in subs (like "s", "tr")
+or would interfere with Moose (like "meta"). These subs have names
+different from the HTML-Tag. Typically all Template files reside an a directory
+named "bycode" inside the "root" directory. The template name is guessed by
+concatenating the Controller's namespace and the action name. All template
+files are pure perl and they are compiled during their first execution by
+wrapping the file contents with some extra things the template author does
+not like to write every time. Nothing you need to worry.
+
+A templating block is added to a file with the "template" keyword followed
+by a code reference that contains the template content.
+
+    # a file inside 'root/bycode/...'
+    
+    template {
+        # your markup goes here
+    };
+
+Additionally, L<Devel::Declare|https://metacpan.org/module/Devel::Declare>
+acts in the background and mangles the template code at compile time to allow
+a nicer syntax that is closer to HTML than regular Perl syntax would be.
+
+    div some_id.hidden(foo => 'bar') { 'some content' };
+
+would generate this HTML:
+
+    <div id="some_id" class="hidden" foo="bar">some content</div>
+
+The content-part of every HTML-Tag is a code reference whose last expression
+is added to the content of the Tag as HTML-escaped text.
+
+If there is more text to write, two predefined globs may get used.
+
+    div {
+        # unescaped text -- be careful
+        print RAW '<?foo bar="42" ?>';
+        
+        # some other tag
+        strong { 'Santa is coming soon' };
+        
+        # escaped text
+        print OUT '{ foo => 42 }';
+    };
+
+And there is an idea borrowed from Template::Declare: modifiable attributes
+with some magic subroutines usable inside a Tag's code-ref.
+
+    div {
+        attr foo => 42;
+        id "element_$i";
+        class '+visible' if $visible;
+        
+        ...
+    };
+
+=head1 Building your own Tag-like things
+
+So far, we only used the standard HTML Tags for creating our markup. However,
+every website has common building blocks. Would be great if we could use a
+similar syntax for creating such blocks. This is what the "block" keyword
+is for.
+
+    # build a block
+    block info_box {
+        # read attributes from definition
+        my $head = attr('head') // 'headline';
+        my $foo = attr('foo')   // 'default_value';
+        
+        div.some_class {
+            h3 { $head };
+            div.content {
+                # insert markup from callee
+                block_content;
+            };
+        };
+    };
+    
+    # use this block:
+    info_box(head => 'Some Title') {
+        # box content goes here
+    };
+
+=head1 Construct your scaffolding
+
+Every page callable via an URL must provide a structure in order to be
+valid HTML. Repeating such a structure every time must be avoided.
+
+A specially named template file, "wrapper.pl" is called before rendering the
+template in question. This behavior can be changed when setting the "wrapper"
+stash variable to some other file name or undef for having no wrapping behavior
+at all. The special keyword "yield" executes the template requested and inserts
+the generated markup at this very position.
+
+    # file "wrapper.pl"
+    html {
+        head { ... };
+        body {
+            # TODO: header
+            # TODO: navigation
+            yield;
+            # TODO: side bars
+            # TODO: footer
+        };
+    };
+
+=head1 Conclusion
+
+There are many options for choosing a templating engine. There is no "best"
+engine available. Choose your engine based on the requirements of your
+workflow and the skills and tools available by the people involved. If you do
+HTML-coding by hand, an engine like Catalyst::View::ByCode may be interesting
+for you. The win is a guarantee to generate valid HTML code and properly
+escaped Text with high speed. On the other hand, if you get HTML Templates
+from designers, it would be a bad choice.
+
+=head1 See also
+
+L<Catalyst::View::ByCode|https://metacpan.org/module/Catalyst::View::ByCode>
+
+=head1 Author
+
+Wolfgang Kinkeldei E<lt>wolfgang [at] kinkeldei [dot] deE<gt>

Added: trunk/examples/CatalystAdvent/root/2012/25.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/25.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/25.pod	2012-12-22 21:49:15 UTC (rev 14423)
@@ -0,0 +1,137 @@
+=head1 Combining assets
+
+=head1 Overview
+
+How do you organize your assets like JavaScript or CSS? Does every request
+trigger several dozen additional requests needed for loading all of these
+resources? Do you often forget to run your static-CSS-builder script prior
+to viewing your local web application? Or do you hate to update something
+in the middle of your handcrafted jQuery-and-all-the-other-JS file?
+
+=head1 Understanding the browser
+
+When looking at the typical "waterfall" inside your favourite browser's
+developer tools you may have observed that JavaScript and CSS assets start loading
+after the HTML content got loaded to the point where the E<lt>scriptE<gt> or
+E<lt>linkE<gt> tags reside. Some browsers even start loading these assets
+after the entire HTML markup has been fully loaded. Depending on the browser
+the loading uses a maximum number of parallel requests. In order to keep the
+loading time small one would like to minimize the amount of data to get
+transferred and the number of requests needed to load all the assets.
+
+On the other hand, maintaining small well-named files each being responsible
+for a single task is much easier to handle. Specifying dependencies is very
+simple when looking at small files but can be hard to decide when facing a
+huge file.
+
+These requirements quickly lead to the temptation to express dependencies
+in a data structure (all file names are randomly chosen):
+
+    my %dependency_for = (
+        'site.js'       => [ qw(uploader.js forms.js) ],
+        'uploader.js'   => [ qw(jquery.js) ],
+        'forms.js'      => [ qw(jquery_ui.js) ],
+        'jquery_ui.js'  => [ qw(jquery.js) ],
+    );
+
+It would be great if you could point your browser to
+http://yourdomain.tld/js/site.js and could get back a stream full of all
+JavaScript needed to run your site.js and have all dependencies resolved.
+
+=head1 Combining assets
+
+Fortunately, there is a helper module on CPAN:
+L<Catalyst::Controller::Combine|https://metacpan.org/module/Catalyst::Controller::Combine>
+
+Ater installation visit your favourite shell and fire
+
+    script/myapp_create.pl controller Js Combine
+
+for creating a "Js" controller (having the expected namespace "js") and consuming
+all URLs starting with F</js/>. By default, it expects its files to reside in
+F<root/static/js>. Simply create this directory unless already present and
+populate it with some JavaScript files. CSS is very similar and just differs
+by the name and path chosen.
+
+Now you have two choices:
+
+=over
+
+=item using a long URL
+
+If you have a countable number of JavaScript (or CSS) files, you may put
+them flat into the asset directory. Then, construct an URL listing all
+files you like to combine (with or without the .js extension) into one
+long pseudo-path. This will trigger the loading of every single file
+being listed in the URL and your browser will receive a stream with all your
+assets combined. 
+
+A URL might look like: L<http://yoursite.tld/js/jquery/uploader/forms/site.js>
+
+However, everybody can follow your effort, directly see your dependencies (or
+load the individual files directly) and you will have to modify this URL at a
+place different from your combining controller. So you might decide not to use
+this mode.
+
+=item specifying dependencies
+
+In order to keep your URLs simple you can specify dependencies like the
+hash listed above. Edit your "Js" controller and you will see a sample
+config entry you might modify to your needs.
+
+    __PACKAGE__->config(
+        depend => {
+            'site.js'       => [ qw(uploader.js forms.js) ],
+            'uploader.js'   => [ qw(jquery.js) ],
+            'forms.js'      => [ qw(jquery_ui.js) ],
+            'jquery_ui.js'  => [ qw(jquery.js) ],
+        },
+    );
+
+Now you may point your browser to L<http://yoursite.tld/js/site.js> and you
+will receive the expected stream with all specified dependencies resolved.
+
+=back
+
+=head1 Minification
+
+There are two great modules on CPAN:
+L<JavaScript::Minifier::XS|https://metacpan.org/module/JavaScript::Minifier::XS> and
+L<CSS::Minifier::XS|https://metacpan.org/module/CSS::Minifier::XS>.
+
+Both offer a sub named C<minify> that returns the minified version of its
+argument. If you simply use one of both modules in your Controller, the 
+combined asset will automatically get minified.
+
+If you plan to use or create a minifier of your own, simply add a C<minify>
+method that will automatically get invoked when present.
+
+=head1 Performance
+
+Usually, performance is not an issue if you just combine assets and minify
+them. If you encounter performance bottlenecks or plan to use a costly processor
+like L<sass|http://sass-lang.com> for processing your asset, you may consider
+to generate your assets by loading the URLs immediately after deployment and
+save the generated asset files at a location where your web server may
+be able to deliver them statically.
+
+A further improvement could be to gzip your response which is an option
+of your frontend web server or can be enabled with the right Plack middleware.
+
+=head1 See also
+
+L<Catalyst::Controller::Combine|https://metacpan.org/module/Catalyst::Controller::Combine>
+
+=head1 Summary
+
+With a simple helper controller you may keep your JavaScript or CSS URLs
+simple but enable delivering a complete resource with one request to your
+visitor's browsers while still keeping your assets maintainable. There
+is nothing you can forget during your development and no other tool you will
+have to invoke.
+
+=head1 Author
+
+Wolfgang Kinkeldei E<lt>wolfgang [at] kinkeldei [dot] deE<gt>
+
+=cut

Deleted: trunk/examples/CatalystAdvent/root/2012/pen/html-formhandler-jquery-validation.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/pen/html-formhandler-jquery-validation.pod	2012-12-19 09:17:42 UTC (rev 14422)
+++ trunk/examples/CatalystAdvent/root/2012/pen/html-formhandler-jquery-validation.pod	2012-12-22 21:49:15 UTC (rev 14423)
@@ -1,250 +0,0 @@
-=head1 HTML::FormHandler & jQuery Validator
-
-=head1 OVERVIEW
-
-=head2 HTML::FormHandler
-
-L<HTML::FormHandler> maintains a clean separation between form construction
-and form rendering. It allows you to define your forms and fields in a number
-of flexible ways. Although it provides renderers for HTML, you can define
-custom renderers for any kind of presentation. HTML::FormHandler allows you to
-define form fields and validators. It can be used for both database and
-non-database forms, and will automatically update or create rows in a
-database. It can be used to process structured data that doesn't come from an
-HTML form.
-
-=head2 HTML::FormHandlerX::Form::JQueryValidator
-
-The L<HTML::FormHandlerX::Form::JQueryValidator> is a simple role which
-provides convinient mechanism to create a simple jQuery validation profile
-from our form definition.
-
-=head2 jQuery Validator
-
-L<jQuery Validator|https://github.com/jzaefferer/jquery-validation> makes
-clientside form validation easy and at the same time provides a lot of options
-to customize the validation plugin to your specific needs. The plugin comes
-with useful commonly used set of validation methods such as validation URL,
-email addresses, zip codes, phone numbers, credit card numbers and etc. All
-these methods come with default error messages which are available in
-more than 30 different languages.
-
-=head2 Simple Order Form
-
-Let's wrap all together and create a simple order form. First of all we start
-with our form defition. Our form looks pretty standard including First & Last
-name, Address, City, Zip Code, Country and Credit card information.
-
-  package MyApp::Form::Order;
-  use MooseX::Types::CreditCard qw/CardNumber CardSecurityCode CardExpiration/;
-  use HTML::FormHandler::Types ':all';
-  use HTML::FormHandler::Moose;
-  extends 'MyApp::Form::Base';
-  with 'HTML::FormHandler::Widget::Theme::Bootstrap';
-  
-  has '+item_class' => ( default => 'Order' );
-  
-  has_field 'first_name' => (
-      type             => 'Text',
-      required         => 1,
-      required_message => 'Please enter your first name.',
-      label            => 'First name',
-  );
-  
-  has_field 'last_name' => (
-      label            => 'Last name',
-      type             => 'Text',
-      required         => 1,
-      required_message => 'Please enter your last name.',
-  );
-  
-  has_field 'address' => (
-      label            => 'Address',
-      type             => 'Text',
-      required         => 1,
-      required_message => 'Please enter your last name.',
-  );
-  
-  has_field 'zip_code' => (
-      label            => 'Zip code',
-      type             => 'Text',
-      apply            => [ Zip ],
-      required         => 1,
-      required_message => 'Please enter valid zip code',
-  );
-  
-  has_field 'city' => (
-      label            => 'City',
-      type             => 'Text',
-      required         => 1,
-      required_message => 'Please enter city.',
-  );
-  
-  has_field 'country' => (
-      label            => 'Country',
-      type             => 'Select',
-      empty_select     => 'Choose country',
-      required         => 1,
-      required_message => 'Please enter your country.',
-  );
-  
-  has_field 'card_type' => (
-      label            => 'Credit card type',
-      type             => 'Select',
-  );
-  
-  has_field 'card_number' => (
-      label            => 'Credit card number',
-      type             => 'Text',
-      element_class    => [qw/creditcard/],
-      apply            => [ CardNumber ],
-      required         => 1,
-      required_message => 'Please enter valid credit card number.',
-  );
-  
-  has_field 'exp_date' => (
-      label            => 'Expiration',
-      type             => 'Date',
-      format           => '%d/%y', # example: 12/12
-      element_class    => [qw/input-mini date/],
-      element_attr     => { placeholder => 'Ex. 12/12', },
-      required         => 1,
-      required_message => 'Please enter valid expiration date',
-  );
-  
-  has_field 'cvc_code' => (
-      label            => 'Card Verification Code',
-      type             => 'Integer',
-      element_class    => [qw/input-mini number/],
-      apply            => [ CardSecurityCode ],
-      required         => 1,
-      required_message => 'Please enter CVC',
-  );
-  
-  has_field 'comment' => ( type  => 'Text' );
-  
-  has_field 'submit'  => ( type => 'Submit', value => 'Process order', element_class => ['btn'] );
-  
-  sub options_card_type {
-      my $self = shift;
-      my @options = map { $_ => $_ } qw(Visa MasterCard AmericanExpress Discover);
-      return \@options;
-  }
-  
-  sub options_country {
-      my $self = shift;
-      my @options = (
-          { value => 'US', label => 'United States' },
-          { value => 'DE', label => 'Germany' },
-          { value => 'FR', label => 'France' },
-          { value => 'BG', label => 'Bulgaria' },
-      );
-      return \@options;
-  }
-  
-  __PACKAGE__->meta->make_immutable;
-  no HTML::FormHandler::Moose;
-  1;
-
-Let's take a look at our form fields keywords. Keywods like type, label,
-required, required_message are mostly self-explanory. The apply keyword
-specify an array refference of constraints and coercion which are executed at
-validation time. The element_class defines an array of classes to include on
-the defined element. This is pretty convinient, because jQuery Validator
-allows us to specify the validation methods as classes.
-For our convinience we've created a small base class called MyApp::Form::Base.
-
-  package MyApp::Form::Base;
-  use utf8;
-  use HTML::FormHandler::Moose;
-  extends 'HTML::FormHandler';
-  
-  with 'HTML::FormHandlerX::Form::JQueryValidator';
-  
-  has_field validation_json => ( type => 'Hidden',  element_attr => { disabled => 'disabled' } );
-  
-  sub default_validation_json { shift->as_escaped_json }
-  
-  sub html_attributes {
-      my ( $self, $field, $type, $attr ) = @_;
-      if ($type eq 'label' && $field->{required}) {
-          my $label = $field->{label};
-          $field->{label} = "$label *" unless $label =~ /\*$/; # we append it once only
-      }
-      return $attr;
-  }
-  
-  1;
-
-Our Base form class have 2 purposes. First, we define a C<html_attributes>
-method, which appends a '*' to the label of each required field. Second, it
-adds a hidden field containing the jQuery Validation profile as encoded json.
-We don't want the validation_json to be submitted when we submit our form
-data and that's why we add an element attribute disable to the
-C<validation_json> field.
-
-
-Now let's display our form and enable the jQuery Validation Plugin.
-
-  # In our Controller
-  ...
-
-  use MyApp::Form::Order;
-  
-  has 'order_form' => ( isa => 'MyApp::Form::Order', is => 'rw',
-     lazy => 1, default => sub { MyApp::Form::Order->new } );
-  
-  BEGIN { extends 'Catalyst::Controller' }
-
-  sub order : Chained('base') PathPart('order') Args(0) {
-      my ( $self, $c ) = @_;
-      my $result = $self->order_form->run(
-          params => $c->req->parameters,
-          action => $c->uri_for($c->action, $c->req->captures ),
-      );
-      $c->stash( form => $result );
-      return unless $result->validated;
-      $c->res->redirect( $c->uri_for('/') );
-  }
-
-  1;
-
-Our template file contains just few lines. The HTML::FormHandler provides a
-render method, which renders the entire form for us!
-  
-  [% form.render %]
-
-  $(document).ready(function() {
-    if ($('#validation_json').length > 0) {
-      var validationJSON = JSON.parse(decodeURIComponent($('#validation_json').val()));
-      $('.form-horizontal').validate({
-          rules: validationJSON.rules,
-          messages: validationJSON.messages
-      });
-    }
-  });
-
-And that is all! We have nice looking form, both validated front-end and
-server-side and automatically integrated with DBIx::Class using the
-L<HTML::FormHandler::TraitFor::Model::DBIC> trait.
-
-And here is the final example: <img src="/calendar/static/images/2012/formhandler-jquery-validator.png" />
-
-=head1 Demo
-
-Please check the live demonstration: http://formhandler-perl.dotcloud.com/examples/order/
-
-=head1 For More Information
-
-For more information please check:
-L<HTML::FormHandler::Manual::Tutorial>,
-L<HTML::FormHandlerX::Form::JQueryValidator>, 
-L<jQuery Validator Plugin|https://github.com/jzaefferer/jquery-validation.git>
-
-=head1 Summary
-
-=head1 Author
-
-Dimitar Petrov <dcpetrov at cpan.org> dpetrov
-
-=cut




More information about the Catalyst-commits mailing list