package Sah::FAQ; # just to make PodWeaver happy

our $VERSION = '0.9.14'; # VERSION

1;
# ABSTRACT: Frequently asked questions


__END__
=pod

=head1 NAME

Sah::FAQ - Frequently asked questions

=head1 VERSION

version 0.9.14

=head1 GENERAL

=head3 Why use a schema (a.k.a "Turing tarpit")? Why not use pure Perl?

Schema language is a specialized language (DSL) that should be more concise to
write than equivalent Perl code for common validation tasks. Its goal is never
to be as powerful as Perl.

90% of the time, my schemas are some variations of the simple cases like:

 "str*"
 ["str":   {"len_between": [1, 10], "match": "some regex"}]
 ["str":   {"in": ["a", "b", "c", ...]}]
 ["array": {"of": "some_other_type"}]
 ["hash":  {"keys": {"key1": "some schema", ...}, "req_keys": [...], ...}]

and writing schemas is faster and less tedious/error-prone than writing
equivalent Perl code, plus L<Data::Sah> can generate JavaScript code and human
description text for me. For more complex validation I stay with Sah until it
starts to get unwieldy. It usually can go pretty far since I can add functions
and custom clauses to its types; it's for the very complex and dynamic
validation needs that I go pure Perl. Your mileage may vary.

=head3 What does "Sah" mean?

Sah is an Indonesian word, meaning "valid" or "legal". It's picked because it's
short.

The previous incarnation of this module uses the namespace L<Data::Schema>,
started in 2009 and deprecated in 2011 in favor of "Sah".

=head2 Comparison to other schema languages and type systems

=head3 Comparison to JSON schema?

=over 4

=item * JSON schema limits its type system to that supported by JSON/JavaScript.

=item * JSON schema's syntax is simpler.

It's metaschema (schema for the schema) is only about 130 lines. There are no
shortcut forms.

=item * JSON schema's features are more limited.

No expression, no function.

=head3 Comparison to Data::Rx?

TBD

=head3 Comparison to Data::FormValidator (DFV)?

TBD

=head3 Comparison to Moose types?

TBD

=head1 SYNTAX

=head2 General

=head3 Why is C<req> not enabled the default?

I am following SQL's behavior. A type declaration like:

 INT

in SQL means C<NULL> is allowed, while:

 INT NOT NULL

means C<NULL> is not allowed. The above is equivalent to specifying this in Sah:

 int*

One could argue that setting C<req> to 1 by default is safer/more convenient to
her/whatever, and C<int> should mean C<< ["int", "req", 1] >> while something
like perhaps C<int?> means C<< ["int", "req", 0] >>. But this is simply a design
choice and each has its pros/cons. Nullable by default can also be convenient in
some cases, like when specifying program options where most of the options are
optional.

=head3 How about adding a C<default_req> configuration in C<Data::Sah> then?

In general I am against compiler configuration which changes language behavior.
In this case, it makes a simple schema like C<int> to have ambiguous meaning (is
undefined value allowed? Ir not allowed? It depends on compiler configuration).

=head3 How to express "not-something"? Why isn't there a C<not> or C<not_in> clause?

There are generally no C<not_CLAUSE> clauses. Instead, a generic C<!CLAUSE>
syntax is provided. Examples:

 // an integer that is not 0
 ["int", {"!is": 0}]

 // a username that is not one of the forbidden/reserved ones
 ["str", {"!in": ["root", "admin", "superuser"]}]

=head2 How to state C<in> as well as C<!in> in the same clause set?

You can't do this since it will cause a conflict:

 ["str ", {"in": ["a","b","c"], "!in": ["x","y","z"]}]

However, you can do this:

 ["str ", {"cset&": [{"in": ["a","b","c"]}, {"!in": ["x","y","z"]}]}]

=head3 How to express mutual failure ("if A fails, B must also fails")?

You can use C<if> clause and negate the clauses. For example:

 "if": [{"!div_by": 2}, {"!div_by": 5}]

=head3 General advice when writing schemas?

=over 4

=item * Avoid C<any> or C<all> if you know that data is of a certain type

For performance and ease of reflection, it is better to create a custom clause
than using the C<any> type, especially with long list of alternatives. An
example:

 // dns_record is either a_record, mx_record, ns_record, cname_record, ...
 ["any", "of", [
         "a_record",
         "mx_record",
         "ns_record",
         "cname_record",
         ...
     ]
 ]

 // base_record
 ["hash", "keys", {
     "owner": "str*",
     "ttl": "int",
 }]

 // a_record
 ["base_record", "merge.normal.keys", {
     "type": ["str*", "is", "A"],
     "address": "str*"
 }]

 // mx_record
 ["base_record", "merge.normal.keys", {
     "type": ["str*", "is", "MX"],
     "host": "str*",
     "prio": "int"
 }]

 ...

If you see the declaration above, every record is a hash. So it is better to
declare C<dns_record> as a C<hash> instead of an C<any>. But we need to select a
different schema based on the C<type> key. We can develop a custom clause like
this:

 ["hash", "select_schema_on_key", ["type", {
     "A": "a_record",
     "MX": "mx_record",
     "NS": "ns_record",
     "CNAME": "cname_record",
     ...
 }]]

This will be faster.

=back

=head2 Hash

=head3 How does Sah check allowed/unallowed keys?

If C<keys> clause is specified, then by default only keys defined in C<keys>
clause is allowed, unless the C<.restrict> attribute is set to false, in which
case no restriction to allowed keys is done by the clause. The same case for
C<re_keys>.

If C<allowed_keys> and/or C<allowed_keys_re> clause is specified, then only keys
matching those clauses are allowed. This is in addition to restriction placed by
other clauses, of course.

=head3 How do I specify schemas for some keys, but still allow some other keys?

Set the C<.restrict> attribute for C<keys> or C<re_keys> to false. Example:

 ["hash", {
     "keys": {"a": "int", "b": "int"},
     "keys.restrict": 0,
     "allowed_keys": ["a", "b", "c", "d", "e"]
 ]

The above schema allows keys C<a, b, c, d, e> and specifies values for C<a, b>.
Another example:

 ["hash", {
     "keys": {"a": "int", "b": "int"},
     "keys.restrict": 0,
     "allowed_keys_re": "^[ab_]",
 ]

The above schema specifies values for C<a, b> but still allows other keys
beginning with an underscore.

=head3 What is the difference between the C<keys> and C<req_keys> clauses?

C<req_keys> require keys to I<exist>, but their values are governed by the
schemas in C<keys> or C<keys_re>. Here are four combination possibilities, each
with the schema:

To require a hash key to exist, but its value can be undef:

 ["hash", "keys", {"a": "int"}, "req_keys": ["a"]]

To allow a hash key to not exist, but when it exists it must not be
undef:

 ["hash", "keys", {"a": "int*"}]

To allow a hash key to not exist, or its value to be undef when exists:

 ["hash", "keys", {"a": "int"}]

To require hash key exist and its value must not be undef:

 ["hash", "keys", {"a": "int*"}, "req_keys": ["a"]]

=head3 Merging and hash keys?

XXX (Turn off hash merging using the C<''> Data::ModeMerge options key.

=head1 AUTHOR

Steven Haryanto <stevenharyanto@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2012 by Steven Haryanto.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut

