diff --git a/lib/typed/coercion/coercer_registry.rb b/lib/typed/coercion/coercer_registry.rb index 91f8985..465bf6b 100644 --- a/lib/typed/coercion/coercer_registry.rb +++ b/lib/typed/coercion/coercer_registry.rb @@ -17,6 +17,7 @@ class CoercerRegistry BooleanCoercer, IntegerCoercer, FloatCoercer, + DateCoercer, EnumCoercer, StructCoercer, TypedArrayCoercer diff --git a/lib/typed/coercion/date_coercer.rb b/lib/typed/coercion/date_coercer.rb new file mode 100644 index 0000000..901c18e --- /dev/null +++ b/lib/typed/coercion/date_coercer.rb @@ -0,0 +1,29 @@ +# typed: strict + +require "date" + +module Typed + module Coercion + class DateCoercer < Coercer + extend T::Generic + + Target = type_member { {fixed: Date} } + + sig { override.params(type: T::Types::Base).returns(T::Boolean) } + def used_for_type?(type) + T::Utils.coerce(type) == T::Utils.coerce(Date) + end + + sig { override.params(type: T::Types::Base, value: Value).returns(Result[Target, CoercionError]) } + def coerce(type:, value:) + return Failure.new(CoercionError.new("Type must be a Date.")) unless used_for_type?(type) + + return Success.new(value) if value.is_a?(Date) + + Success.new(Date.parse(value)) + rescue Date::Error, TypeError + Failure.new(CoercionError.new("'#{value}' cannot be coerced into Date.")) + end + end + end +end diff --git a/test/typed/coercion/coercer_registry_test.rb b/test/typed/coercion/coercer_registry_test.rb index 33cce4d..7214aa4 100644 --- a/test/typed/coercion/coercer_registry_test.rb +++ b/test/typed/coercion/coercer_registry_test.rb @@ -14,6 +14,6 @@ def test_register_prepends_coercer_so_it_overrides_built_in_ones end def test_when_type_doesnt_match_coercer_returns_nil - assert_nil(Typed::Coercion::CoercerRegistry.instance.select_coercer_by(type: T::Utils.coerce(Date))) + assert_nil(Typed::Coercion::CoercerRegistry.instance.select_coercer_by(type: T::Utils.coerce(BigDecimal))) end end diff --git a/test/typed/coercion/date_coercer_test.rb b/test/typed/coercion/date_coercer_test.rb new file mode 100644 index 0000000..8bb5976 --- /dev/null +++ b/test/typed/coercion/date_coercer_test.rb @@ -0,0 +1,31 @@ +# typed: true + +class DateCoercerTest < Minitest::Test + def setup + @coercer = Typed::Coercion::DateCoercer.new + @type = T::Utils.coerce(Date) + end + + def test_used_for_type_works + assert(@coercer.used_for_type?(@type)) + refute(@coercer.used_for_type?(T::Utils.coerce(Integer))) + end + + def test_when_non_date_type_given_returns_failure + result = @coercer.coerce(type: T::Utils.coerce(Integer), value: Date.new(2024, 1, 1)) + + assert_failure(result) + assert_error(Typed::Coercion::CoercionError.new("Type must be a Date."), result) + end + + def test_when_coercable_returns_success + assert_payload(Date.new(2024, 1, 1), @coercer.coerce(type: @type, value: "20240101")) + assert_payload(Date.new(2024, 5, 1), @coercer.coerce(type: @type, value: "1/5/2024")) + assert_payload(Date.new(2024, 5, 1), @coercer.coerce(type: @type, value: Date.new(2024, 5, 1))) + end + + def test_when_not_coercable_returns_failure + assert_error(Typed::Coercion::CoercionError.new("'a' cannot be coerced into Date."), @coercer.coerce(type: @type, value: "a")) + assert_error(Typed::Coercion::CoercionError.new("'1' cannot be coerced into Date."), @coercer.coerce(type: @type, value: 1)) + end +end diff --git a/test/typed/coercion_test.rb b/test/typed/coercion_test.rb index c07089c..ee2db17 100644 --- a/test/typed/coercion_test.rb +++ b/test/typed/coercion_test.rb @@ -1,6 +1,6 @@ # typed: true -require "date" +require "bigdecimal" class CoercionTest < Minitest::Test def teardown @@ -21,7 +21,7 @@ def test_when_coercer_is_matched_coerce_coerces end def test_when_coercer_isnt_matched_coerce_returns_failure - result = Typed::Coercion.coerce(type: T::Utils.coerce(Date), value: "testing") + result = Typed::Coercion.coerce(type: T::Utils.coerce(BigDecimal), value: "testing") assert_failure(result) assert_error(Typed::Coercion::CoercionNotSupportedError.new, result)