December 2012 Archives

A Killer Feature for Mojolicious

I just thought of a truly killer feature for Mojolicious. You are undoubtedly asking yourself why I don't just share this with the Mojolicious team on IRC or GitHub, etc. Most of you undoubtedly already know the answer to that, but I digress.

Many shops that I've worked with have this concept generally referred to as content-injection or template filtering depending on how you go about it. It is basically a means of hooking into template rendering after the template has been rendered but before it has been output (or otherwise used by the application).

The problem this solves is that some things are best left to be handled by the template, while other things are best done by using regex or a DOM parser.

I believe Mojolicious is at a unique advantage to offer something like this out-of-the-box because it has it's own templating system -and- it's own DOM parser. An example of a mojolicious template using the technique I'm describing could be as follows:

__DATA__

@@ alter_me.html.ep
<!DOCTYPE html>
<html>
  <head>
    <title>Alter me!</title>
  </head>
  <body>You've been altered.</body>
</html>

% filter begin
% my ($c, $dom) = @_; # is passed the DOM parser using rendered template
  $dom->at('title')->replace_content('because I can');
% end

@@ further_convince_me.html.ep
<!DOCTYPE html>
<html>
  <head>
    <title>Convince me!</title>
  </head>
  <body>
    <select id="industry" value="" name="industry">
      <option id="industry" value="" name="industry">Please Select…</option>
      <option id="industry" value="Aerospace &amp; Defense" name="industry">Aerospace &amp; Defense</option>
      <option id="industry" value="Agriculture" name="industry">Agriculture</option>
      <option id="industry" value="Automotive &amp; Transport" name="industry">Automotive &amp; Transport</option>
      <option id="industry" value="Banking" name="industry">Banking</option>
      <option id="industry" value="Beverages" name="industry">Beverages</option>
      <option id="industry" value="Business Services" name="industry">Business Services</option>
      <option id="industry" value="Charitable Organizations" name="industry">Charitable Organizations</option>
      <option id="industry" value="Chemicals" name="industry">Chemicals</option>
      <option id="industry" value="Computer Hardware" name="industry">Computer Hardware</option>
      <option id="industry" value="Computer Services" name="industry">Computer Services</option>
      <option id="industry" value="Computer Software" name="industry">Computer Software</option>
      <option id="industry" value="Construction" name="industry">Construction</option>
      <option id="industry" value="Consumer Products Manufacturers" name="industry">Consumer Products Manufacturers</option>
      <option id="industry" value="Consumer Services" name="industry">Consumer Services</option>
      <option id="industry" value="Cultural Institutions" name="industry">Cultural Institutions</option>
      <option id="industry" value="Education" name="industry">Education</option>
      <option id="industry" value="Electronics" name="industry">Electronics</option>
      <option id="industry" value="Energy &amp; Utilities" name="industry">Energy &amp; Utilities</option>
      <option id="industry" value="Entertainment" name="industry">Entertainment</option>
      <option id="industry" value="Environmental Services &amp; Equipment" name="industry">Environmental Services &amp; Equipment</option>
      <option id="industry" value="Financial Services" name="industry">Financial Services</option>
      <option id="industry" value="Food" name="industry">Food</option>
      <option id="industry" value="Foundations" name="industry">Foundations</option>
      <option id="industry" value="Government" name="industry">Government</option>
      <option id="industry" value="Health Care" name="industry">Health Care</option>
      <option id="industry" value="Industrial Manufacturing" name="industry">Industrial Manufacturing</option>
      <option id="industry" value="Insurance" name="industry">Insurance</option>
      <option id="industry" value="Leisure" name="industry">Leisure</option>
      <option id="industry" value="Media" name="industry">Media</option>
      <option id="industry" value="Membership Organizations" name="industry">Membership Organizations</option>
      <option id="industry" value="Metals &amp; Mining" name="industry">Metals &amp; Mining</option>
      <option id="industry" value="Pharmaceuticals" name="industry">Pharmaceuticals</option>
      <option id="industry" value="Real Estate" name="industry">Real Estate</option>
      <option id="industry" value="Retail" name="industry">Retail</option>
      <option id="industry" value="Security Products &amp; Services" name="industry">Security Products &amp; Services</option>
      <option id="industry" value="Telecommunications Equipment" name="industry">Telecommunications Equipment</option>
      <option id="industry" value="Telecommunications Services" name="industry">Telecommunications Services</option>
      <option id="industry" value="Transportation Services" name="industry">Transportation Services</option>
      <option id="industry" value="Technology" name="industry">Technology</option>
      <option id="industry" value="Other" name="industry">Other</option>
  </select>
  </body>
