Spec-Zone.ru › Ruby on Rails 8.1

module ActiveRecord::AttributeMethods::Serialization::ClassMethods

Открытые методы экземпляра

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

Spec-Zone.ru

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