dry-rb / dry-schema

Coercion and validation for data structures
https://dry-rb.org/gems/dry-schema
MIT License
426 stars 109 forks source link

Provide a way of defining schemas for arrays as root nodes #22

Open flash-gordon opened 5 years ago

flash-gordon commented 5 years ago

As an example

Dry::Schema.JSON do
  each do
    schema do
      required(:name).filled(:str?)
      required(:age).filled(:int?)
    end
  end
end
Morozzzko commented 5 years ago

Is it still coming?

https://dry-rb.zulipchat.com/#narrow/stream/191662-general/topic/dry-schema/near/165152587

solnic commented 5 years ago

I've been thinking about it, we could give this a shot, we have better APIs to build this feature now. The only difference would be to use array instead of each.

dsennerlov commented 5 years ago

Also needs to support the simple case of

[
    "I'm a string",
    "I'm another string"
]
CesarOliveira commented 4 years ago

There is a way to workaround this until have a final solution?

solnic commented 4 years ago

@CesarOliveira you can define a schema for an array item and have a wrapper that applies it to each item and then merge results into one.

CesarOliveira commented 4 years ago

hi @solnic I thought something like that, put a verification inside the match_schema.rb if is a collection and call the schema for each item, but I thought maybe was something similar done in the gem, thanks for the answer and congrats in the gem as well

volkanunsal commented 3 years ago

Any update on this feature?

solnic commented 3 years ago

@volkanunsal not yet, still scheduled for 2.0.0 though

Drowze commented 9 months ago

I guess this is perhaps related to this issue... But coercion doesn't seem to be working as expected on 2D (and possibly N-dimensional) arrays. See:

Given:

point = ['23', 42]
coordinates = [['23', 42], ['26', 43]]
coordinates_floats = [[23.0, 42.0], [26.0, 43.0]]

Coercion doesn't normally work when trying to create a schema for coordinates:

coordinates_schema = Dry::Schema.Params do
  required(:coordinates).value(:array, min_size?: 2).each do
    value(:array, size?: 2).each { value(Dry::Types["coercible.float"]) }
  end
end

coordinates_schema.call(coordinates: coordinates)
=> #<Dry::Schema::Result{:coordinates=>[["23", 42], ["26", 43]]} errors={:coordinates=>{0=>{0=>["must be a float"], 1=>["must be a float"]}, 1=>{0=>["must be a float"], 1=>["must be a float"]}}} path=[]>

It also didn't immediately work when I tried to exract the inner array into its own type:

point_type = Dry::Types["array"].constrained(size: 2).of(Dry::Types["coercible.float"])
point_schema = Dry::Schema.Params do
  required(:point).value(point_type)
end

coordinates_schema = Dry::Schema.Params do
  required(:coordinates).array(min_size?: 2) do
    value(point_type)
  end
end

point_schema.call(point: point)
#<Dry::Schema::Result{:point=>[23.0, 42.0]} errors={} path=[]>

coordinates_schema.call(coordinates: coordinates)
#<Dry::Schema::Result{:coordinates=>[["23", 42], ["26", 43]]} errors={:coordinates=>{0=>{0=>["must be a float"], 1=>["must be a float"]}, 1=>{0=>["must be a float"], 1=>["must be a float"]}}} path=[]>

coordinates_schema.call(coordinates: coordinates_floats)
#<Dry::Schema::Result{:coordinates=>[[23.0, 42.0], [26.0, 43.0]]} errors={} path=[]>

What eventually worked though was avoiding to expand the array definition (by using a custom type):

point_type = Dry::Types["array"].constrained(size: 2).of(Dry::Types["coercible.float"])
coordinates_schema = Dry::Schema.Params do
  required(:coordinates).array(point_type, min_size?: 2)
end

coordinates_schema.call(coordinates: coordinates)
#<Dry::Schema::Result{:coordinates=>[[23.0, 42.0], [26.0, 43.0]]} errors={} path=[]>

...and since I was already on that boat, I just converted everything to a custom tye and it worked alright:

coordinates_type = Dry::Types["array"].constrained(min_size: 2).of(
  Dry::Types["array"].constrained(size: 2).of(Dry::Types["coercible.float"])
)
coordinates_schema = Dry::Schema.Params do
  required(:coordinates).filled(coordinates_type)
end

coordinates_schema.call(coordinates: coordinates)
#<Dry::Schema::Result{:coordinates=>[[23.0, 42.0], [26.0, 43.0]]} errors={} path=[]>
vladimirtcats commented 1 week ago

Hi everyone. Is this feature going to be released? @solnic

timriley commented 1 week ago

This feature isn't built yet, but we'd welcome it and I'd be happy to support anyone who wants to give it a go :)

Otherwise, we'll get to it eventually, but cannot make any promises regarding timing.