Why ActiveModel does deserialize(serialize(value))?

Hello!

I just read the following and struggle to understand why we do this: deserialize(serialize(value))

https://github.com/rails/rails/blob/main/activemodel/lib/active_model/type/helpers/mutable.rb#L8

I guess it’s to be sure that the value can be serializeed and deserialized without errors. Is that why? Are there other reasons?

Also, would it be tolerable to skip this back and forth operation when it’s costly?

Thanks for your help, Younes

I’m not sure but this is private API for what its worth.

This was a good find, as I was actually thinking about the AM::Attributes API recently.

What do you think a good interface for this would be? What are the downfalls of the current implementation?

I’m working on a PR for this issue: https://github.com/rails/rails/issues/42388

The problem with the current implementation is that when using an external key management service, we currently end up with 3 calls to #encryption_key instead of, ideally, one.

With the following patch I have 2 calls instead of 3 which is an okay-enough optimization IMO but as I’m rather new to rails contribution, I prefer making sure I’m not breaking something or creating an undesirable side-effect.

diff --git a/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb b/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb
index 7cdb4b18bb..1ffc5ab7bc 100644
--- a/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb
+++ b/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb
@@ -26,6 +26,10 @@ def initialize(scheme:, cast_type: ActiveModel::Type::String.new, previous_type:
         @previous_type = previous_type
       end
 
+      def cast(value)
+        value
+      end
+
       def deserialize(value)
         cast_type.deserialize decrypt(value)
       end

@Younes_Serraj I think you should just open a patch, since that feature is so new and the author seems to be very up for making adjustments :pray:

cc @jorgemanrubia

A serialize-then-deserialize looks sloppy but depending on the performance ramifications, it can be a common method for making the input data structure more uniform or predictable. Deserialization is not truly the reverse process of serialization in all cases.

This may be an inaccurate or contrived example but I might use this trick to get all keys in a hash to be string keys or something like that.