# How to define nested array of objects with validation and map them

**URL:** <https://discourse.dry-rb.org/t/how-to-define-nested-array-of-objects-with-validation-and-map-them/774>\
**Category:** Uncategorized\
**Created:** [May 6, 2019, 3:56pm UTC](https://discourse.dry-rb.org/t/how-to-define-nested-array-of-objects-with-validation-and-map-them/774 "2019-05-06T15:56:27Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![dnagir](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/dnagir/32/372_2.png) [@dnagir](https://discourse.dry-rb.org/u/dnagir)\
**Post date:** [May 6, 2019, 3:56pm UTC](https://discourse.dry-rb.org/t/how-to-define-nested-array-of-objects-with-validation-and-map-them/774/1 "2019-05-06T15:56:27Z")

</div>

I’m struggling to define the validation/schema/types (using `dry-validation` `rc3`) that:

- takes a nested field (along with the top level ones): `{ line_items: [{ quantity: 1, code: 'ABC' }] }`
- applies the custom validation to each item (quantity can only be 1 if code is `ABC`)
- maps the resulting (valid) hash to a `Types::Array.of(LineItemType)` (where `LineItemType` is similar to `LineItemType = Types::Constructor(::LineItem)`

I’ve struggled to find the examples in the documentation with not much success (no docs for v1 by the look of it?). I also got lost trying to look it up in the codebase.

For example, the input:

```json
{
  "discount": 123,
  "line_items": [
    { "quantity": 1, "code": "ABC" }
  ]
}

```

Would need to be mapped (after the validations) into a `Dry::Type`-ed object:

```ruby
Order.new(
  discount: 123,
  line_items: [LineItem.new(quantity: 1, code 'ABC')]
)

```

Would appreciate some help.

Cheers,  
D.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [May 6, 2019, 7:29pm UTC](https://discourse.dry-rb.org/t/how-to-define-nested-array-of-objects-with-validation-and-map-them/774/2 "2019-05-06T19:29:10Z")

</div>

Both `dry-schema` and `dry-validation` _are not_ meant to be used for instantiating custom objects.

Why not just do this:

```ruby
require 'dry/validation'
require 'dry/types'
require 'dry/struct'

module Types
  include Dry::Types()
end

class OrderContract < Dry::Validation::Contract
  params do
    required(:discount).value(:integer)

    required(:line_items).array(:hash) do
      required(:quantity).value(:integer)
      required(:code).value(:string)
    end
  end

  rule(:line_items) do
    values[:line_items].each_with_index do |item, idx|
      key([:line_items, idx]).failure('quantity must be 1') if item[:quantity] != 1 && item[:code] == 'ABC'
    end
  end
end

class LineItem < Dry::Struct
  attribute :quantity, Types::Integer
  attribute :code, Types::String
end

class Order < Dry::Struct
  attribute :discount, Types::Integer
  attribute :line_items, Types::Array.of(LineItem)
end

contract = OrderContract.new

data = {
  discount: 4,
  line_items: [
    { quantity: 2, code: 'FOO' },
    { quantity: 3, code: 'ABC' },
    { quantity: 4, code: 'BAR' }
  ]
}

result = contract.(data)

puts result.errors.to_h.inspect
# {:line_items=>{1=>["quantity must be 1"]}}

data = {
  discount: 4,
  line_items: [
    { quantity: 2, code: 'FOO' },
    { quantity: 1, code: 'ABC' },
    { quantity: 4, code: 'BAR' }
  ]
}

result = contract.(data)

puts result.errors.to_h.inspect
# {}

order = Order.new(data)

puts order.inspect
# #<Order discount=4 line_items=[#<LineItem quantity=2 code="FOO">, #<LineItem quantity=1 code="ABC">, #<LineItem quantity=4 code="BAR">]>

```

❓ ❓

---

<div class="post-metadata">

**Author:** ![dnagir](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/dnagir/32/372_2.png) [@dnagir](https://discourse.dry-rb.org/u/dnagir)\
**Post date:** [May 7, 2019, 1:04am UTC](https://discourse.dry-rb.org/t/how-to-define-nested-array-of-objects-with-validation-and-map-them/774/3 "2019-05-07T01:04:09Z")

</div>

Thanks @solnic. That would be fine.

How would you be able to validate the nested values though (`quantity` can only be `1` if the `code=='ABC'`)? The `rule` doesn’t seem to exist on the nested schema.

* * *

_Side note…_

Not sure if that helps - maybe worth adding to the docs that custom objects instantiation is not supported?

I was under impression that it was ok because the values can be coerced using the `Types::Constructor(Tod::TimeOfDay) { |s| Tod::TimeOfDay.parse(s) }`.

So it is surprising for me that I cannot compose `Array.of` with `Constructor`.  
Would help me massively If I somehow knew earlier (another option might be raising an exception when composing unsupported types during the validation).

Not sure if that makes sense for you though 🙏

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [May 7, 2019, 8:24am UTC](https://discourse.dry-rb.org/t/how-to-define-nested-array-of-objects-with-validation-and-map-them/774/4 "2019-05-07T08:24:22Z")

</div>

> [@dnagir](#):
>
> How would you be able to validate the nested values though ( `quantity` can only be `1` if the `code=='ABC'` )? The `rule` doesn’t seem to exist on the nested schema.

I updated the above example to be complete and working. Also, here’s a gist with the same code [dry\_validation\_and\_struct.rb · GitHub](https://gist.github.com/solnic/5ee0a24b688af87ae23cec4384e33ab0)

> [@dnagir](#):
>
> Not sure if that helps - maybe worth adding to the docs that custom objects instantiation is not supported?

Yes this is a good suggestion. I’m working on new docs so I’ll make sure to include this information.

> [@dnagir](#):
>
> I was under impression that it was ok because the values can be coerced using the `Types::Constructor(Tod::TimeOfDay) { |s| Tod::TimeOfDay.parse(s) }` .
> 
> So it is surprising for me that I cannot compose `Array.of` with `Constructor` .  
> Would help me massively If I somehow knew earlier (another option might be raising an exception when composing unsupported types during the validation).

I can understand why it confused you. The reason why it’s not supported is that the whole idea behind this style of validation is to validate “pure data” (so, primitive objects, like hashes, arrays, strings, numbers etc.) before the data can be passed down to your system. Instantiating custom objects prior validation is just against this idea BUT it is clearly not enforced by the library in any way, which is the actual problem we’re having here.

I need to think about it because it’s not immediately obvious how to deal with it 🙂 I’ll figure it out eventually. Thanks for feedback!
