module ActiveRecord::AttributeMethods::Serialization::ClassMethods
Открытые методы экземпляра
# File activerecord/lib/active_record/attribute_methods/serialization.rb, line 193
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 Если у вас есть атрибут, который необходимо сохранить в базе данных как сериализованный объект, а затем получить, десериализовав его в тот же объект, укажите имя этого атрибута с помощью данного метода — сериализация будет выполняться автоматически.
Форматом сериализации может быть 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
-
-
comparable— указывает, можно ли безопасно сравнивать десериализованный объект для обнаружения изменений. По умолчанию —false. Если установлено значениеfalse, старое и новое значения будут сравниваться по их сериализованному представлению (например, JSON или YAML), из-за чего иногда два семантически равных объекта могут считаться разными. Например, два хеша с одинаковыми ключами и значениями, но в разном порядке, имеют разные сериализованные представления, однако после десериализации семантически равны. Если установлено значениеtrue, сравнение будет выполняться для десериализованного объекта. Этот параметр следует включать только в том случае, если известно, что уtypeесть корректный метод==, который глубоко сравнивает объекты. -
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.