Ruby on RailsMiddleCoding
Что такое метод serialize в ActiveRecord и когда его следует избегать?
serialize сохраняет объект Ruby (Hash, Array) в текстовое поле БД как YAML/JSON. Избегайте его, когда нужна фильтрация, индексация или типобезопасность — используйте jsonb-колонку или отдельную таблицу.
serialize в ActiveRecord
Что это и как работает
serialize позволяет хранить произвольные Ruby-объекты (Hash, Array, custom class) в текстовой или бинарной колонке БД. При чтении ActiveRecord автоматически десериализует значение обратно в Ruby-объект.
# Старый способ (Rails < 7.1) — YAML по умолчанию
class UserProfile < ApplicationRecord
serialize :preferences, Hash
serialize :tags, Array
end
# Запись
profile = UserProfile.create(
preferences: { theme: "dark", notifications: true },
tags: ["ruby", "rails"]
)
# Чтение — автоматически десериализуется
profile.preferences[:theme] # => "dark"
Rails 7.1+: store_as и новый API
# Rails 7.1 ввёл typed attributes через attribute API
class EventLog < ApplicationRecord
serialize :metadata, coder: JSON # явно JSON вместо YAML
end
# Ещё лучше — store для именованных ключей
class User < ApplicationRecord
store :settings, accessors: %i[color_scheme locale], coder: JSON
end
user = User.new
user.color_scheme = "dark"
user.locale = "en"
user.settings # => { "color_scheme" => "dark", "locale" => "en" }
Лучшая альтернатива — PostgreSQL jsonb
# migration
class AddMetadataToProducts < ActiveRecord::Migration[7.1]
def change
add_column :products, :metadata, :jsonb, null: false, default: {}
add_index :products, :metadata, using: :gin
end
end
# Модель — никакого serialize не нужно
class Product < ApplicationRecord
# jsonb работает нативно
end
# Запрос по содержимому jsonb
Product.where("metadata @> ?", { category: "electronics" }.to_json)
Product.where("metadata->>? = ?", "color", "red")
Когда serialize допустим
- Прототипирование или хранение опциональных настроек, по которым никогда не будет фильтрации.
- Миграция легаси-кода, где нет возможности изменить схему БД.
- Нереляционные данные с непредсказуемой структурой (логи, события).
Когда избегать serialize
- Нужна фильтрация по вложенным полям (
WHERE preferences->>'theme' = 'dark'не работает на text-колонке). - Нужен индекс на поле внутри объекта.
- Сериализованные данные меняются независимо (нормализуйте в отдельную таблицу).
Подводные камни
- YAML-сериализация по умолчанию небезопасна для данных из ненадёжных источников — старые версии Rails содержали RCE-уязвимости через YAML.load.
- Dirty tracking не работает корректно для вложенных мутаций:
profile.preferences[:theme] = "light"не помечаетpreferencesкак changed — нужно использоватьprofile.preferences_will_change!. - Тип данных проверяется при записи, но не при чтении легаси-данных с неверным типом — десериализация падает молча или возвращает строку.
- Поиск и сортировка по полю внутри YAML/text-сериализации невозможны без LIKE, что неэффективно и ненадёжно.
- Миграция между форматами (YAML → JSON) требует ручного rake task для конвертации всех строк.
- serialize с классом (
serialize :data, MyClass) привязывает схему БД к конкретному Ruby-классу — переименование класса ломает десериализацию. - Использование
storeбезcoder: JSONхранит данные в YAML — проблемы при использовании из других языков или инструментов. - jsonb-колонки в PostgreSQL поддерживают GIN-индексы и операторы
@>,?— serialize на text-колонке лишён всех этих возможностей.
Common mistakes
- Сводить serialize method к названию метода без lifecycle и failure path.
- Игнорировать модель runtime: Rails 8.1 строит приложение вокруг Rack, routing, controllers, Active Record, views и conventions over configuration.
- Не отделять validation, authorization, transaction boundary и business logic.
What the interviewer is testing
- Объясняет serialize method через конкретную точку lifecycle в Ruby on Rails.
- Приводит корректный минимальный пример без вымышленных методов или callbacks.
- Называет edge cases: пустые значения, ошибки, транзакции, безопасность или concurrency.