These are chat archives for dry-rb/chat

23rd
Jul 2018
RKushnir
@RKushnir
Jul 23 2018 10:28

Hi. How would you go about validating a schema, when there’s one attribute(‘success’ | ‘error’) that defines the rest of the structure?

Is it possible to make not just one rule, but a whole nested schema, dependent on other rule?

Grant Shangreaux
@gcentauri
Jul 23 2018 14:12
depending on what you are doing, it may make sense to have two steps. the first one validate the single attribute (success | error) then use the value of that error to determine what schema to use for validating the rest of the structure? if success? success_schema.call(x) else error_schema.call(x)? @RKushnir
define separate schemas for the different cases
RKushnir
@RKushnir
Jul 23 2018 15:17
I see. That uses custom code to tie the schemas together. I was hoping to just validate the whole document with one schema
but thanks for suggestion @gcentauri
Grant Shangreaux
@gcentauri
Jul 23 2018 15:53
@RKushnir you might be able to do something like using the rule code to define a nested schema, but then why add that complexity? it seems like the schemas define two different data structures even though they are the same thing. i have the same issue in my work right now and i don't know if this is the right answer or not, but it began to feel like trying to do too much with one schema was causing more trouble than it was worth
I did see that dry-validation is going to be changing to handle more of the high level validation rules and dry-schema will be the sort of basic structure validation, so its possible that there is some work being done to address these kind of things
RKushnir
@RKushnir
Jul 23 2018 17:58

@gcentauri my reason for willing to keep just one schema is because this dual success/error structure is nested, the whole document looks like

{ results: [
  { status: ‘success’, payload… }, 
  { status: ‘error’, error_message: … },
   … 
] }

So I would need to validate the top level structure(either with one more schema, or manually), and then iterate over items

I tried to use rule, but my attempt didn’t work, it was something like this:
rule(successul: [:status]) do |status|
  status.eql?(’success’) & schema do
    required(:id)
  end
end
Grant Shangreaux
@gcentauri
Jul 23 2018 18:11
hmm. ok, i'm playing around with some things now, i'll let you know if i figure anything out. so the top level has a results key that holds an array of hashes that are either 'success' or 'error' and they both have different structures depending on if it is an error or success, correct?
Grant Shangreaux
@gcentauri
Jul 23 2018 18:35
ok, so i think the problem with using either rule or validate for validating the nested data here is that when the nested schema is called, it returns a result, which will be truthy even if the validation fails
Grant Shangreaux
@gcentauri
Jul 23 2018 18:43
you could maybe define the success/error schema to handle all the different possible parameters combined.
Grant Shangreaux
@gcentauri
Jul 23 2018 19:21
require 'dry-validation'

schema = Dry::Validation.Schema do
  required(:status).filled

  optional(:payload).schema do
    required(:id).filled
    required(:name).filled
  end

  optional(:error_message)

  rule(payload: [:status]) do |status|
    status.eql?('success').then(value(:payload).filled?)
  end

  rule(error_message: [:status]) do |status|
    status.eql?('error').then(value(:error_message).filled?)
  end
end

good_data = {status: 'success', payload: {id: 1, name: 'Foo'}}
bad_data = {status: 'success', payload: {id: 1}}
missing_payload = {status: 'success'}
error_data = {status: 'error'}

good = schema.call(good_data)
bad = schema.call(bad_data)
missing = schema.call(missing_payload)
error = schema.call(error_data)

#<Dry::Validation::Result output={:status=>"success", :payload=>{:id=>1, :name=>"Foo"}} errors={}>
#<Dry::Validation::Result output={:status=>"success", :payload=>{:id=>1}} errors={:payload=>{:name=>["is missing"]}}>
#<Dry::Validation::Result output={:status=>"success"} errors={:payload=>["must be filled"]}>
#<Dry::Validation::Result output={:status=>"error"} errors={:error_message=>["must be filled"]}>
thats probably the best i can do for now. maybe that will help @RKushnir
Grant Shangreaux
@gcentauri
Jul 23 2018 19:31
if any of the dry-rb authors are out there, perhaps there is a 'best' way for this kind of conditional validation?
RKushnir
@RKushnir
Jul 23 2018 21:08
thanks @gcentauri for so much effort. that’s what I also figured, it does the job, and is probably good enough. still, would be interesting to know if there’s any way to do a similar thing:
when(status: ‘success’).schema do
  required(:id).filled(:int?)
  required(:name).filled(:str?)
end

when(status: ‘error’).schema do
  required(:error_message).filled(:str?)
end
Grant Shangreaux
@gcentauri
Jul 23 2018 21:15
@RKushnir - no problem really, i was looking for an excuse to spend more time on that problem myself. thinking about my minimal time with real typed languages, it sort of seems like this would be something where you'd have a Result : Success | Error union type. then have some matcher that would use the proper validator for the Result.
it looks like dry-validation is on its way to a 1.0 release so perhaps we will find out a preferred way soon enough
RKushnir
@RKushnir
Jul 23 2018 21:16
I was typing just that :)
It reminds me of type unions in flow for JS
Grant Shangreaux
@gcentauri
Jul 23 2018 21:18
i have faith that some combo of a dry-types definition, schemas, and high level validation rules can do the trick