Giter VIP home page Giter VIP logo

sorbet-schema's Issues

Add type validations

When we ApplyValidators, we should check the field type against the given value. For now, we won't do any coercion and simply return validation issues that we see.

Add ability to set custom type serializer

We have support for adding a serializer to a field to change its serialization behavior; it would be nice to be able to set a serializer based on a type that would be used for any matching type in a schema.

Prevent casting when chaining from deserialize

Currently, when you deserialize into a struct, we need to do casting if we use the result in a chain:

CreateEntityRequest
  .deserialize_from(:hash, entity_params.to_h)
  .and_then { |create_entity_request| CreateEntity.call(T.cast(create_entity_request, CreateEntityRequest)) }

This is because the deserialize result payload is a T::Struct but we can't guarantee the type. We should be able to fix this with a Tapioca compiler that adds more specific RBIs for each T::Struct present in your application.

Nested Structs with custom Coercer

I didn't have access to make a branch and I got lazy so going to make this issue just so you have something to noodle on tomorrow if you're bored.

Sorbet-schema can be used to deserialize data into a struct with a custom coercer.
struct Example
const :country, Country

It can also be used to deserialize data into a struct with a nested struct.
struct Nested
const :name, String

struct Example
const :nested, Nested

It doesn't work though when you want to deserialize data into a struct with a nested struct that has a custom coercer.
struct Nested
const :country, Country

struct Example
const :nested, Nested

With this last example, it'll still create the Example struct with the Nested struct correctly created but country will not be coerced, it'll just be the string representation I passed as json. This is because the nested struct will get converted using the Struct coercer and this will just not consider any custom coercers https://github.com/maxveldink/sorbet-schema/blob/main/lib/typed/coercion/struct_coercer.rb#L24.

Here's a first draft at how I got it working but I'm sure there's a better way to do it.

       sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) }
      def coerce(type:, value:)
        return Failure.new(CoercionError.new("Field type must inherit from T::Struct for Struct coercion.")) unless used_for_type?(type)
        return Success.new(value) if type.recursively_valid?(value)

        return Failure.new(CoercionError.new("Value of type '#{value.class}' cannot be coerced to #{type} Struct.")) unless value.is_a?(Hash)

        values = {}
        type.raw_type.props.each do |name, prop|
          if prop[:type_object].try(:raw_type).respond_to?(:props)
            Typed::HashSerializer
              .new(schema: Typed::Schema.from_struct(prop[:type_object].raw_type))
              .deserialize(value[name])
              .and_then { |struct| Typed::Success.new(values[name] = struct) }
              .on_error { |error| return Failure.new(CoercionError.new("Nested hash for #{type} could not be coerced to #{name}")) }
          else
            Coercion
              .coerce(type: prop[:type_object], value: value[name])
              .and_then { |coerced_value| Typed::Success.new(values[name] = struct) }
              .on_error { |error| return Failure.new(CoercionError.new("Value for #{name} could not be coerced to #{prop[:type]}")) }
          end
        end

        Success.new(type.raw_type.new(values))
      rescue ArgumentError, RuntimeError
        Failure.new(CoercionError.new("Given hash could not be coerced to #{type}."))
      end

Allow ability to specify serialization key for fields instead of always defaulting to field name

Goal: Be able to build a struct from another struct even if the field names are different

Context: Serializing a struct currently always makes the keys the name of the fields, this makes this flow not work if you want to build one struct from another:

  1. struct1 has a field income_type
  2. you serialize your instance of struct1 to a hash
  3. you deserialize the hash to build struct2 which has a single field called type

step 3 will raise an error Typed::Validations::RequiredFieldError: type is required. because the hash doesn't have a key for type, but it has a key for `income_type.

Ideally, it'd be great to specify a target for each field. If target is specified, that's what is used for the key name instead of the field name.

When serializing Structs, use their `schema`

Conventionally, we expect a Struct to define a .schema class method for it's schema (a default is implemented on all of them via struct_ext.rb.) We should defer to this when we're doing struct serialization as well

`CoercionNotSupportedError` not easy to debug

these errors are not obvious to debug:
Failed to deserialize response: Typed::Coercion::CoercionNotSupportedError

Adding the field name, source and destination types, and what was the value that was used

Resolve T::Struct warning during test runs

While running the test suite, we see the following warnings a few times:

(eval at /Users/maxveldink/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sorbet-runtime-0.5.11284/lib/types/props/has_lazily_specialized_methods.rb:65):1: warning: method redefined; discarding old __t_props_generated_deserialize
/Users/maxveldink/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sorbet-runtime-0.5.11284/lib/types/props/has_lazily_specialized_methods.rb:91: warning: previous definition of __t_props_generated_deserialize was here

I'm not quite sure why we're redefining methods, but we should try and suppress this warning.

Figure out why RBI for `T::Struct` isn't being generated correctly

Currently, when someone pulls in sorbet-schema and rebuilds the RBIs, the T::Struct extensions aren't detected by Tapioca and added to the RBIs. Further, the deserialize_to extension method on struct returns a T::Struct in the success position, which needs to be cast by the caller. We probably need a compiler that detects structs in the project and adds custom RBIs for these.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.