Repository: rails/rails
File: activerecord/lib/active_record/attribute_methods/serialization.rb (Ruby)

Code before:
        #     # Serializes an attribute value to a string that will be stored in the database.
        #     def self.dump(value)
        #       rot13(ActiveSupport::JSON.dump(value))
        #     end
        #
        #     # Deserializes a string from the database to an attribute value.
        #     def self.load(string)
        #       ActiveSupport::JSON.load(rot13(string))
        #     end
        #   end
        #
        #   class User < ActiveRecord::Base
        #     serialize :preferences, coder: Rot13JSON
        #   end
        #
        def serialize(attr_name, coder: nil, type: Object, comparable: false, yaml: {}, json: {}, **options)
          coder ||= default_column_serializer
          unless coder
            raise ArgumentError, <<~MSG.squish
              missing keyword: :coder

              If no default coder is configured, a coder must be provided to `serialize`.
            MSG
          end

          column_serializer = build_column_serializer(attr_name, coder, type, { yaml: yaml, json: json })

          attribute(attr_name, **options)

          decorate_attributes([attr_name]) do |attr_name, cast_type|
            if type_incompatible_with_serialize?(cast_type, coder, type)
              raise ColumnNotSerializableError.new(attr_name, cast_type)
            end

            cast_type = cast_type.subtype if Type::Serialized === cast_type
            Type::Serialized.new(cast_type, column_serializer, comparable: comparable)
          end
        end

        private
          def build_column_serializer(attr_name, coder, type, options = {})
            # When ::JSON is used, force it to go through the Active Support JSON encoder
            # to ensure special objects (e.g. Active Record models) are dumped correctly
            # using the #as_json hook.
            coder = Coders::JSON if coder == ::JSON

            if coder == ::YAML || coder == Coders::YAMLColumn
              Coders::YAMLColumn.new(attr_name, type, **(options[:yaml] || {}))
            elsif coder == Coders::JSON
              Coders::JSON.new(**(options[:json] || {}))
            elsif coder.respond_to?(:new) && !coder.respond_to?(:load)

Review comment (suggestion):
Could these be passed as keyword arguments instead of a `Hash`?

```suggestion
          column_serializer = build_column_serializer(attr_name, coder, type, yaml: yaml, json: json)
```

Code after:
        #     # Serializes an attribute value to a string that will be stored in the database.
        #     def self.dump(value)
        #       rot13(ActiveSupport::JSON.dump(value))
        #     end
        #
        #     # Deserializes a string from the database to an attribute value.
        #     def self.load(string)
        #       ActiveSupport::JSON.load(rot13(string))
        #     end
        #   end
        #
        #   class User < ActiveRecord::Base
        #     serialize :preferences, coder: Rot13JSON
        #   end
        #
        def serialize(attr_name, coder: nil, type: Object, comparable: false, yaml: {}, **options)
          coder ||= default_column_serializer
          unless coder
            raise ArgumentError, <<~MSG.squish
              missing keyword: :coder

              If no default coder is configured, a coder must be provided to `serialize`.
            MSG
          end

          column_serializer = build_column_serializer(attr_name, coder, type, yaml)

          attribute(attr_name, **options)

          decorate_attributes([attr_name]) do |attr_name, cast_type|
            if type_incompatible_with_serialize?(cast_type, coder, type)
              raise ColumnNotSerializableError.new(attr_name, cast_type)
            end

            cast_type = cast_type.subtype if Type::Serialized === cast_type
            Type::Serialized.new(cast_type, column_serializer, comparable: comparable)
          end
        end

        private
          def build_column_serializer(attr_name, coder, type, yaml = nil)
            # When ::JSON is used, force it to go through the Active Support JSON encoder
            # to ensure special objects (e.g. Active Record models) are dumped correctly
            # using the #as_json hook.

            if coder == ::YAML || coder == Coders::YAMLColumn
              Coders::YAMLColumn.new(attr_name, type, **(yaml || {}))
            elsif coder == ::JSON || coder == Coders::JSON
              Coders::JSON.new
            elsif coder.respond_to?(:new) && !coder.respond_to?(:load)
              coder.new(attr_name, type)