diff --git a/lib/typed/coercion/boolean_coercer.rb b/lib/typed/coercion/boolean_coercer.rb new file mode 100644 index 0000000..08f0b74 --- /dev/null +++ b/lib/typed/coercion/boolean_coercer.rb @@ -0,0 +1,31 @@ +# typed: strict + +module Typed + module Coercion + class BooleanCoercer < Coercer + extend T::Generic + + Target = type_member { {fixed: T::Boolean} } + + sig { override.params(type: Field::Type).returns(T::Boolean) } + def used_for_type?(type) + type == T::Utils.coerce(T::Boolean) + end + + sig { override.params(field: Field, value: Value).returns(Result[Target, CoercionError]) } + def coerce(field:, value:) + if T.cast(field.type, T::Types::Base).valid?(value) + Success.new(value) + elsif value == "true" + Success.new(true) + elsif value == "false" + Success.new(false) + else + Failure.new(CoercionError.new) + end + rescue TypeError => e + Failure.new(CoercionError.new("Field type must be a T::Boolean.")) + end + end + end +end diff --git a/lib/typed/coercion/coercer_registry.rb b/lib/typed/coercion/coercer_registry.rb index 569702f..47a086d 100644 --- a/lib/typed/coercion/coercer_registry.rb +++ b/lib/typed/coercion/coercer_registry.rb @@ -11,7 +11,7 @@ class CoercerRegistry Registry = T.type_alias { T::Array[T.class_of(Coercer)] } - DEFAULT_COERCERS = T.let([StringCoercer, IntegerCoercer, FloatCoercer, EnumCoercer, StructCoercer], Registry) + DEFAULT_COERCERS = T.let([StringCoercer, BooleanCoercer, IntegerCoercer, FloatCoercer, EnumCoercer, StructCoercer], Registry) sig { void } def initialize diff --git a/test/typed/coercion/boolean_coercer_test.rb b/test/typed/coercion/boolean_coercer_test.rb new file mode 100644 index 0000000..8d661fd --- /dev/null +++ b/test/typed/coercion/boolean_coercer_test.rb @@ -0,0 +1,41 @@ +# typed: true + +class BooleanCoercerTest < Minitest::Test + def setup + @coercer = Typed::Coercion::BooleanCoercer.new + @field = Typed::Field.new(name: :capital, type: T::Utils.coerce(T::Boolean)) + end + + def test_used_for_type_works + assert(@coercer.used_for_type?(T::Utils.coerce(T::Boolean))) + refute(@coercer.used_for_type?(Integer)) + end + + def test_when_boolean_field_given_returns_failure + result = @coercer.coerce(field: Typed::Field.new(name: :testing, type: Integer), value: "testing") + + assert_failure(result) + assert_error(Typed::Coercion::CoercionError.new("Field type must be a T::Boolean."), result) + end + + def test_when_true_boolean_can_be_coerced_returns_success + result = @coercer.coerce(field: @field, value: "true") + + assert_success(result) + assert_payload(true, result) + end + + def test_when_false_boolean_can_be_coerced_returns_success + result = @coercer.coerce(field: @field, value: "false") + + assert_success(result) + assert_payload(false, result) + end + + def test_when_enum_cannot_be_coerced_returns_failure + result = @coercer.coerce(field: @field, value: "bad") + + assert_failure(result) + assert_error(Typed::Coercion::CoercionError.new, result) + end +end