-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add basic JSON serializer and supporting classes
- Loading branch information
1 parent
479f285
commit 0c149d1
Showing
33 changed files
with
3,991 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# typed: strict | ||
# frozen_string_literal: true | ||
|
||
require "sorbet-runtime" | ||
require "sorbet-result" | ||
|
||
# We can't use `Loader.for_gem` here as we've unconventionally named the root file. | ||
require "zeitwerk" | ||
loader = Zeitwerk::Loader.new | ||
loader.push_dir(__dir__.to_s) | ||
loader.ignore(__FILE__) | ||
loader.inflector.inflect( | ||
"json_serializer" => "JSONSerializer" | ||
) | ||
loader.setup | ||
|
||
# Sorbet-aware namespace to super-charge your projects | ||
module Typed; end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class ApplyValidators | ||
extend T::Sig | ||
|
||
sig { params(schema: Schema).void } | ||
def initialize(schema:) | ||
@schema = schema | ||
end | ||
|
||
sig { params(params: Serializer::Params).returns(Result[Serializer::Params, ValidationError]) } | ||
def call(params) | ||
failing_results = schema.fields.map do |field| | ||
field.validate(params[field.name]) | ||
end.select(&:failure?) | ||
|
||
case failing_results.length | ||
when 0 | ||
Success.new(params) | ||
when 1 | ||
Failure.new(T.must(failing_results.first).error) | ||
else | ||
Failure.new(MultipleValidationError.new(errors: failing_results.map(&:error))) | ||
end | ||
end | ||
|
||
private | ||
|
||
sig { returns(Schema) } | ||
attr_reader :schema | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class DeserializeError < StandardError | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class Field < T::Struct | ||
extend T::Sig | ||
|
||
const :name, Symbol | ||
const :type, T::Class[T.anything] | ||
const :required, T::Boolean, default: true | ||
|
||
ValidationResult = T.type_alias { Result[T.untyped, ValidationError] } | ||
|
||
sig { returns(T::Boolean) } | ||
def required? | ||
required | ||
end | ||
|
||
sig { returns(T::Boolean) } | ||
def optional? | ||
!required | ||
end | ||
|
||
sig { params(value: T.untyped).returns(ValidationResult) } | ||
def validate(value) | ||
validate_required(value) | ||
end | ||
|
||
private | ||
|
||
sig { params(value: T.untyped).returns(ValidationResult) } | ||
def validate_required(value) | ||
if required? && value.nil? | ||
Failure.new(RequiredFieldError.new(field_name: name)) | ||
else | ||
Success.new(value) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# typed: strict | ||
|
||
require "json" | ||
|
||
module Typed | ||
class JSONSerializer < Serializer | ||
extend T::Sig | ||
|
||
sig { override.params(source: String).returns(Result[T::Struct, DeserializeError]) } | ||
def deserialize(source) | ||
parsed_json = JSON.parse(source) | ||
|
||
creation_params = schema.fields.each_with_object(T.let({}, Params)) do |field, hsh| | ||
hsh[field.name] = parsed_json[field.name.to_s] | ||
end | ||
|
||
ApplyValidators | ||
.new(schema:) | ||
.call(creation_params) | ||
.and_then do |validated_params| | ||
Success.new(schema.target.new(**validated_params)) | ||
end | ||
rescue JSON::ParserError | ||
Failure.new(ParseError.new(format: :json)) | ||
end | ||
|
||
sig { override.params(struct: T::Struct).returns(String) } | ||
def serialize(struct) | ||
JSON.generate(struct.serialize) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class MultipleValidationError < ValidationError | ||
extend T::Sig | ||
|
||
sig { params(errors: T::Array[ValidationError]).void } | ||
def initialize(errors:) | ||
combined_message = errors.map(&:message).join(" | ") | ||
|
||
super("Multiple validation errors found: #{combined_message}") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class ParseError < DeserializeError | ||
extend T::Sig | ||
|
||
sig { params(format: Symbol).void } | ||
def initialize(format:) | ||
super("#{format} could not be parsed. Check for typos.") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class RequiredFieldError < ValidationError | ||
extend T::Sig | ||
|
||
sig { params(field_name: Symbol).void } | ||
def initialize(field_name:) | ||
super("#{field_name} is required.") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class Schema < T::Struct | ||
const :fields, T::Array[Field], default: [] | ||
const :target, T.class_of(T::Struct) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class Serializer | ||
extend T::Sig | ||
extend T::Helpers | ||
abstract! | ||
|
||
Params = T.type_alias { T::Hash[Symbol, T.untyped] } | ||
|
||
sig { returns(Schema) } | ||
attr_reader :schema | ||
|
||
sig { params(schema: Schema).void } | ||
def initialize(schema:) | ||
@schema = schema | ||
end | ||
|
||
sig { abstract.params(source: String).returns(Typed::Result[T::Struct, DeserializeError]) } | ||
def deserialize(source) | ||
end | ||
|
||
sig { abstract.params(struct: T::Struct).returns(String) } | ||
def serialize(struct) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# typed: strict | ||
|
||
module Typed | ||
class ValidationError < DeserializeError | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.