модуль ActiveRecord::AttributeMethods::Serialization::ClassMethods
Публичные методы экземпляров
# File activerecord/lib/active_record/attribute_methods/serialization.rb, line 183
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 Если у вас есть атрибут, который нужно сохранить в базе данных как сериализованный объект, а затем извлечь, десериализовав в тот же объект, укажите имя этого атрибута с помощью этого метода, и сериализация будет обрабатываться автоматически.
Формат сериализации может быть YAML, JSON или любой пользовательский формат с помощью пользовательского класса кодера.
Обратите внимание, что адаптеры баз данных выполняют некоторые задачи сериализации за вас. Например: json и jsonb типы в PostgreSQL будут преобразовываться между синтаксисом JSON объекта/массива и Ruby Hash или Array объектами прозрачно. В этом случае не нужно использовать serialize.
Для более сложных случаев, таких как преобразование в или из объектов предметной области вашего приложения, воспользуйтесь API ActiveRecord::Attributes.
Параметры
-
attr_name- Имя атрибута для сериализации. -
coderРеализация сериализатора для использования, напримерJSON.-
Значение атрибута будет сериализовано с помощью метода кодера
dump(value), и будет десериализовано с помощью метода кодераload(string). Методdumpможет возвращатьnilдля сериализации значения какNULL.
-
-
type- Необязательно. Тип сериализованного объекта.-
Попытка сериализации другого типа вызовет ошибку
ActiveRecord::SerializationTypeMismatch. -
Если столбец
NULLили при создании новой записи, значение по умолчанию будет установлено вtype.new
-
-
yaml- Необязательно. Специфические параметры YAML. Допустимая конфигурация:-
:permitted_classes-Arrayс разрешенными классами. -
:unsafe_load- Небезопасно загрузить YAML-блоки, разрешить YAML загружать любой класс.
-
Опции
-
:default- Значение по умолчанию для использования, когда значение не предоставлено. Если эта опция не передана, будет использовано предыдущее значение по умолчанию (если таковое было). В противном случае значением по умолчанию будетnil.
Выбор сериализатора
Хотя можно использовать любой формат сериализации, рекомендуется тщательно оценить свойства сериализатора перед его использованием, так как миграция на другой формат позже может быть затруднительной.
Избегайте приема произвольных типов
При сериализации данных в столбце настоятельно рекомендуется убедиться, что будут сериализованы только ожидаемые типы. Например, некоторые сериализаторы, такие как Marshal или YAML способны сериализовать практически любой Ruby-объект.
Это может привести к сериализации неожиданных типов, и важно, чтобы сериализация типов оставалась обратно и вперёд совместимой, пока некоторые записи базы данных всё ещё содержат эти сериализованные типы.
class Address
def initialize(line, city, country)
@line, @city, @country = line, city, country
end
end
В приведенном выше примере, если любое из Address атрибутов переименовано, экземпляры, сохранённые до изменения, будут загружены со старыми атрибутами. Эта проблема ещё хуже, когда сериализованный тип происходит из зависимости, которая не ожидает быть сериализованной таким образом и может изменить своё внутреннее представление без предупреждения.
Поэтому настоятельно рекомендуется вместо этого преобразовать эти объекты в примитивы формата сериализации, например:
class Address
attr_reader :line, :city, :country
def self.load(payload)
data = YAML.safe_load(payload)
new(data["line"], data["city"], data["country"])
end
def self.dump(address)
YAML.safe_dump(
"line" => address.line,
"city" => address.city,
"country" => address.country,
)
end
def initialize(line, city, country)
@line, @city, @country = line, city, country
end
end
class User < ActiveRecord::Base
serialize :address, coder: Address
end
Этот шаблон позволяет более тщательно подходить к тому, что сериализуется, и позволяет эволюционировать формат с обратной совместимостью.
Обеспечение стабильности сериализации
Некоторые методы сериализации могут принимать типы, которые они не поддерживают, молча преобразуя их в другие типы. Это может вызвать ошибки при десериализации.
Например, сериализатор JSON, предоставляемый стандартной библиотекой, будет молчаливо преобразовывать недопустимые типы в String:
>> JSON.parse(JSON.dump(Struct.new(:foo))) => "#<Class:0x000000013090b4c0>"
Примеры
Сериализация атрибута preferences с помощью YAML
class User < ActiveRecord::Base serialize :preferences, coder: YAML end
Сериализация атрибута preferences с помощью JSON
class User < ActiveRecord::Base serialize :preferences, coder: JSON end
Сериализация preferences Hash с помощью YAML
class User < ActiveRecord::Base serialize :preferences, type: Hash, coder: YAML end
Сериализация preferences в YAML, разрешая выбор классов
class User < ActiveRecord::Base
serialize :preferences, coder: YAML, yaml: { permitted_classes: [Symbol, Time] }
end
Сериализация атрибута preferences с помощью пользовательского кодера
class Rot13JSON
def self.rot13(string)
string.tr("a-zA-Z", "n-za-mN-ZA-M")
end
# 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
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.