Calling a macro on a depply nested rule

Hello,

I was wondering if it’s possible to apply a macro to each element of an array that is itself nested in an array. Consider the following structure

optional(:rooms).array(:hash) do
  required(:guests).array(:hash, GuestSchema)
end

I’d like to apply a pre-existing macro to each guest in such a structure. If rooms wasn’t array, but a hash I could just do rule(rooms: :guests).each(:macro), but in case of array that doesn’t seem to work. I’m also unable to call a macro from within a rule(:rooms).each block.

Can you provide an executable script/test that shows what you want to achieve?

Of course, I was thinking something like this: https://github.com/dry-rb/dry-validation/compare/master...DawidJanczak:macro-on-deeply-nested-rule (extending previous example in that file) The spec will fail early on while evaluating the schema, but I hope the full scenario will highlight the issue I’m running into.

1 Like

@solnic does the above spec work for you?

Just in case someone else is running into this the way we ended up handling this is by replacing macros with methods accepting key and value and invoking those methods manually for each key and value in the nested list.

Yeah I get it now. We’re missing support for specifying such paths. addresses.phones does not denote that addresses is an array with elements that have phones key. This will be supported eventually so stay tuned.

Feel free to report a feature request about it so that it’s easier to track progress.

Thank you @solnic, I opened an issue here https://github.com/dry-rb/dry-validation/issues/578 :slight_smile:

1 Like

Any progress in here? I saw on that forum, that part of the solution is to use

register_macro(:name_of_macro) do
  key([*path, :attribute]).failure("does not seem to be possible") unless ...
end

However, it will not show the full path to the nested attribute. Any ideas on a workaround ?

If you don’t mind, can you show the gist of your implementation?
Thanks in adavance.

Sure, it’s something like this:

    rule(:outer_array).each do                                                                                                                                                                                                                      
      value[:nested_array].each_with_index do |nested_elem, nested_elem_index|
        nested_key = key(key.path.keys + [:nested_elem_entry, nested_elem_index])

        validation_method(key: nested_key, value: nested_elem)
      end 
    end 

The above is for the deeply nested rule. The more shallow nesting is easier: rule(:inner_array).each { validation_method(key: key, value: value) }