|
NAMEBrannigan::Examples - Example schemes, input and output for Brannigan.DESCRIPTIONThe following document provides examples for Brannigan usage. Brannigan is a system for validating and parsing input, targeted at web applications. It can validate and parse both simple user input and complex data structures.In this document you will find example schemes and input/output to/from these schemes. The examples in this document are directly extracted from the tests provided with the Brannigan distrubution. SIMPLE SCHEMESThe following example shows a Brannigan object with two simple schemes. The first scheme (post) is the base scheme, while the second (edit_post) inherits the base scheme while making a few simple changes.my $b = Brannigan->new( { name => 'post', ignore_missing => 1, params => { subject => { required => 1, length_between => [3, 40], }, text => { required => 1, min_length => 10, validate => sub { my $value = shift; return undef unless $value; return $value =~ m/^lorem ipsum/ ? 1 : undef; } }, day => { required => 0, integer => 1, value_between => [1, 31], }, mon => { required => 0, integer => 1, value_between => [1, 12], }, year => { required => 0, integer => 1, value_between => [1900, 2900], }, section => { required => 1, integer => 1, value_between => [1, 3], parse => sub { my $val = shift; my $ret = $val == 1 ? 'reviews' : $val == 2 ? 'receips' : 'general'; return { section => $ret }; }, }, id => { required => 1, exact_length => 10, value_between => [1000000000, 2000000000], }, }, groups => { date => { params => [qw/year mon day/], parse => sub { my ($year, $mon, $day) = @_; return undef unless $year && $mon && $day; return { date => $year.'-'.$mon.'-'.$day }; }, }, }, }, { name => 'edit_post', inherits_from => 'post', params => { subject => { required => 0, }, id => { forbidden => 1, }, edit_reason => { required => 0, }, }, }); Basically, these schemes are used to create and edit blog posts. A post has a subject between 3 to 40 characters long; a text field longer than 10 characters that must also begin with 'lorem ipsum'; day, month and year values; a section integer the blog section for this post belongs; and a 10-digit ID field. We have a 'date' group that automatically parses the day, month and year values to a YYYY-MM-DD string. The 'edit_post' scheme is used when a blog post is edited. This time, the subject is not required (maybe we just want to use the previous value), an ID shouldn't be provided, and a new paramter called 'edit_reason' can be provided (but isn't required). INPUT/OUTPUT EXAMPLE WITH REJECTS AND NO INHERITANCESuppose our app receives the following input for creating a new post:my $input_bad = { subject => 'su', text => undef, day => 13, mon => 12, year => 2010, section => 2, thing => 3, id => 300000000, }; The output from running "$b->process('post', $input_bad)" will be: { '_rejects' => { 'text' => [ 'required(1)' ], 'subject' => [ 'length_between(3, 40)' ], 'id' => [ 'exact_length(10)', 'value_between(1000000000, 2000000000)' ] }, 'date' => '2010-12-13', 'subject' => 'su', 'section' => 'receips', 'day' => 13, 'mon' => 12, 'id' => 300000000, 'year' => 2010 } We can see that three parameters failed validation, and we can see the validations they failed in the _rejects key. Even though they did fail, they are still returned in the output. Notice, also, that while the input had a 'thing' key with a value of 3, the output lacks that key, since it is not referenced anywhere in the scheme and 'ignore_missing' is on. INPUT/OUTPUT EXAMPLE WITH NO REJECTS AND NO INHERITANCEmy $input_good = { subject => 'subject', text => 'lorem ipsum dolor sit amet', section => 2, thing => 3, id => 1515151515, }; my $output = $b->process('post', $input_good) In this case, $output will be: { 'subject' => 'subject', 'text' => 'lorem ipsum dolor sit amet', 'section' => 'receips', 'id' => 1515151515 } This time all parameters passed their validations, so a _rejects key does not exist in the output. INPUT/OUTPUT EXAMPLE WITH REJECTS AND INHERITANCEWell, let's suppose now we are editing the post we've created above.my $input_edited_bad = { subject => 'subject edited', section => 3, id => 1515151515, }; my $output2 = $b->process('edit_post', $input_edited_bad); $output2 will be: { '_rejects' => { 'id' => [ 'forbidden(1)' ], 'text' => [ 'required(1)' ], }, 'subject' => 'subject edited', 'section' => 'general', 'id' => 1515151515 } INPUT/OUTPUT EXAMPLE WITH INHERITANCE AND NO REJECTSmy $input_edited_good = { id => undef, section => 1, text => 'lorem ipsum oh shit my parents are here', edit_reason => 'forgot to mention my parents are here', }; my $output3 = $b->process('edit_post', $input_edited_bad); $output3 will be: { 'section' => 'reviews', 'text' => 'lorem ipsum oh shit my parents are here', 'edit_reason' => 'forgot to mention my parents are here', } COMPLEX SCHEMEThe following schemes are examples of Brannigan's ability to validate and parse complex data structures.my $b = Brannigan->new( { name => 'complex_scheme', ignore_missing => 1, params => { name => { hash => 1, keys => { '/^(first|last)_name$/' => { required => 1, }, middle_name => { required => 0, }, }, }, '/^(birth|death)_date$/' => { hash => 1, required => 1, keys => { _all => { required => 1, integer => 1, }, day => { value_between => [1, 31], }, mon => { value_between => [1, 12], }, year => { value_between => [1900, 2100], }, }, parse => sub { my ($date, $type) = @_; # $type has either 'birth' or 'date', # $date has the hash-ref that was provided $date->{day} = '0'.$date->{day} if $date->{day} < 0; $date->{mon} = '0'.$date->{mon} if $date->{mon} < 0; return { "${type}_date" => join('-', $date->{year}, $date->{mon}, $date->{day}) }; }, }, death_date => { required => 0, }, id_num => { integer => 1, exact_length => 9, validate => sub { my $value = shift; return $value =~ m/^0/ ? 1 : undef; }, default => sub { # generate a random 9-digit number that begins with zero my @digits = (0); foreach (2 .. 9) { push(@digits, int(rand(10))); } #return join('', @digits); # commented out so we can actually write a test for this return '012345678'; }, }, phones => { hash => 1, keys => { _all => { validate => sub { my $value = shift; return $value =~ m/^\d{2,3}-\d{7}$/ ? 1 : undef; }, }, '/^(home|mobile|fax)$/' => { parse => sub { my ($value, $type) = @_; return { $type => $value }; }, }, }, }, education => { required => 1, array => 1, length_between => [1, 3], values => { hash => 1, keys => { '/^(start_year|end_year)$/' => { required => 1, value_between => [1900, 2100], }, school => { required => 1, min_length => 4, }, type => { required => 1, one_of => ['Elementary', 'High School', 'College/University'], parse => sub { my $value = shift; # returns the first character of the value in lowercase my @chars = split(//, $value); return { type => lc shift @chars }; }, }, }, }, }, employment => { required => 1, array => 1, length_between => [1, 5], values => { hash => 1, keys => { '/^(start|end)_year$/' => { required => 1, value_between => [1900, 2100], }, employer => { required => 1, max_length => 20, }, responsibilities => { array => 1, required => 1, }, }, }, }, other_info => { hash => 1, keys => { bio => { hash => 1, keys => { '/^(en|he|fr)$/' => { length_between => [100, 300], }, fr => { required => 1, }, }, }, }, }, '/^picture_(\d+)$/' => { max_length => 5, validate => sub { my ($value, $num) = @_; return $value =~ m!^http://! && $value =~ m!\.(png|jpg)$! ? 1 : undef; }, }, picture_1 => { default => 'http://www.example.com/images/default.png', }, }, groups => { generate_url => { params => [qw/id_num name/], parse => sub { my ($id_num, $name) = @_; return { url => "http://www.example.com/?id=${id_num}&$name->{last_name}" }; }, }, pictures => { regex => '/^picture_(\d+)$/', parse => sub { return { pictures => \@_ }; }, }, }, }, { name => 'complex_inherit', inherits_from => 'complex_scheme', params => { education => { values => { keys => { honors => { array => 1, max_length => 5, values => { length_between => [5, 15], }, }, }, }, }, picture_1 => { required => 1, }, other_info => { keys => { social => { array => 1, values => { hash => 1, keys => { website => { required => 1, }, user_id => { required => 1, }, }, }, }, }, }, }, }, { name => 'complex_inherit_2', inherits_from => 'complex_inherit', params => { some_other_thing => { validate => sub { my $value = shift; return $value =~ m/I'm a little teapot/ ? 1 : undef; }, parse => sub { my $value = shift; $value =~ s/I'm a little teapot/I like to wear women's clothing/; return { some_other_thing => $value }; }, }, }, groups => { dates => { params => [qw/birth_date death_date/], parse => sub { my ($b, $d) = @_; return { dates => 'This guy was born '.$b->{year}.'-'.$b->{mon}.'-'.$b->{day}.', unfortunately, he died '.$d->{year}.'-'.$d->{mon}.'-'.$d->{day} }; }, }, }, }, { name => 'complex_inherit_3', inherits_from => ['complex_inherit', 'complex_inherit_2'], params => { employment => { required => 0, }, }, } ); This is a fairly complex example. Our base scheme ('complex_scheme') defines a resume-like structure (well, expect maybe for the 'death_date' parameter). The other schemes are just meant as an example of inheritance. Now, suppose we have the following input: my $input = { name => { first_name => 'Some', last_name => 'One', }, birth_date => { day => 32, mon => -5, year => 1984, }, death_date => { day => 12, mon => 12, year => 2112, }, phones => { home => '123-1234567', mobile => 'what?', }, education => [ { school => 'First Elementary School of Somewhere', start_year => 1990, end_year => 1996, type => 'Elementary' }, { school => 'Sch', start_year => 1996, end_year => 3000, type => 'Fake' }, ], other_info => { bio => { en => "Born, lives, will die.", he => "Nolad, Chai, Yamut." }, }, picture_1 => '', picture_2 => 'http://www.example.com/images/mypic.jpg', picture_3 => 'http://www.example.com/images/mypic.png', picture_4 => 'http://www.example.com/images/mypic.gif', }; my $output = $b->process('complex_scheme', $input); Our $output will be: { 'education' => [ { 'start_year' => 1990, 'type' => 'e', 'school' => 'First Elementary School of Somewhere', 'end_year' => 1996 }, { 'start_year' => 1996, 'type' => 'f', 'school' => 'Sch', 'end_year' => 3000 } ], 'death_date' => { 'mon' => 12, 'day' => 12, 'year' => 2112 }, 'name' => { 'last_name' => 'One', 'first_name' => 'Some' }, 'id_num' => '012345678', 'picture_2' => 'http://www.example.com/images/mypic.jpg', 'phones' => { 'mobile' => 'what?', 'home' => '123-1234567' }, 'birth_date' => { 'mon' => -5, 'day' => 32, 'year' => 1984 }, 'other_info' => { 'bio' => { 'en' => 'Born, lives, will die.', 'he' => 'Nolad, Chai, Yamut.' } }, '_rejects' => { 'education' => { '1' => { 'type' => [ 'one_of(Elementary, High School, College/University)' ], 'school' => [ 'min_length(4)' ], 'end_year' => [ 'value_between(1900, 2100)' ] } }, 'death_date' => { 'year' => [ 'value_between(1900, 2100)' ] }, 'picture_2' => [ 'max_length(5)' ], 'birth_date' => { 'mon' => [ 'integer(1)', 'value_between(1, 12)' ], 'day' => [ 'value_between(1, 31)' ] }, 'phones' => { 'mobile' => [ 'validate' ] }, 'employment' => [ 'required(1)' ], 'other_info' => { 'bio' => { 'en' => [ 'length_between(100, 300)' ], 'fr' => [ 'required(1)' ], 'he' => [ 'length_between(100, 300)' ] } }, 'picture_3' => [ 'max_length(5)' ], 'picture_4' => [ 'max_length(5)', 'validate' ] }, 'picture_3' => 'http://www.example.com/images/mypic.png', 'picture_4' => 'http://www.example.com/images/mypic.gif', 'url' => 'http://www.example.com/?id=012345678&One', 'pictures' => [ 'http://www.example.com/images/mypic.jpg', 'http://www.example.com/images/mypic.png', 'http://www.example.com/images/mypic.gif', 'http://www.example.com/images/default.png' ], 'picture_1' => 'http://www.example.com/images/default.png' } Let's now modify the input like so: $input->{education}->[0]->{honors} = ['Valedictorian', "Teacher's Pet", "The Dean's Suckup"]; $input->{education}->[1]->{honors} = 'Woooooeeeee!'; $input->{other_info}->{social} = [{ website => 'facebook', user_id => 123412341234 }, { website => 'noogie.com', user_id => 'snoogens' }]; Processing this input with the 'complex_inherit' scheme yields: { 'education' => [ { 'start_year' => 1990, 'honors' => [ 'Valedictorian', 'Teacher\'s Pet', 'The Dean\'s Suckup' ], 'type' => 'e', 'school' => 'First Elementary School of Somewhere', 'end_year' => 1996 }, { 'start_year' => 1996, 'honors' => 'Woooooeeeee!', 'type' => 'f', 'school' => 'Sch', 'end_year' => 3000 } ], 'name' => { 'last_name' => 'One', 'first_name' => 'Some' }, 'death_date' => { 'mon' => 12, 'day' => 12, 'year' => 2112 }, 'id_num' => '012345678', 'picture_2' => 'http://www.example.com/images/mypic.jpg', 'birth_date' => { 'mon' => -5, 'day' => 32, 'year' => 1984 }, 'phones' => { 'mobile' => 'what?', 'home' => '123-1234567' }, 'other_info' => { 'social' => [ { 'website' => 'facebook', 'user_id' => '123412341234' }, { 'website' => 'noogie.com', 'user_id' => 'snoogens' } ], 'bio' => { 'en' => 'Born, lives, will die.', 'he' => 'Nolad, Chai, Yamut.' } }, '_rejects' => { 'education' => { '1' => { 'honors' => { '_self' => [ 'array(1)' ] }, 'type' => [ 'one_of(Elementary, High School, College/University)' ], 'school' => [ 'min_length(4)' ], 'end_year' => [ 'value_between(1900, 2100)' ] }, '0' => { 'honors' => { '2' => [ 'length_between(5, 15)' ] } } }, 'death_date' => { 'year' => [ 'value_between(1900, 2100)' ] }, 'picture_2' => [ 'max_length(5)' ], 'phones' => { 'mobile' => [ 'validate' ] }, 'birth_date' => { 'mon' => [ 'integer(1)', 'value_between(1, 12)' ], 'day' => [ 'value_between(1, 31)' ] }, 'employment' => [ 'required(1)' ], 'other_info' => { 'bio' => { 'en' => [ 'length_between(100, 300)' ], 'fr' => [ 'required(1)' ], 'he' => [ 'length_between(100, 300)' ] } }, 'picture_3' => [ 'max_length(5)' ], 'picture_4' => [ 'max_length(5)', 'validate' ], 'picture_1' => [ 'required(1)' ] }, 'picture_3' => 'http://www.example.com/images/mypic.png', 'picture_4' => 'http://www.example.com/images/mypic.gif', 'url' => 'http://www.example.com/?id=012345678&One', 'pictures' => [ 'http://www.example.com/images/mypic.jpg', 'http://www.example.com/images/mypic.png', 'http://www.example.com/images/mypic.gif', 'http://www.example.com/images/default.png' ], 'picture_1' => 'http://www.example.com/images/default.png' } Now let's take the original input (before we changed it), and modify it like so: $input->{some_other_thing} = "I'd like to tell the whole world that I'm a little teapot."; Processing with 'complex_inherit_2' results in the following output: { 'education' => [ { 'start_year' => 1990, 'honors' => [ 'Valedictorian', 'Teacher\'s Pet', 'The Dean\'s Suckup' ], 'type' => 'e', 'school' => 'First Elementary School of Somewhere', 'end_year' => 1996 }, { 'start_year' => 1996, 'honors' => 'Woooooeeeee!', 'type' => 'f', 'school' => 'Sch', 'end_year' => 3000 } ], 'dates' => 'This guy was born 1984--5-32, unfortunately, he died 2112-12-12', 'name' => { 'last_name' => 'One', 'first_name' => 'Some' }, 'death_date' => { 'mon' => 12, 'day' => 12, 'year' => 2112 }, 'id_num' => '012345678', 'picture_2' => 'http://www.example.com/images/mypic.jpg', 'birth_date' => { 'mon' => -5, 'day' => 32, 'year' => 1984 }, 'phones' => { 'mobile' => 'what?', 'home' => '123-1234567' }, 'other_info' => { 'social' => [ { 'website' => 'facebook', 'user_id' => '123412341234' }, { 'website' => 'noogie.com', 'user_id' => 'snoogens' } ], 'bio' => { 'en' => 'Born, lives, will die.', 'he' => 'Nolad, Chai, Yamut.' } }, '_rejects' => { 'education' => { '1' => { 'honors' => { '_self' => [ 'array(1)' ] }, 'type' => [ 'one_of(Elementary, High School, College/University)' ], 'school' => [ 'min_length(4)' ], 'end_year' => [ 'value_between(1900, 2100)' ] }, '0' => { 'honors' => { '2' => [ 'length_between(5, 15)' ] } } }, 'death_date' => { 'year' => [ 'value_between(1900, 2100)' ] }, 'picture_2' => [ 'max_length(5)' ], 'phones' => { 'mobile' => [ 'validate' ] }, 'birth_date' => { 'mon' => [ 'integer(1)', 'value_between(1, 12)' ], 'day' => [ 'value_between(1, 31)' ] }, 'employment' => [ 'required(1)' ], 'other_info' => { 'bio' => { 'en' => [ 'length_between(100, 300)' ], 'fr' => [ 'required(1)' ], 'he' => [ 'length_between(100, 300)' ] } }, 'picture_3' => [ 'max_length(5)' ], 'picture_4' => [ 'max_length(5)', 'validate' ], 'picture_1' => [ 'required(1)' ] }, 'picture_3' => 'http://www.example.com/images/mypic.png', 'picture_4' => 'http://www.example.com/images/mypic.gif', 'url' => 'http://www.example.com/?id=012345678&One', 'some_other_thing' => 'I\'d like to tell the whole world that I like to wear women\'s clothing.', 'pictures' => [ 'http://www.example.com/images/mypic.jpg', 'http://www.example.com/images/mypic.png', 'http://www.example.com/images/mypic.gif', 'http://www.example.com/images/default.png' ], 'picture_1' => 'http://www.example.com/images/default.png' }
Visit the GSP FreeBSD Man Page Interface. |