Data::ManagerJust because your users may suck  doesn’t mean you should, too
Jay Shirley• Infinity Interactive   http://www.infinityinteractive.com• Cold Hard Code        http://www.coldhardcode.com/• ...
A Brief Introduction
A rant, perhaps.
Definitely a diatribe.
Tools
Make us better.
Do one thing really well.
Used together.
Synergy!
Example: Catalyst
Catalyst grows Tools    HTTP::Body, others
Forms
WTF is a Form?
Modern Perl
Modern Applications
Mobile. JavaScript. Apps.
Forms are dead.
Data Validation      =Form Generator
Data Validation      ≠Form Generator
2011
1999 called... they don’t want their  CGI.pm    back
Tools should grow with you.
Don’t marry tools.
Easy to get away from.
Single Purpose!
Imagine in 5 years…
Why is old embarrassing?
How to pick a tool.
Don’t judge a tool by typing.
Opinions are ok.
Leverages other tools.
Doesn’t impose.
Introducing (finally)
Data::ManagerA very good way of managing incoming data.
In case you missed it…
Bad:              Error Message!              Error Message!  Line Item 1             Line Item 1Delivery Options         ...
Good:Error Message!  Line Item 1       Line Item 1Delivery Options    Gift Options                   Error Message!  Line ...
GETTING STARTEDmy $dv = Data::Verifier->new(…);my $results = $dv->verify(%params);if($results->success) {    # o/} else { ...
Data::Managermy $dm = Data::Manager->new;my $verifier = Data::Verifer->new(...);$dm->set_verifier(address, $verifier);$dm-...
Data::Manager Scopesmy $dm = Data::Manager->new;my $verifier = Data::Verifer->new(...);$dm->set_verifier(address, $verifie...
Data::Manager and Messages$dm->verify(address, $data);my $messages = $dm->messages_for_scope(address);
Data::Manager and Results$dm->verify(address, $data);my $results = $dm->get_results(address);# $results can serialize# $re...
A Year in Review• Data::Verifier  • 15+ releases and now version 0.47• Data::Manager  • 2 releases, could use more. 0.07• M...
Where we are now.• Elegant handling of multiple scopes.• We know how to use this stuff better.• The code hasn’t changed tha...
From A to Z
In the beginning…
Markup by hand
Inconsistent
Subtle or just broken
Became complex
Hard to change
Married to bad/old markup
Solution?
Not form generators!
Macros!
Form Template:<fieldset> <legend>[% c.loc(About You) %]</legend> [% context.scope = person; text_field({ label => Your Nam...
Results in
“Person” Scope
“Identity” Scope
And what we want
We get.
Scopes<fieldset> <legend>[% c.loc(About You) %]</legend> [% context.scope = person; text_field({ label => Your Name,      ...
Just markup.• Easily define the layout form.• Macros make it sensible.• Markup for forms can be provided by designers and e...
We have the biggest Macros       of them all.• Too big to put on a slide, so they’re on github.• Well defined.• Easy to use...
The gist of the macro• Too big to put on a slide (288L).• Ask me to see them, happy to share.• Looks at context.scope, det...
Priorities:• Good Ideal user experience.
Priorities:• Good Ideal user experience.• Maximize reliability.
Priorities:• Good Ideal user experience.• Maximize reliability.• Optimize for use, hide the complexity.
CRUD ≠ User Experience• CRUD principles are awesome.
CRUD ≠ User Experience• CRUD principles are awesome.• Behind the scene.
CRUD ≠ User Experience• CRUD principles are awesome.• Behind the scene.• Many forms encompass multiple objects.
CRUD ≠ User Experience• CRUD principles are awesome.• Behind the scene.• Many forms encompass multiple objects.• Making a ...
Build a UI first.
Pick method and tools      second.
Our tools let us pick methods
Priorities:• Ideal user experience.• Maximize reliability.• Optimize for use, hide the complexity.
Reliability• Don’t think about errors.• Convention makes them happen.• Users know what to expect.• Plans + Simple API = Re...
Having a plan means you   never have to say sorry.Not really, but it should stop you from having to                  apolo...
Priorities:• Ideal user experience.• Maximize reliability.• Optimize for use, hide the complexity.
Optimize for use.Save yourself agony with a testable, nice and clean                       API
Optimize for use• Write code behind a clean API.• Make methods that do smaller work.• That gives us easy code.• This is a ...
Optimized for use:# In your controller: (see Catalyst::Action::REST for *_POST)sub root_POST {    my ( $self, $c ) = @_;  ...
Simple Modelpackage MyApp::Model::DataManager;use Moose;with Catalyst::Component::InstancePerContext;has profiles => ( ......
Profileshas profiles => (    default => sub { {       login => {            filters => [   qw(trim) ],            profile =...
Going Further(with type constraints)   username => {       type        => MyEmailType,       required    => 1,       coerc...
Type Checking• Handles coercion (or not)• Verifies if data is of type• Emits “invalid” error in results if invalid.
extend Data::Manager• Add your own methods.• Make it better.• Some ideas:  • all_valids, hashref of all valid fields.  • da...
extend Data::Manager• Setup defaults for verifiers by scope.• By DBIC result sources.• By Catalyst models.• Lots of options.
Example: Checking   uniqueness An often broken endeavor.
Issues:• Must filter out ‘$self ’ from the result set.• Must then load $self.• Must figure out create vs. update.
A simple unique checkmy $unique_check = sub {    my $r = shift;    my $user = $r->get_value(username);    my $id   = $r->g...
A Problem
Difficulty in testing.• Verifier doesn’t know the database.• Verifier shouldn’t know the database.• Modify verification profiles...
Insertingsub build_per_context_instance {    my ( $self, $c ) = @_;    my $unique_check = sub { ... };    my $dm = Data::M...
On a user object…sub build_per_context_instance {    my ( $self, $c ) = @_;    my $unique_check = $c->model(‘DB::User’)->u...
Not ideal• Can add same check for backend tools• Access in similar fashion• Verification profiles are modifiable
Still optimized for use:# Same controller!sub create_account_POST {    my ( $self, $c ) = @_;    my $data    = ‘create_acc...
Changing validation doesn’t      change code.
Changing validation shouldn’t       change code.
.* just works.(you just have to use it)
Thank you.Please think about your applications.     Think about the problems.          Fix the problems.
Building Better Applications with Data::Manager
Building Better Applications with Data::Manager
Building Better Applications with Data::Manager
Building Better Applications with Data::Manager
Building Better Applications with Data::Manager
Building Better Applications with Data::Manager
Building Better Applications with Data::Manager
Upcoming SlideShare
Loading in …5
×

Building Better Applications with Data::Manager

1,745 views

Published on

Published in: Technology
  • Be the first to comment

Building Better Applications with Data::Manager

  1. 1. Data::ManagerJust because your users may suck doesn’t mean you should, too
  2. 2. Jay Shirley• Infinity Interactive http://www.infinityinteractive.com• Cold Hard Code http://www.coldhardcode.com/• j@shirley.im http://j.shirley.im/
  3. 3. A Brief Introduction
  4. 4. A rant, perhaps.
  5. 5. Definitely a diatribe.
  6. 6. Tools
  7. 7. Make us better.
  8. 8. Do one thing really well.
  9. 9. Used together.
  10. 10. Synergy!
  11. 11. Example: Catalyst
  12. 12. Catalyst grows Tools HTTP::Body, others
  13. 13. Forms
  14. 14. WTF is a Form?
  15. 15. Modern Perl
  16. 16. Modern Applications
  17. 17. Mobile. JavaScript. Apps.
  18. 18. Forms are dead.
  19. 19. Data Validation =Form Generator
  20. 20. Data Validation ≠Form Generator
  21. 21. 2011
  22. 22. 1999 called... they don’t want their CGI.pm back
  23. 23. Tools should grow with you.
  24. 24. Don’t marry tools.
  25. 25. Easy to get away from.
  26. 26. Single Purpose!
  27. 27. Imagine in 5 years…
  28. 28. Why is old embarrassing?
  29. 29. How to pick a tool.
  30. 30. Don’t judge a tool by typing.
  31. 31. Opinions are ok.
  32. 32. Leverages other tools.
  33. 33. Doesn’t impose.
  34. 34. Introducing (finally)
  35. 35. Data::ManagerA very good way of managing incoming data.
  36. 36. In case you missed it…
  37. 37. Bad: Error Message! Error Message! Line Item 1 Line Item 1Delivery Options Gift Options Line Item 2 Line Item 2Delivery Options Gift Options
  38. 38. Good:Error Message! Line Item 1 Line Item 1Delivery Options Gift Options Error Message! Line Item 2 Line Item 2Delivery Options Gift Options
  39. 39. GETTING STARTEDmy $dv = Data::Verifier->new(…);my $results = $dv->verify(%params);if($results->success) { # o/} else { # :(}
  40. 40. Data::Managermy $dm = Data::Manager->new;my $verifier = Data::Verifer->new(...);$dm->set_verifier(address, $verifier);$dm->verify(address, $data);if ( $dm->success ) { # o/} else { # :(}
  41. 41. Data::Manager Scopesmy $dm = Data::Manager->new;my $verifier = Data::Verifer->new(...);$dm->set_verifier(address, $verifier);$dm->verify(address, $data);if ( $dm->success ) { # o/} else { # :(}
  42. 42. Data::Manager and Messages$dm->verify(address, $data);my $messages = $dm->messages_for_scope(address);
  43. 43. Data::Manager and Results$dm->verify(address, $data);my $results = $dm->get_results(address);# $results can serialize# $results is Data::Verifier::Results
  44. 44. A Year in Review• Data::Verifier • 15+ releases and now version 0.47• Data::Manager • 2 releases, could use more. 0.07• Message::Stack • Stable API, simple workhorse. 0.19.
  45. 45. Where we are now.• Elegant handling of multiple scopes.• We know how to use this stuff better.• The code hasn’t changed that much.• API is stable.• Supports more things as needed. Fork it!
  46. 46. From A to Z
  47. 47. In the beginning…
  48. 48. Markup by hand
  49. 49. Inconsistent
  50. 50. Subtle or just broken
  51. 51. Became complex
  52. 52. Hard to change
  53. 53. Married to bad/old markup
  54. 54. Solution?
  55. 55. Not form generators!
  56. 56. Macros!
  57. 57. Form Template:<fieldset> <legend>[% c.loc(About You) %]</legend> [% context.scope = person; text_field({ label => Your Name, name => name, hint => Your Name, required => 1 }); text_field({ label => Your Email, name => email, hint => you@company.com, type=email, required => 1, more => This will be your username. }); context.scope = identity; text_field({ label => Password, type => password, name => password, required => 1 }); text_field({ label => Confirm Password, type => password, name => confirm_password, required => 1 });%]</fieldset>
  58. 58. Results in
  59. 59. “Person” Scope
  60. 60. “Identity” Scope
  61. 61. And what we want
  62. 62. We get.
  63. 63. Scopes<fieldset> <legend>[% c.loc(About You) %]</legend> [% context.scope = person; text_field({ label => Your Name, name => name, hint => Your Name, required => 1 }); text_field({ label => Your Email, name => email, hint => you@company.com, type=>email, required => 1, more => This will be your username. }); context.scope = identity; text_field({ label => Password, type => password, name => password, required => 1 }); text_field({ label => Confirm Password, type => password, name => confirm_password, required => 1 });%]</fieldset>
  64. 64. Just markup.• Easily define the layout form.• Macros make it sensible.• Markup for forms can be provided by designers and easily adapted.• Multiple macro “styles” can be included.
  65. 65. We have the biggest Macros of them all.• Too big to put on a slide, so they’re on github.• Well defined.• Easy to use.• Not that hard to update.
  66. 66. The gist of the macro• Too big to put on a slide (288L).• Ask me to see them, happy to share.• Looks at context.scope, determines: • Message::Stack scope. • Data::Verifier::Results
  67. 67. Priorities:• Good Ideal user experience.
  68. 68. Priorities:• Good Ideal user experience.• Maximize reliability.
  69. 69. Priorities:• Good Ideal user experience.• Maximize reliability.• Optimize for use, hide the complexity.
  70. 70. CRUD ≠ User Experience• CRUD principles are awesome.
  71. 71. CRUD ≠ User Experience• CRUD principles are awesome.• Behind the scene.
  72. 72. CRUD ≠ User Experience• CRUD principles are awesome.• Behind the scene.• Many forms encompass multiple objects.
  73. 73. CRUD ≠ User Experience• CRUD principles are awesome.• Behind the scene.• Many forms encompass multiple objects.• Making a user view 3 pages to update 3 objects is bad form.
  74. 74. Build a UI first.
  75. 75. Pick method and tools second.
  76. 76. Our tools let us pick methods
  77. 77. Priorities:• Ideal user experience.• Maximize reliability.• Optimize for use, hide the complexity.
  78. 78. Reliability• Don’t think about errors.• Convention makes them happen.• Users know what to expect.• Plans + Simple API = Reliability
  79. 79. Having a plan means you never have to say sorry.Not really, but it should stop you from having to apologize twice.
  80. 80. Priorities:• Ideal user experience.• Maximize reliability.• Optimize for use, hide the complexity.
  81. 81. Optimize for use.Save yourself agony with a testable, nice and clean API
  82. 82. Optimize for use• Write code behind a clean API.• Make methods that do smaller work.• That gives us easy code.• This is a different topic.
  83. 83. Optimized for use:# In your controller: (see Catalyst::Action::REST for *_POST)sub root_POST { my ( $self, $c ) = @_; my $data = $c->req->params; my $results = $c->model(‘DataManager’)->verify($scope,$data); unless ( $results->success ) { $c->message({ type => ‘error’, message => ‘You Fail‘ }); $c->res->redirect( $c->uri_for_action(/object/create_form) ); $c->detach; } # Success, store from $results, verify and redirect.}
  84. 84. Simple Modelpackage MyApp::Model::DataManager;use Moose;with Catalyst::Component::InstancePerContext;has profiles => ( ... );sub build_per_context_instance { my ( $self, $c ) = @_; my $dm = Data::Manager->new; foreach my $scope ( $self->scopes ) { $dm->set_verifier( $scope => Data::Verifier->new( $self->get_profile($scope) ) ); } $dm;}
  85. 85. Profileshas profiles => ( default => sub { { login => { filters => [ qw(trim) ], profile => { username => { type => Str, required => 1, }, password => { type => Str, required => 1, } } },
  86. 86. Going Further(with type constraints) username => { type => MyEmailType, required => 1, coerce => 1, },
  87. 87. Type Checking• Handles coercion (or not)• Verifies if data is of type• Emits “invalid” error in results if invalid.
  88. 88. extend Data::Manager• Add your own methods.• Make it better.• Some ideas: • all_valids, hashref of all valid fields. • data_for_scope, hashref of fields in scope. • error_scopes, list of scopes needing attention.
  89. 89. extend Data::Manager• Setup defaults for verifiers by scope.• By DBIC result sources.• By Catalyst models.• Lots of options.
  90. 90. Example: Checking uniqueness An often broken endeavor.
  91. 91. Issues:• Must filter out ‘$self ’ from the result set.• Must then load $self.• Must figure out create vs. update.
  92. 92. A simple unique checkmy $unique_check = sub { my $r = shift; my $user = $r->get_value(username); my $id = $r->get_value(id); my $rec = $c->model(DB::User) ->search({ username => $user })->first; return 1 if not defined $rec; return 1 if $rec->id == $id; return 0;};
  93. 93. A Problem
  94. 94. Difficulty in testing.• Verifier doesn’t know the database.• Verifier shouldn’t know the database.• Modify verification profiles where it makes sense.
  95. 95. Insertingsub build_per_context_instance { my ( $self, $c ) = @_; my $unique_check = sub { ... }; my $dm = Data::Manager->new; foreach my $scope ( $self->scopes ) { $dm->set_verifier( $scope => Data::Verifier->new( $self->get_profile($scope) ) ); } $dm->get_verifier(create_account) ->profile->{username}->{post_check} = $unique_check; $dm;}
  96. 96. On a user object…sub build_per_context_instance { my ( $self, $c ) = @_; my $unique_check = $c->model(‘DB::User’)->unique_check; my $dm = Data::Manager->new; foreach my $scope ( $self->scopes ) { $dm->set_verifier( $scope => Data::Verifier->new( $self->get_profile($scope) ) ); } $dm->get_verifier(create_account) ->profile->{username}->{post_check} = $unique_check; $dm;}
  97. 97. Not ideal• Can add same check for backend tools• Access in similar fashion• Verification profiles are modifiable
  98. 98. Still optimized for use:# Same controller!sub create_account_POST { my ( $self, $c ) = @_; my $data = ‘create_account’; my $data = $c->req->params; my $results = $c->model(‘DataManager’)->verify($scope,$data); unless ( $results->success ) { $c->message({ type => ‘error’, message => ‘You Fail‘ }); $c->res->redirect( $c->uri_for_action(/create_account) ); $c->detach; } # Success, store from $results, verify and redirect.}
  99. 99. Changing validation doesn’t change code.
  100. 100. Changing validation shouldn’t change code.
  101. 101. .* just works.(you just have to use it)
  102. 102. Thank you.Please think about your applications. Think about the problems. Fix the problems.

×