Title: Data::Domain
1DataDomain
- a data validation tool
- laurent.dami_at_justice.ge.ch
2What is a "data domain" ?
- term from data management
- a set of values
- may be infinite
- defined by extension (enumeration) or by
intension (set of rules)
3How to work with domains ?
- To put the definition into practice, we need to
be able to - Define a domain
- atomic building blocks (mostly scalar)
- composition operators
- Check if a value belongs to a domain
- if not explain WHY
- answers should be consistent over time
- validating a withdrawal from an account is
not a domain operation
4Why data domains ?
- when some data crosses "boundaries"
- user form
- database
- parse tree
- config. file
- function call
- principle of defensive programming
5CPAN many modules
- Parameter checking
- ParamsCheck, ParamsValidate
- Data Modelling Object-Relational Maps
- JiftyDBI, Alzabo, RoseDBObject,
DBIxClass - HTML Form tools
- CGIFormBuilder, DataFormValidator
- Business rules
- Brick, DeclareConstraintsSimple,
DataConstraint - Terminology "domain" often called "template" or
"profile"
6Other technologies
- database validation mechanisms
- reference table
- rules constraints
- triggers
- typing (strong / dynamic)
- XML schema
- Javascript frameworks
- Parsers
- ...
7Some design dimensions
- shape of data
- scalar (string, num, date, ...)
- array or hash
- multi-level tree
- objects
- shape of messages
- single scalar
- collection
- multi-level tree
- Conciseness (declarative style)
- Expressiveness
- Internal dependencies (i.e. begin_date /
end_date)
8Scenario
Ajax submit
Key Value
Foo.1.bar
Foo.2.buz
CGIExpand
table
HTML form
data tree
DataDomaininspect
decorate
invalid
JSON error messages
valid
process form (?DBIxDataModel)
see video
9Synopsis
- my domain Struct(
- anInt gt Int (-min gt 3, -max gt 18),
- aNum gt Num (-min gt 3.33, -max gt 18.5),
- aDate gt Date(-max gt 'today'),
- aLaterDate gt sub
- my context shift
- Date(-min gt context-gtflataDate)
- ,
- aString gt String(-min_length gt 2,
- -optional gt 1),
- anEnum gt Enum(qw/foo bar buz/),
- anIntList gt List(-min_size gt 1, -all gt
Int), - aMixedList gt List(Integer, String, Date),
- )
- my messages domain-gtinspect(some_data)
- display_error(messages) if messages
10Design principles
- Do One Thing Well just check
- no HTML form generation
- no Database schema generation
- no data modification ( filtering, canonic form)
- return informative messages
- concise yet expressive
- extensible (OO inheritance)
11Domain creation
- Object-oriented
- my dom DataDomainString-gtnew(
- -min gt "aaa",
- -max_length gt 8,
- -regex gt qr/foobar/,
- )
- Functional shortcuts
- my dom String(-min gt "aaa", ...)
- Default argument for each domain constructor
- my dom String(qr/foobar/) default is
-regex - Arguments add up constraints as "and"
12Generic arguments
- -optional
- if true, an undef value is accepted
- - name
- name to be returned in error messages
- - messages
- ad hoc error messages for that domain
13Builtin scalar domains
- Whatever (-defined, -true, -isa, -can)
- Num, Int (-min, -max, -range, -not_in)
- Date, Time(-min, -max, -range)
- String (-regex, -antiregex, -min, -max,
-range, -min_length, -max_length,
-not_in) - Enum (-values)
14Builtin structured domains
- List (-items, -min_size, -max_size,
-all, -any) - Struct (-fields, -exclude)
- One_of (-options)
15Example
- use RegexpCommon
- sub Name
- return String(-regex gt qr/-.
alpha/, - -antiregex gt qr/REprofanity/,
- _at__)
-
- my person_dom Struct(
- lastname gt Name,
- firstname gt Name(-optional gt 1),
- d_birth gt Date(-optional gt 1,
- -max gt 'today'),
- )
16Lazy Domains
- Principle
- a coderef that returns a domain at the time it
inspects a value - can look at the surrounding context (subvalues
seen so far) - my person_dom Struct(
- ...
- d_birth gt Date(-optional gt 1,
- -max gt 'today'),
- d_death gt sub
- my context shift
- return Date(-min gt context-gtflatd_birth)
- ,
- )
-
(inspiration ParseRecDescent)
17What is in the "context"
- root
- top of tree
- path
- sequence of keys or array indices to the current
node - list
- ref to last array visited while walking the tree
- flat
- flattened hash with all keys seen so far
18Example Contextual sets
my some_cities Switzerland gt qw/Genève
Lausanne Bern Zurich Bellinzona/, France
gt qw/Paris Lyon Marseille Lille Strasbourg/,
Italy gt qw/Milano Genova Livorno Roma
Venezia/, my domain Struct( country gt
Enum(keys some_cities), city gt sub
my context shift my country
context-gtflatcountry return
Enum(-values gt some_cities-gtcountry) ,
)
19Example Ordered list
my domain List(-all gt sub my context
shift my index context-gtpath-1
return Int if index 0 first item my min
context-gtlistindex-1 1 return
Int(-min gt min) )
20New Domain Constructors
- by wrapping
- sub Phone
- String(-regex gt qr/\?0-9() /,
-messages gt "Invalid phone number", - _at__)
-
- by subclassing
- implement new()
- implement _inspect()