dry-schema
                                
                                 dry-schema copied to clipboard
                                
                                    dry-schema copied to clipboard
                            
                            
                            
                        Make JSON schema resilient to any JSON-compatible value as input
The JSON standard defines the following data types
- number (WRT ruby both float and integer shall be supported)
- string
- boolean
- array
- object (represented as Hash in ruby)
- null(=- nil)
If any JSON schema receives any value of the types above it shouldn't raise an exception at least. Better yet it should provide a helpful error message.
We want this for 1.0.0?
I guess so, it's just about having a sanity check at the beginning of validation, no?
@flash-gordon yes, JSON is a Processor specialization, it supports adding arbitrary steps, it simply passes result-like objects from step to step, and can aggregate errors in this process. This means you should be able to add a special validation step to do your checks. I suspect we'll want to make step API nicer to work with though, but its core functionality is implemented already.
Can we make :float values in JSON schema to be coercible from integers?
Javascript from {a: 1.0} generates JSON {"a": 1}, and then ruby parses this 1 to integer value.
And json schema returns error that value must be float.
Looks like a bug
@aglushkov we can, but it looks like it would be a breaking change. I think it should be possible to use a custom type container to override json's float as a workaround for the time being.
I reckon we could have something like this @flash-gordon :
Dry::Schema.JSON(Array) do
   # do your thing
end
Dry::Schema.JSON do
   # do your thing
end
API-wise it's not a big deal, though I would expect .JSON and .JSON(Hash) to work the same way, just in case
I mean, no matter how we do this on the user side, dealing with arrays inside dry-schema will be non-trivial
I would propose to introduce the root keyword:
Dry::Schema.JSON do
  root.array(:integer)
end
To implement this I would:
- add a special __root__key to the schema
- wrap input with hash before passing to the steps
- filter __root__out from the result
- return an error on "root" schema nesting
- handle a special case on passing "root" schema as value
It's a bit hacky, but still, wdyt? @solnic @flash-gordon
Why not having something like .JSONArray instead? It would be easier to implement IMO
besides, the biggest problem I think is producing error messages, not the DSL part
besides, the biggest problem I think is producing error messages, not the DSL part
I think you can pass a special option to the message_compiler to skip the first node of the ast. Still don't know how this will play with dry-v though.
Here's some food for thought:
require 'dry/schema'
Dry::Schema::ProcessorSteps.prepend(Module.new {
  def call(result)
    catch(:halt) { super }
    result
  end
})
class Dry::Schema::MessageCompiler
  def visit_unexpected_input(node, opts)
    input, * = node
    Dry::Schema::Message.new(
      path: [nil],
      predicate: nil,
      text: "we did not expect this",
      input: input
    )
  end
end
schema = Dry::Schema.JSON do
  required(:name).filled(:string)
  before(:key_coercer) do |result|
    unless result.output.is_a?(Hash)
      result.add_error([:unexpected_input, [result.output]])
      throw :halt
    end
  end
end
puts schema.("oops").errors.to_h.inspect
# {nil=>["we did not expect this"]}
#
# `nil` represents input itself in error hash
For anyone in the future, before using catch/throw pls consult me :) Maybe we'll be able to find a better solution.
@flash-gordon we can have result.halt that just sets it internally to a failure state and further processing won't happen
@solnic yeahyeah sure, I just mean using catch/throw without a real need may have undesirable consequences. And even though this doesn't seem to be the case here, I'd like to try other options if available