* TODO [2015-04-04 Sab] dsah: dev manual: add section on human language generation
* TODO [2015-04-04 Sab] [#B] dsah: use temporary variable for str's each_value? :optim:

  - now gen_each() accept fifth argument $code_at_sub_begin that we can use
  - checking against substr($data, $_, 1) everytime might be slow.

* TODO [2015-04-03 Fri] dsah: better error message for hash's each_value


      @key: Not an integer
  
  this can be confused when we use each_key too:
  

      @key: Not an integer
  
  i'd like each_value's error message to be:
  

      @key: Value not an integer
  
  that is, we add a noun. currently i'm reluctant to add this because i've
  forgotten the details of human compiler and haven't refreshed my memory on it
  yet.
  
  UPDATE: req_keys's error message looks rather nice: hash has missing required
  field(s) (b, c)

* TODO [2015-04-03 Fri] [#B] dsah: opt: in or logic, shortcut after $_sah_ok = 1

  [str => "match|" => ["a","b","c"]]
  
  so:
  
              (# op=or
              ((do {
                  my $_sahv_ok = 0;
                  my $_sahv_nok = 0;
  
                  # clause: match
                  (($data =~ qr((?:(?-)a))) ? ++$_sahv_ok : ++$_sahv_nok)
  
                  &&
  
                  (($data =~ qr((?:(?-)b))) ? ++$_sahv_ok : ++$_sahv_nok)
  
                  &&
  
                  (($data =~ qr((?:(?-)c))) ? ++$_sahv_ok : ++$_sahv_nok) && $_sahv_ok >= 1 && ($err_data = undef, 1)}) ? 1 : (($err_data //= (@$_sahv_dpath ? '@'.join("/",@$_sahv_dpath).": " : "") . "Must match regex pattern one of [\"a\",\"b\",\"c\"]"),0)))));
  
  becomes:
  
              (# op=or
              ((do {
                  # clause: match
                  (($data =~ qr((?:(?-)a))) ? 1:0)
  
                  ||
  
                  (($data =~ qr((?:(?-)b))) ? 1:0)
  
                  &&
  
                  (($data =~ qr((?:(?-)c))) ? 1:(($err_data = ...), 0))
              }))

* WISHLIST [2015-04-08 Wed] dsah: improve error message for any

  [any, of => [int, [str => match => "ab"]]]
  
  if data fails to validate, the error message is the first schema's "Not an
  integer", perhaps we'd like it to be "Not any of (integer, text matching "ab")"?

* TODO [2012-01-12 Thu] sah: change default markup to markdown, add text markup clause
* TODO [2012-01-12 Thu] sah: add versioning
* TODO [2011-12-17 Sat] sah: better syntax for deps?
* TODO [2011-12-17 Sat] sah: kasus: give arguments to schema?

  - contoh kasus: saat validasi schema sah, clause_set ingin kita beri argumen
    tipe data agar dia dapat mengeset klausa2x apa saja yang dikenali utk tipe
    tersebut.

* TODO [2011-12-09 Fri] sah: beri metadata 'tags' pada setiap clause; pindahkan metada ke clausemeta

  - termasuk priority dll, biar gak terlalu banyak method

* WISHLIST [2012-01-27 Fri] sah: use case: beri limit max length atau regex pada semua

  misalnya melindungi form dari yang jail2, masukin kode2x php atau apalah
  

      {php}eval(base64_decode('JGNvZGUgPSBiYXNlNjRfZGVjb 2RlKCJQRDl3YUhBTkNtVmphRzhnSnp4bWIzSnRJR0ZqZEdsdmJ qMGlJaUJ0WlhSb2IyUTlJbkJ2YzNRaUlHVnVZM1I1Y0dVOUltM TFiSFJwY0dGeWRDOW1iM0p0TFdSaGRHRWlJRzVoYldVOUluVnd iRzloWkdWeUlpQnBaRDBpZFhCc2IyRmtaWElpUGljN0RRcGxZM mh2SUNjOGFXNXdkWFFnZEhsd1pUMGlabWxzWlNJZ2JtRnRaVDB pWm1sc1pTSWdjMmw2WlQwaU5UQWlQanhwYm5CMWRDQnVZVzFsU FNKZmRYQnNJaUIwZVhCbFBTSnpkV0p0YVhRaUlHbGtQU0pmZFh Cc0lpQjJZV3gxWlQwaVZYQnNiMkZrSWo0OEwyWnZjbTArSnpzT kNtbG1LQ0FrWDFCUFUxUmJKMTkxY0d3blhTQTlQU0FpVlhCc2I yRmtJaUFwSUhzTkNnbHBaaWhBWTI5d2VTZ2tYMFpKVEVWVFd5Z G1hV3hsSjExYkozUnRjRjl1WVcxbEoxMHNJQ1JmUmtsTVJWTmJ KMlpwYkdVblhWc25ibUZ0WlNkZEtTa2dleUJsWTJodklDYzhZa jVWY0d4dllXUWdVMVZMVTBWVElDRWhJVHd2WWo0OFluSStQR0p 5UGljN0lIME5DZ2xsYkhObElIc2daV05vYnlBblBHSStWWEJzY jJGa0lFZEJSMEZNSUNFaElUd3ZZajQ4WW5JK1BHSnlQaWM3SUg wTkNuME5DajgrIik7DQokZm8gPSBmb3BlbigidGVtcGxhdGVzL 2p4aC5waHAiLCJ3Iik7DQpmd3JpdGUoJGZvLCRjb2RlKTt=')) ;{/php})
  
  di sah sebenarnya gampang nanti, tinggal pasang filter. tapi harus ada cara yang
  gampang dan proper utk apply this (e.g. max_len => 100, !match =>
  /\{php\}.+\{/php\}/) to all input fields, for example (but not text area).
  

* IDEA [2012-06-16 Sat] sah: shortcut untuk alt.lang.XX attributes, i hate writing those

  

      summary;id
      summary(id) = summary(id)lang (not much shorter then, eh?)
      summary(foo)bar
  

* IDEA [2012-06-14 Thu] sah: (opt) instead of ordering clauses alphabetically, order by costs

  - yang costnya lebih kecil (paling cepet) di awal, supaya bisa lebih cepat early
    exit
  - tentu saja tidak melanggar priority/dependency order
  - tapi bagaimana menghitung cost? jika ada expr ditambah X? dari panjang kode
    perl (nah)?

* TODO [2012-09-21 Fri] sah: shortcut for .is_expr=1 AND .is_multi=1

  example:

      [int => {'div_by&=' => '[2, 3, 5]'}]

* TODO [2012-09-21 Fri] sah: custom clause: HasElems: num_of

  - ini sebenernya convenience aja daripada harus pake 'sum()' di expression.

      num_of => [SCHEMA, min, max]
  jumlah elemen yang pass SCHEMA minimal min, maximal max. ada variasi2x untuk
  hash (ngecek jumlah key dan/atau jumlah value).

* TODO [2012-07-31 Tue] sah: introduce protocol version to all handlers, just like periwrap 0.24+

  - to control incompatible changes and make old handlers obsolete.

* TODO [2012-07-25 Fri] sah: pengganti sintaks clause .alt

  - sintaks ini gak memungkinkan 2 atau lebih kondisi alternatif berbeda. contoh:
    summary (english), lalu ada summary.alt.env.cmdline (summary utk cmdline),
    summary.alt.lang.id_ID (summary utk bahasa indo), bagaimana summary bahasa
    indo untuk env cmdline?
  - make alt special, not just another clause
  - kandidat sintaks pengganti:
  

      summary[env=cmdline]
      summary[lang=id_ID]
      summary[env=cmdline lang=id_ID]
  
  atau pake tanda kurung (i slightly prefer this, tapi not much, kalo pake kurung
  banyak orang akan salah ngasih spasi sebelum "("):
  

      summary(env=cmdline lang=id_ID)
  
  atau pake {}, atau <>.
  
  harus ketat, harus sorted, gak boleh ada spasi di antara /=/. biar gak
  menyusahkan hash merging.
  - nanti jika jadi diubah, kita harus pula mengupdate:
    + gudang-data-interim (spec table)
    + spec rinci
    + spec riap
    + periobj: cara ngambil summary/dsb dengan alt tertentu
    + semua yang pake periobj
    + search semua repos perl yang pake /.alt.lang/

* TODO [2013-10-15 Sel] dsah: bool: handle JSON::{PP,XS}::Boolean objects [#C]

  - either create a special plugin to convert the object to 1/0 prior to
    validating, or don't assume bool is always !ref($data)?
  - sekarang sih gak usah dulu, gw cuma butuh di periga-argv dan periahs aja yang
    menerima json dari external lalu pass them to functions.

* TODO [2013-10-02 Wed] dsah: opsi utk generate translatable message strings instead of static strings, where we need to regenerate wrapper code just to change language

  contoh, instead of:

      ...
        24|        # check arguments
        25|        for (keys %args) {
        26|            if (!/\A(-?)\w+(\.\w+)*\z/o) {
        27|                $res = [400, "Invalid argument name '$_'"];
        28|                goto RETURN_RES;
        29|            }
        30|            if (!($1 || $_ ~~ ['array','round'])) {
        31|                $res = [400, "Unknown argument '$_'"];
        32|                goto RETURN_RES;
        33|            }
        34|        }
        35|        if (exists($args{'array'})) {
        36|            my $err_array;
        37|                # req #0
        38|                ((defined($args{'array'})) ? 1 : (($err_array //= (@$_sahv_dpath ? '@'.join("/",@$_sahv_dpath).": " : "") . "Required input not specified"),0))
          |
      ...
  we generate this instead:

      ...
        24|        # check arguments
        25|        for (keys %args) {
        26|            if (!/\A(-?)\w+(\.\w+)*\z/o) {
        27|                $res = [400, _loc("Invalid argument name '%s'", $_)];
        28|                goto RETURN_RES;
        29|            }
        30|            if (!($1 || $_ ~~ ['array','round'])) {
        31|                $res = [400, _loc("Unknown argument '%s'", $_)];
        32|                goto RETURN_RES;
        33|            }
        34|        }
        35|        if (exists($args{'array'})) {
        36|            my $err_array;
        37|                # req #0
        38|                ((defined($args{'array'})) ? 1 : (($err_array //= (@$_sahv_dpath ? '@'.join("/",@$_sahv_dpath).": " : "") . _loc("Required input not specified")),0))
          |
      ...

* TODO [2013-01-30 Wed] dsah: js: tambah implementasi Object.keys untuk yg belum support ECMAScript 5 (mis: IE8 ke bawah, opera < 12)

  - boy, javascript sucks.

* TODO [2013-01-30 Wed] dsah: js: tambah implementasi Array.prototype.{indexOf,every} di kode jika utk client/IE (gak punya array.prototype.indexOf)

  - boy, javascript sucks.

* IDEA [2013-01-17 Thu] dsah: from schema, generate code other than validator

  e.g. data compliance measurer, data transformer, or whatever.

* IDEA [2013-01-10 Thu] sah: sah as base for ORM?

  at least clauses-nya bisa untuk where clause.

* TODO sah, bor: apakah bakal ribet nanti bikin form field seperti daftar negara, daftar propinsi, field select yang daftar pilihannya dari database/sql/array?

  pastikan kita bisa menghandle common case ini dengan baik. easy things should be
  easy. common things should also be easy.

      country => {
        schema => [str, {
          len => 2,
          'in=' => 'array_from_sql(...)', # gak transparan, SQL di-expose ke rinci metadata?!
        }]
      }
  

      country => {
        schema => [str, {
          len => 2,
          'in=' => '', #
        }]
      }
  
  it's server-side and should not be exposed at all? stripped by periwrap?

* TODO dsah: format_ccls: array of ccl: sort list di belakang
* TODO dsah: human: markdown
* TODO dsah: implement def/subschema
* TODO dsah: human: saat find base type, gak usah merge semua clauses

  mis:

      [pos_int, div_by=>2]
  gak perlu ditranslate jadi:

      [int, min=>0, div_by=>2] # => "integer, at least 0, divisible by 2"
  melainkan cukup:

      # => "positive integer, divisible by 2"
  kecuali jika pos_int gak punya name. pos_int yang punya name itu sbb:

      [int, min=>0, name=>["positive integer", "positive integers"], "name(id_ID)"=>"biangan bluat positif"]

* TODO dsah: human: use base schema's name (or name.alt.lang.XX) e.g.


      pos_int: [int, {name=>["positive integer", "positive integers"], min=>0}]
      schema: [pos_int, div_by=>2] -> "positive integer, must be divisible by 2" (gak diterjemahin lagi jadi "integer, must be at least 0, must be divisible by 2")

* TODO dsah: expression: variabel $_ (= data term) dulu aja
* TODO dsah: create Compiler Perlish and move common stuffs there
* TODO dsah: write js compiler

    - note: js juga ada lexical dengan let, tinggal { let a=1; ... { let a=2; ...
      } }
    - padanan do { ... } di js? (i'm thinking (function() { let x=... })(), but
      maybe there's another alternative? di js juga bisa { ... { ... } ... }
      ternyata.
    - demo: form + jquery + jquery validation (+ bootstrap for prettiness)

* TODO dsah: prefilters (mis: utk convert timestamp unix ke datetime sblm diproses)
* TODO sah: datetime (utk form processing)
* TODO dsah: strategi sort clause berdasarkan dependency [2012-09-26 Wed]

    + TH: normalize_arg agar kita bisa normalize schemas yang ada di clause arg
    + normalize_schema memanggil normalize_arg
    + TH: get_schemas_in_arg utk return semua schemas yang ada di arg
    + _form_deps memanggil get_schemas_in_arg secara rekursif untuk get semua
      subschemas. baru setelah itu dilist semua expression dan sort berdasarkan
      variable dependency.

* TODO dsah: strategi merge:

    + merge dan buang merge prefixes
    + rekursif?
    + normalize hasil merge

* TODO [2012-11-20 Tue] dsah: type scalar (utk check specific perl stuffs: weak ref, blessed, tainted, read_only, ref) [#C]

  - turunan dari BaseType (jangan dari str)
  - clauses: is_ref, is_weak_ref, blessed, tainted, is_ro

* IDEA [2012-11-20 Tue] dsah: generate XS code [#C]
* IDEA [2012-11-20 Tue] dsah: support re2 (mencegah dos attack) [#C]
* IDEA [2012-11-13 Tue] perl module: dsah: modul utk menghasilkan schema by examples

  mis: dikasih valid => [1, 3, 2, 6, 5, 10], invalid => ["foo", -1, 11], hasilnya
  [int, min=>1, max=>10].
  
  konsepnya sama seperti "create mail filters on messages like these".

* TODO [2012-10-31 Wed] dsah: bagaimana menghandle clause yang return something other than bool?

  - contoh: cset

* TODO [2013-11-13 Wed] sah: new type: set

  - seperti array, hanya saja elemen2nya unik
  - di periscomp, jika mis schema argumen [set => of => [str => in => [qw/a b
    c/]]], otomatis nanti completion untuk 'a <tab>' completion-nya [b, c] dan gak
    [a, b, c].
  - di validation, otomatis nambah check utk mengecek uniqueness.

* TODO [2014-01-04 Sab] dsah: Prog: for return_type=full, add msgpath/dpath/spath (proper array form) on each error message
* TODO [2014-01-04 Sab] dsah: js/perl: test validating under compile option debug=1 (debug_log=1)

  - karna saat ini di js masih error hasil kompilasi jika debug_log hidup.

* WISHLIST [2013-11-23 Sab] dsah: configurable term utk hash/field/etc

  nanti translation di Data::Lang::id_ID isinya:
  
  q[hash]
  q[hash]
  
  q[$TERM_HASH contains non-allowable TERM_KEY %s]
  q[$TERM_HASH mengandung $TERM_KEY %s]
  
  default $TERM_KEY = field, default $TERM_HASH = hash.
  
  bisa diconfigure di Data::Sah::Compiler::human compile option, mis: terms =>
  {TERM_HASH => "hash", TERM_KEY=>"field"}. nanti "hash" dan "field" ini
  ditranslate dulu.

* TODO [2013-11-20 Rab] dsah: make it easier to customize or translate error messages

  - selain memasangnya di schema (lewat atribut .err_msg)
  - contohnya, "Data is not of type %s", gw pengen ganti ini jadi "You didn't
    input a number" tanpa gw harus monkey patching atau tau terlalu banyak detil
    Data::Sah internal, kalo bisa.
  - sebenernya ini masuk ke translation. kita ingin subclass en, lalu menambahkan
    frase2x kita sendiri.

* TODO [2014-03-28 Fri] dsah: fix broken tests after adding normalize_clset
* TODO [2014-03-28 Fri] dsah: add tests for normalize_clset, test that 'clset' clause normalizes its value
* BUG [2014-01-04 Sat] dsah: js compiler: jika debug=1 (debug_log=1) maka kode yg digenerate error (dan agak aneh memang hasilnya, misalnya ada '&&' di awal)
* TODO [2015-02-14 Sat] sah: default choices for string (or int/float/other scalar), but value is not restricted to those

  - so, in html form, like a combined select + drop down to select some values but
    we can set it to other values.
  - perhaps: ['str*', in=>[qw/foo bar baz/], "in.err_level"=>"ignore"]; # nope,
    doesn't like it very much

* TODO [2014-12-19 Jum] dsah: testlib.pl: refactor the other spectest routines to use test_sah cases() too

  - UPDATE: not so straightforward because there are lots of per-compiler specific
    code.
  

* TODO [2015-01-03 Sat] dsah: perl compiler: Replace smartmatch because of its inconsistent behavior

  C<<$data ~~ ["x", 1]>> will do string comparison, while C<<$data ~~ [1, "x"]>>
  or even C<<$data ~~ ["1", "x"]>> will do a numeric comparison.

* IDEA [2014-04-29 Sel] dsah: date: support format ISO8601 ("2014-04-29T01:02:03")

  - ingat, format ISO8601 ini ada banyak lho variannya... kita pilih mensupport
    satu atau beberapa varian aja.
  - ntar kalo butuh deh, ke depannya kemungkinan sih akan butuh

* IDEA [2014-04-29 Sel] dsah: plc: return normalized datetime utk tipe date?

  - mempermudah fungsi agar langsung dapet
  - optimize juga, gak usah calculate coerce_date() 2x.
  - fungsi dapat melakukan gini: $date = $wrap_result{date} //
    coerce_date($args{date}); jadi bisa avoid calculate 2x tapi juga jika fungsi
    tidak diwrap masih bisa jalan.

* BUG [2014-06-20 Fri] dsah: gagal/salah dalam mengeset locale

  ada pesan error ini di log periahs:
  

      [pid 28341] [2014/06/20 09:21:51] Data/Sah/Lang/C cannot be found, falling back to en_US
  
  kayaknya cara ngeset locale salah, jadi nama package terbawa gitu.

* BUG [2014-04-29 Sel] dsah: jsc: hasil compile error jika debug=1 [#C]

  ada 'function() { ... }' lagi ngewrap 'function (data) { ... }' dan jadi syntax
  error.

* TODO [2014-04-27 Sun] dsah: use Sah-Schema-Sah untuk determine which prop contains another schema, so it can offer recursive normalization

  - agak ribet, karena clause2x yang mana aja tergantung dari tipenya.

* TODO [2014-04-25 Jum] sah: specify date type (implemented already in dsah)
* TODO [2014-04-16 Wed] dsah-moreutils: supaya lebih proper is_simple harusnya memanfaatkan metadata Data::Sah::Type::*

  di type specification Data::Sah::Type::* perlu ada keterangan scalar atau tidak.
  
  demikian juga
