Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Validating Data Everywhere with Rx

Validating Data Everywhere with Rx

Data validation is a real pain, but it's a huge help in preventing bugs. By using one system for validating data everywhere, the burden of validation is greatly simplified.

Rx is a schema language that can be used to validate data structures anywhere in your Perl programs. It integrates with Moose types, is extremely extensible, and is portable across languages: Rx implementations also exist in Python, Ruby, PHP, and JavaScript.

Ricardo Signes

June 24, 2009
Tweet

More Decks by Ricardo Signes

Other Decks in Programming

Transcript

  1. Rx

  2. { “cur_page”: 1, “per_page”: 10, “total”: 902, “items”: [ {

    “id”: 1234, “hdr_subject”: “rock hard math tests”, “discarded_by”: “dnsbl/rbl.wolfram.com” }, ... ], }
  3. --- cur_page: 1 per_page: 10 total : 902 items :

    - id: 1234 hdr_subject : rock hard math tests discarded_by: dnsbl/rbl.wolfram.com ...
  4. { “cur_page”: 1, “per_page”: 10, “total”: 902, “items”: [ {

    “id”: 1234, “hdr_subject”: “rock hard math tests”, “discarded_by”: “dnsbl/rbl.wolfram.com” }, ... ], }
  5. XML

  6. --- cur_page: 1 per_page: 10 total : 902 items :

    - id: 1234 hdr_subject : rock hard math tests discarded_by: dnsbl/rbl.wolfram.com ...
  7. { “cur_page”: 1, “per_page”: 10, “total”: 902, “items”: [ {

    “id”: 1234, “hdr_subject”: “rock hard math tests”, “discarded_by”: “dnsbl/rbl.wolfram.com” }, ... ], }
  8. <spam:page spam:curPageNum=”1” spam:perPage=”10” spam:totalItems=”902”><spam:item spam:id=”1234”><spam:headers> <spam:header spam:header=”subject”> rock hard math

    tests</spam:header> </spam:headers><spam:filterInfo> <spam:discardedBy><spam:filterType> dnsbl</spam:filterType> <spam:filterArgs>rbl.wolfram.com </spam:filterArgs></spam:discardedBy> </spam:filterInfo></spam:item> <elipsis /></spam:page>
  9. XSD

  10. total : 902 cur_page: 1 per_page: 10 items : -

    id: 1234 hdr_subject : rock hard math tests discarded_by: dnsbl/rbl.wolfram.com
  11. total : 902 cur_page: 1 per_page: 10 items : -

    id: 1234 hdr_subject : rock hard math tests discarded_by: dnsbl/rbl.wolfram.com type: //rec required: total : //int per_page: //int cur_page: { type: //int, range: { min: 1 } } items : { type: //rec required: { ... }
  12. type: //rec required: total : //int per_page: //int cur_page: {

    type: //int, range: { min: 1 } } items : { type: //rec required: { ... }
  13. my $rx = Data::Rx->new; my $data = { type =>

    ‘//rec’, ... }; my $schema = $rx->make_schema($data); Perl
  14. my $rx = Data::Rx->new; my $data = { type =>

    ‘//rec’, ... }; my $schema = $rx->make_schema($data); Perl
  15. my $rx = Data::Rx->new; my $data = { type =>

    ‘//rec’, ... }; my $schema = $rx->make_schema($data); if ($schema->check( $input )) { Perl
  16. my $rx = Data::Rx->new; my $data = { type =>

    ‘//rec’, ... }; my $schema = $rx->make_schema($data); if ($schema->check( $input )) { ... Perl
  17. my $rx = Data::Rx->new; my $data = { type =>

    ‘//rec’, ... }; my $schema = $rx->make_schema($data); if ($schema->check( $input )) { ... } Perl
  18. my $rx = Data::Rx->new; my $data = decode_json( $json );

    my $schema = $rx->make_schema($data); if ($schema->check( $input )) { ... } Perl
  19. var rx = new Rx(); var schema = rx.makeSchema( schemaFromJSON

    ); if (schema.check(input) { ... } JavaScript
  20. $rx = new Rx(); $schema = $rx.makeSchema( $schemaFromJSON ) if

    ($schema.check( $input )) { ... } PHP (forgive me)
  21. --- type : //any of : - //int - //str

    - { type: //num, value: 3.141 } “String type, I choose you!”
  22. the //arr type --- type: //arr length: { min: 6,

    max: 6 } contents: type : //int range: { min: 3, max: 18 } [ 7, 3, 4, 11, 17, 9 ]
  23. the //seq type --- type: //seq contents: - //int -

    //bool - //str [ 10, true, “awesome” ]
  24. the //rec type --- type : //rec required: first: //str

    last : //str optional: { middle: //str } { first => ‘Rico’, last => ‘Signes’ }
  25. the //all type --- type : //all of: - {

    type: //int, range: { min: 3 } } - { type: //num, range: { max: 18 } } 10
  26. use Test::More tests => 2; my $rx = Data::Rx->new({ prefix

    => { moose => ‘tag:rjbs.manxome.org,2008-10-04:rx/moose/’, }, type_plugins => [ ‘Data::Rx::Type::MooseTC’ ] }); my $array_of_int = $rx->make_schema({ type => ‘/moose/tc’, moose_type => ‘ArrayRef[Int]’, }); ok($array_of_int->check([1]), “[1] is an ArrayRef[Int]”); ok(! $array_of_int->check( 1 ), “1 is not an ArrayRef[Int]”); t/moose-types.t
  27. •...but then you’d have to rewrite your tests in every

    language. •We already have Rx running on a bunch of platforms... •...with a shared test suite... •...and we can re-use it.
  28. { “empty” : “[]”, “str-1” : “[\”string\”, 1 ]”, “0”

    : “[ 0 ]”, “0-1” : “[ 0, 1 ]”, “0-1-1” : “[ 0, 1, 1 ]”, “0-s1-1” : “[ 0, \”1\”, 1 ]”, “0-1-1-2”: “[ 0, 1, 1, 2 ]”, “0-1-1-2”: “[ 0, 1, 1, 2 ]”, “0-str” : “[ 0, \”string\” ]”, “0-str-3”: “[ 0, \”string\”, 3 ]”, “0-str-3-18” : “[ 0, \”string\”, 3, 18 ]”, “0-str-3-T” : “[ 0, \”string\”, 3, true ]”, “0-str-3-T-F” : “[ 0, \”string\”, 3, true, false ]”, “0-str-3-T-str”: “[ 0, \”string\”, 3, true, \”false\” ]”, “0-str-3-T-F-T”: “[ 0, \”string\”, 3, true, false, true ]” } spec/data/arr.json
  29. { “schema”: { “type” : “//arr”, “contents”: { “type”: “//int”

    }, “length” : { “max”: 3 } }, “pass”: { “arr”: [ “empty”, “0”, “0-1”, “0-1-1” ] }, “fail”: { “arr” : [ “str-1”, “0-s1-1”, “0-1-1-2”, “0-str” ], “bool”: “*”, “null”: “*”, “num” : “*”, “obj” : “*”, “str” : “*” } } spec/schemata/array-3-int.json
  30. { “schema”: { “type” : “tag:rx-foundation.biz/mtfnpy”, “hardness”: 8, “ronpaul” :

    false }, “pass”: { “mtf”: { “garbled”: true }, “arr”: [ “empty”, “0”, “0-1”, “0-1-1” ] }, “fail”: { “bool”: “*”, “null”: “*”, “num” : “*”, “obj” : “*”, “str” : “*” } } your-schema.json
  31. Rx validates data (not strings) •Rx isn’t about JSON •...or

    YAML •it validates data in memory •how you transmit it over the wire is your problem
  32. Core Types are Portable •you won’t see regex in Rx

    core •because regex don’t work the same everywhere
  33. Core Types are Portable •you won’t see regex in Rx

    core •because regex don’t work the same everywhere •(but there is a PCRE type outside of core!)
  34. Too Portable for Perl! •//int means an integer •but in

    many/most languages, 5 !== “5” •perl actually can’t distinguish these
  35. Too Portable for Perl! •//int means an integer •but in

    many/most languages, 5 !== “5” •perl actually can’t distinguish these •...that’s why the test suite has “todo”