Why is hint on required type array excluded?


I was wondering why the following call does not return a hint on ‘must be an array’

Dry::Validation.Schema { required(:my_key).filled(:array?) }.call({:my_key => ''}).hints

The schema currently only returns an error on ‘must be filled’. IMHO it makes sense to also indicate as a hint that an array value is expected.

I found the following commit which introduces the exclusion of the array type check for hints :

This is altered a few times in different commits resulting in HINT_EXCLUSION constant in https://github.com/dry-rb/dry-validation/commit/96755f2c28b7a0c55b273de08c49b3ed6c22c378#diff-40a33adf2e599a5888b6e2b635de1b92)

I know I can make a custom predicate to circumvent this exclusion, but this seems overhead.
I guess there is a good reason to exclude this info from the hints, but I fail to see why…

some follow-up, I’m actually trying to validate an array of Hashes and want to:
(1) first detect it is an array
(2) detect it is not empty
(3) validate that each element of the array complies to something (lets keep it simple :value must be Hash)

The dry-validation docs contain an example which does (1) and (3)

 schema = Dry::Validation.Schema do
 schema.call(phone_numbers: '').messages
---> { :phone_numbers => ["must be an array"] }

But if I try to add restriction (2) it changes the error to {:phone_numbers=>[“must be filled”]} and looses the info on being an array…

Dry::Validation.Schema { required(:phone_numbers).filled(:array?).each(:hash?)}.call(phone_numbers: []).messages
=> {:phone_numbers=>["must be filled"]}


Dry::Validation.Schema { required(:phone_numbers).filled.each(:hash?)}.call(phone_numbers: []).messages
=> {:phone_numbers=>["must be filled"]}


Dry::Validation.Schema { required(:phone_numbers) { array? & filled? { each { hash?} } }}.call(phone_numbers: []).messages
TypeError: no implicit conversion of Symbol into Integer

There are two caveats here, both are high priority for fixing/improving before 1.0.0:

  • chaining is not supported, and it doesn’t raise when you try to do that. We’ll either add support for chaining (I would prefer that) or at least make it raise
  • each actually adds array? check for you, which is implicit and problematic in some cases, so it will be changed before 1.0.0

So, what you want now is this:

required(:phone_numbers).value(:array?, :filled?) { each(:hash?) }

This will check array? twice, but we gotta live with that for now.

Oh and to answer your question regarding hints, we exclude all primitive type checks from hints as they are so basic it didn’t make sense to include them, it is expected that your rules defines type checks as the very first rule, so it will always cause an error so we don’t need them as hints. We may try to experiment with enforcing that, so that if you have rules that don’t have any primitive type check, it’ll warn you.

Thanks. works nicely :wink:

Key point is indeed that chaining is not supported:

required(:phone_numbers).value(:array?, :filled?).each(:hash?) does not work

required(:phone_numbers).value(:array?, :filled?) { each(:hash?) } works fine...