</html>

% filter begin
% my ($c, $dom) = @_;
  $dom->at('#industry option[value*="'. $c->param('industry') .'"]')->attrs(selected => 'selected');
% end

Say it once, Don't Repeat It

The following is a story about me (you can call me "guy") who was working as a
conversation with one of the lead system architects (herein after referred
to as God).

One day, Guy explained to God that the webapp has many parameters hitting it,
some of which are actually the same types of parameters but with varying and
arbitrary names, and that while there's is no realistic way to stop the Angels
from creating templates with parameters named however they choose, Guy sugge…

Candy-Coated Data Validation; Codename: ShuhgaBear

2-CENTS

This week, in the intertubes, I came across a Node.js validation library which seemed to be popular amongst "those people". At-a-glance, as far as validation libraries go, I didn't think it was that well done, but I must admit to noticing a certain appeal in the expressiveness of the design which I suppose is due largely to that fact that its a JavaScript library.

Anyway, I decided that it would be cool to try and mimic the expressiveness while using Validation::Class as the foundation. It started as a feature-branch on Github but I was amazed at how quickly the concept came to fruition. The results is a module called Validation::Class::Simple::Streamer which has been added to the master-branch on Github as an optional experimental addition to the distribution.

Why? Validation::Class is design for a codebase that desires to be optimized and take advantage of compartmentalization and reusability, etc., though it requires some forethought and configuration. Validation::Class::Simple was added to provide a "container-class" to allow developers to use Validation::Class they way they would a traditional data validation library, in an ad-hoc fashion not interested in the advanced setup, etc. Validation::Class::Simple::Streamer was created because its a simple, maybe even fun, way of validating data and offers a low barrier-to-entry for people that don't care to configure the full framework.

SYNOPSIS

use Validation::Class::Simple::Streamer;

my $params = Validation::Class::Simple::Streamer->new($parameters);

unless ($params->check('email_address')->length(3)->email) {
    # validated login, password and email_address
}

unless ($params->check('home_phone')->telephone) {
    # validated login, password, email_address and home_phone
}

$params->check('password');

# be as expressive as you like
ok() if
    # validates login, password, email_address and home_phone
    $params->max_length(15) &&
    $params->min_symbols(1) &&
    $params->matches('password2')
;

# using your own workflow
$params->check($_)->filters('trim, strip') for qw(login password);

# are you of legal age?
if ($params->check('user_age')->between('18-75')) {
    # access to explicit content approved
    # validated login, password, email_address, home_phone and user_age
}

# validate like a boss
# THE END

DESCRIPTION

Validation::Class::Simple::Streamer is a simple streaming validation module that makes data validation fun. It is built around the powerful Validation::Class data validation framework via Validation::Class::Simple.

This module is/was inspired by the simplicity and expressiveness of the Node.js validator library, https://github.com/chriso/node-validator, but built on top of the ever-awesome Validation::Class framework, which is designed to be modular and extensible, i.e. whatever custom directives you create and install will become methods on this class which you can then use to enforce policies.

GITHUB LINK

http://goo.gl/2sZvu

