Spec-Zone.ru › Ruby on Rails 7.1

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

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

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

Spec-Zone.ru

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