модуль ActiveRecord::AttributeMethods::Serialization::ClassMethods
Открытые методы экземпляров
# File activerecord/lib/active_record/attribute_methods/serialization.rb, line 183
def serialize(attr_name, coder: nil, type: Object, 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)
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.