From bc24d2553cf1fc4913264b9188375f7dc2601b37 Mon Sep 17 00:00:00 2001 From: Thomas Marshall Date: Sun, 19 May 2024 10:35:25 +0100 Subject: [PATCH] Allow explicit nil to be serialized by enums This commit uses an `UNSET` constant as the default `serialized_val` on `T::Enum`. This allows the runtime to understand the difference between an explicit `nil` and an unset default when serializing enum values. Sorbet already understands statically that a serialized value can be of type `NilClass`, but the runtime was serializing as a string representation based on the constant name instead. --- gems/sorbet-runtime/lib/types/enum.rb | 7 +++++-- gems/sorbet-runtime/test/types/enum.rb | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/gems/sorbet-runtime/lib/types/enum.rb b/gems/sorbet-runtime/lib/types/enum.rb index b575cc2a869..38eea14f5a5 100644 --- a/gems/sorbet-runtime/lib/types/enum.rb +++ b/gems/sorbet-runtime/lib/types/enum.rb @@ -273,8 +273,11 @@ def ===(other) ### Private implementation ### + UNSET = T.let(Module.new.freeze, Module) + private_constant :UNSET + sig {params(serialized_val: SerializedVal).void} - def initialize(serialized_val=nil) + def initialize(serialized_val=UNSET) raise 'T::Enum is abstract' if self.class == T::Enum if !self.class.started_initializing? raise "Must instantiate all enum values of #{self.class} inside 'enums do'." @@ -300,7 +303,7 @@ def initialize(serialized_val=nil) sig {params(const_name: Symbol).void} def _bind_name(const_name) @const_name = const_name - @serialized_val = const_to_serialized_val(const_name) if @serialized_val.nil? + @serialized_val = const_to_serialized_val(const_name) if @serialized_val.equal?(UNSET) freeze end diff --git a/gems/sorbet-runtime/test/types/enum.rb b/gems/sorbet-runtime/test/types/enum.rb index a3dda4474b5..b4ea033db52 100644 --- a/gems/sorbet-runtime/test/types/enum.rb +++ b/gems/sorbet-runtime/test/types/enum.rb @@ -19,6 +19,7 @@ class CardSuitCustom < T::Enum SPADE = new('_spade_') DIAMOND = new('_diamond_') HEART = new('_heart_') + NONE = new(nil) end end @@ -38,6 +39,7 @@ class CardSuitCustom < T::Enum assert_equal('_spade_', CardSuitCustom::SPADE.serialize) assert_equal('_diamond_', CardSuitCustom::DIAMOND.serialize) assert_equal('_heart_', CardSuitCustom::HEART.serialize) + assert_equal(nil, CardSuitCustom::NONE.serialize) end end end @@ -361,10 +363,11 @@ class CustomSerializationEnum < T::Enum enums do new('foo') new + new(nil) end end end - assert_equal('Enum values must be assigned to constants: ["foo", nil]', ex.message) + assert_equal('Enum values must be assigned to constants: ["foo", T::Enum::UNSET, nil]', ex.message) end end