Rails forms: Reform vs Model Inheritance
Сравнение
| Подход | Пример |
|---|---|
| Reform с собственной структурой (composition) | class ListingForm < Reform::Form |
Форма-наследник модели (напрямую от Listing) | class ListingForm < Listing |
Reform (Reform::Form + Contract)
class ListingForm < Reform::Form
property :address
property :city
property :state
validation do
required(:address).filled
required(:city).filled
required(:state).filled
end
end
Полностью независимая форма, которая:
- валидирует без связи с ActiveRecord
- управляет структурой полей
- может сохранять в модель через
sync+model.save - может объединять несколько моделей
Наследование от модели
class ListingForm < Listing
attr_accessor :some_virtual_param
validates :address, presence: true
validates :some_virtual_param, numericality: true
end
Работает как патч модели: добавляются поля и валидации прямо на модель + немного виртуальных полей.
Сравнение Reform vs наследование от модели
| Критерий | Reform (< Reform::Form) | Наследование от модели |
|---|---|---|
| 🧱 Изоляция формы | ✅ Полная (никак не трогает модель) | ❌ Сильно зависит от модели |
| 🎯 Валидирует только input | ✅ Да, это и цель | ❌ Наследует бизнес-валидации |
| 🧩 Поддержка вложенных моделей | ✅ Через composition, collection, nested | ❌ Очень сложно, часто невозможна |
| 🔁 Использует модель | Через model (явно) | Модель — это и есть форма |
| 🧪 Тестируемость | ✅ Отличная | ❌ Путается с AR |
| 💥 Усложнение модели | ❌ Нет | ⚠️ Модель становится перегруженной |
| 📚 Документация и поддержка | 👍 Хорошая (Reform, Trailblazer) | 🟡 Стандартный Rails, но анти-паттерн |
| 🧰 Валидации | Dry-validation или Reform DSL | ActiveRecord validation |
| 🧬 Методы сохранения | sync, save, validate, prepopulate | save/update напрямую |
Минусы Form < Model
- Всё завязано на БД — даже если тебе нужны просто временные данные
- Сложно тестировать — приходится поднимать БД
- Колбэки и ассоциации модели могут мешать логике формы
- Переиспользование ограничено — нельзя собрать из нескольких моделей
Когда Reform — победитель
- У тебя форма, не совпадающая 1:1 с моделью
- Нужно объединить несколько сущностей (напр.
Listing + Address + Owner) - Хочешь использовать dry-validation или строгую структуру
- Строишь API и хочешь единый интерфейс валидации
- Хочешь безболезненно тестировать формы без ActiveRecord
Итог
| Выбор | Используй когда |
|---|---|
Form < Reform::Form | ✅ Универсальное, масштабируемое, чистое решение |
Form < Model | 🔧 Подходит ТОЛЬКО для очень простых UI-форм, но лучше избегать |