MISC BANTER

  • [19:55] * Now talking to ironcamel
  • [20:49] anewkirk: speaking of Validation::Class, I just added a new feature called simple-streamer
  • [20:58] ironcamel: can you give me a summary of what it does
  • [20:59] anewkirk: sure, it allows you to define rules as-you-go and in boolean-context it calls validate for you automatically
  • [21:00] anewkirk: imagine a dancer plugin
  • [21:00] anewkirk: that exports a method named check()
  • [21:00] anewkirk: ... and then in your route handler you simply say
  • [21:01] anewkirk: return '/dashboard' if check('login')->required->min_length(5)
  • [21:01] anewkirk: you're not pre-declaring rules at this point
  • [21:01] anewkirk: and that code actually works
  • [21:01] ironcamel: unless ($params->check('home_phone')->telephone) {
  • [21:02] ironcamel: what is ->telephone() and who defines it?
  • [21:02] anewkirk: telephone is the directive
  • [21:02] anewkirk: as in ....
  • [21:02] ironcamel: it is a built in thing?
  • [21:03] anewkirk: field home_phone => { telephone => 1 };
  • [21:03] ironcamel: what?
  • [21:03] anewkirk: check out https://github.com/chriso/node-validator
  • [21:04] anewkirk: it node.js validation library
  • [21:05] anewkirk: I was inspired by HTML::Zoom also -- https://metacpan.org/module/HTML::Zoom
  • [21:06] anewkirk: which mst calls a streaming template engine because you're not really manipulating the template until its rendered
  • [21:06] anewkirk: basically the functions build-up instructions which all get executed once you call render
  • [21:09] anewkirk: its a play on the literal definition of streaming
  • [21:09] anewkirk: ok, so check('foobar')->required->min_symbols doesn't do any validation, those functions simply queue-up instructions
  • [21:09] anewkirk: so its kinda streaming the validation event
  • [21:09] ironcamel: maybe Validation::Class::Simple::Sugar would be better
  • [21:10] ironcamel: i don't get it
  • [21:10] anewkirk: but its not really sugar
  • [21:10] ironcamel: are you thinking it is streaming because you are chaining method calls?
  • [21:10] anewkirk: the chainable nature of the functions makes it stream-like, yes
  • [21:10] ironcamel: it kind of is sugar
  • [21:10] anewkirk: well ...
  • [21:10] anewkirk: maybe
  • [21:11] ironcamel: because ->length(3) is sugar for (length($foo) eq 3)
  • [21:11] ironcamel: *==
  • [21:11] anewkirk: yes and no
  • [21:11] ironcamel: chaining functions has nothing to do with streaming
  • [21:12] anewkirk: i know, its a play on the concept of streaming
  • [21:23] ironcamel: i would probably use Validation::Class::Simple for most of my own personal projects
  • [21:24] ironcamel: so it almost seams like the streamer thing should be Validation::Nodeish
  • [21:24] ironcamel: Validator::Node
  • [21:24] ironcamel: yes
  • [21:24] anewkirk: oh god no hahaha

Validation::Class: Released version 7.900001

I wanted to formally annouce the release of Validation::Class, as of version 7.900001 the codebase is dramatically different though it maintains backwards compatibility at about 95%. The following is the current complete list of core directives (validation rules). As stated in the documentation, the purpose of the core directives is merely to provide a reasonable layer of protection against bad/malformed data and validators are not very sophisticated. e.g. the email directive does not perform a host lookup, etc:

alias           - handles parameter aliases
between         - handles numeric range validation
city            - handles city/area validation for cities in the USA
creditcard      - handles validation for credit cards
date            - handles validation of simple date formats
decimal         - handles validation of floating point integers
default         - hold the value used if no parameter exists
depends_on      - validates the existence of dependent parameters
email           - checks the validity of email addresses
error           - holds the error message that will supersede other errors
errors          - used internally
filtering       - specifies pre-or-post filtering per field
filters         - specifies list of filters per field
hostname        - handles validatation of server hostnames
label           - holds a user-friendly string (name) representing the field
length          - validates the exact length
matches         - validates that dependent parameter values match
max_alpha       - validates the length of alphabetic characters
max_digits      - validates the length of numeric characters
max_length      - validates the length of all characters
max_sum         - validates the numeric value
max_symbols     - validates the length of non-alphanumeric characters
messages        - holds error message which will supersede class-level messages
min_alpha       - validates the length of alphabetic characters
min_digits      - validates the length of numeric characters
min_length      - validates the length of all characters
min_sum         - validates the numeric value
min_symbols     - validates the length of non-alphanumeric characters
mixin           - specifies list of mixins to merge
mixin_field     - specifies list of fields to merge
multiples       - validates whether multi-value parameters are expected
name            - used internally
options         - enumerated list of values to be validated against
pattern         - handles validation of simple patterns and complex regular expressions
readonly        - ignore parameter values
required        - validation of supply and demand
ssn             - validation of social security numbers in the USA
state           - handles state validation for states in the USA
telephone       - handles telephone number validation for the USA and North America
time            - handles validation for standard time formats
toggle          - used internally
uuid            - handles validation of globally/universally unique identifiers
validation      - execute user-defined validation routines
value           - hold the absolute value
zipcode         - handles postal-code validation for areas in the USA and North America

Check it out on MetaCPAN.

If you like it, like it, follow it, etc. If you hate it, let me know why, please.

Thanks.

If you are interested in helping me extend the library by creating your own directive classes, contact me socially, IRC (anewkirk|anaio), or checkout the directives base class.

About Al Newkirk

user-pic ... proud Perl hacker, ask me anything!