module ActiveRecord::AttributeMethods::Serialization::ClassMethods

def build_column_serializer(attr_name, coder, type, yaml = nil)

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.
  coder = Coders::JSON if coder == ::JSON
  if coder == ::YAML || coder == Coders::YAMLColumn
    Coders::YAMLColumn.new(attr_name, type, **(yaml || {}))
  elsif coder.respond_to?(:new) && !coder.respond_to?(:load)
    coder.new(attr_name, type)
  elsif type && type != Object
    Coders::ColumnSerializer.new(attr_name, coder, type)
  else
    coder
  end
end

def serialize(attr_name, class_name_or_coder = nil, coder: nil, type: Object, yaml: {}, **options)


end
serialize :preferences, coder: Rot13JSON
class User < ActiveRecord::Base

end
end
ActiveSupport::JSON.load(rot13(string))
def self.load(string)
# Deserializes a string from the database to an attribute value.

end
rot13(ActiveSupport::JSON.dump(value))
def self.dump(value)
# Serializes an attribute value to a string that will be stored in the database.

end
string.tr("a-zA-Z", "n-za-mN-ZA-M")
def self.rot13(string)
class Rot13JSON

===== Serialize the +preferences+ attribute using a custom coder

end
serialize :preferences, coder: YAML, yaml: { permitted_classes: [Symbol, Time] }
class User < ActiveRecord::Base

===== Serializes +preferences+ to YAML, permitting select classes

end
serialize :preferences, type: Hash, coder: YAML
class User < ActiveRecord::Base

===== Serialize the +preferences+ +Hash+ using YAML

end
serialize :preferences, coder: JSON
class User < ActiveRecord::Base

===== Serialize the +preferences+ attribute using JSON

end
serialize :preferences, coder: YAML
class User < ActiveRecord::Base

===== Serialize the +preferences+ attribute using YAML

==== Examples

=> "#"
>> JSON.parse(JSON.dump(Struct.new(:foo)))

silently cast unsupported types to +String+:
For instance the +JSON+ serializer provided in the standard library will

data is deserialized.
silently casting them to other types. This can cause bugs when the
Some serialization methods may accept some types they don't support by

===== Ensure serialization stability

to evolve the format in a backward compatible way.
This pattern allows to be more deliberate about what is serialized, and

end
serialize :address, coder: Address
class User < ActiveRecord::Base

end
end
@line, @city, @country = line, city, country
def initialize(line, city, country)

end
)
"country" => address.country,
"city" => address.city,
"line" => address.line,
YAML.safe_dump(
def self.dump(address)

end
new(data["line"], data["city"], data["country"])
data = YAML.safe_load(payload)
def self.load(payload)

attr_reader :line, :city, :country
class Address

primitives of the serialization format, for example:
As such, it is heavily recommended to instead convert these objects into

change its internal representation without notice.
from a dependency which doesn't expect to be serialized this way and may
old attributes. This problem is even worse when the serialized type comes
instances that were persisted before the change will be loaded with the
In the above example, if any of the +Address+ attributes is renamed,

end
end
@line, @city, @country = line, city, country
def initialize(line, city, country)
class Address

as some database records still contain these serialized types.
that type serialization remains backward and forward compatible as long
This can lead to unexpected types being serialized, and it is important

+Marshal+ or +YAML+ are capable of serializing almost any Ruby object.
only expected types will be serialized. For instance some serializer like
When serializing data in a column, it is heavily recommended to make sure

===== Avoid accepting arbitrary types

another format later on can be difficult.
evaluate the properties of a serializer before using it, as migrating to
While any serialization format can be used, it is recommended to carefully

==== Choosing a serializer

be used. Otherwise, the default will be +nil+.
this option is not passed, the previous default value (if any) will
* +:default+ - The default value to use when no value is provided. If

==== Options

* +:unsafe_load+ - Unsafely load YAML blobs, allow YAML to load any class.
* +:permitted_classes+ - +Array+ with the permitted classes.
* +yaml+ - Optional. Yaml specific options. The allowed config is:
will set to +type.new+
* If the column is +NULL+ or starting from a new record, the default value
ActiveRecord::SerializationTypeMismatch error.
* Attempting to serialize another type will raise an
* +type+ - Optional. What the type of the serialized object should be.
+dump+ method may return +nil+ to serialize the value as +NULL+.
deserialized using the coder's load(string) method. The
using the coder's dump(value) method, and will be
* The attribute value will be serialized
* +coder+ The serializer implementation to use, e.g. +JSON+.
* +attr_name+ - The name of the attribute to serialize.

==== Parameters

domain objects, consider using the ActiveRecord::Attributes API.
For more complex cases, such as conversion to or from your application

case.
objects transparently. There is no need to use #serialize in this
converted between JSON object/array syntax and Ruby +Hash+ or +Array+
for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
Keep in mind that database adapters handle certain serialization tasks

custom coder class.
The serialization format may be YAML, JSON, or any custom format using a

will be handled automatically.
then specify the name of that attribute using this method and serialization
serialized object, and retrieved by deserializing into the same object,
If you have an attribute that needs to be saved to the database as a
def serialize(attr_name, class_name_or_coder = nil, coder: nil, type: Object, yaml: {}, **options)
  unless class_name_or_coder.nil?
    if class_name_or_coder == ::JSON || [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
      ActiveRecord.deprecator.warn(<<~MSG)
        Passing the coder as positional argument is deprecated and will be removed in Rails 7.2.
        Please pass the coder as a keyword argument:
          serialize #{attr_name.inspect}, coder: #{class_name_or_coder}
      MSG
      coder = class_name_or_coder
    else
      ActiveRecord.deprecator.warn(<<~MSG)
        Passing the class as positional argument is deprecated and will be removed in Rails 7.2.
        Please pass the class as a keyword argument:
          serialize #{attr_name.inspect}, type: #{class_name_or_coder.name}
      MSG
      type = class_name_or_coder
    end
  end
  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) do |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)
  end
end

def type_incompatible_with_serialize?(cast_type, coder, type)

def type_incompatible_with_serialize?(cast_type, coder, type)
  cast_type.is_a?(ActiveRecord::Type::Json) && coder == ::JSON ||
    cast_type.respond_to?(:type_cast_array, true) && type == ::Array
end