Spec-Zone.ru › Ruby on Rails 7.2

модуль ActiveRecord::AttributeMethods::Serialization::ClassMethods

Публичные методы экземпляров

serialize(attr_name, coder: nil, type: Object, yaml: {}, **options) Показать исходный код
# 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API