zato
Posted on November 18, 2020
Say, you have a model with status field:
class Order < ApplicationRecord
enum status: [
:cart,
:requested,
:confirmed,
:complete,
:canceled
], _prefix: true
In some case, you want to put some constraints on status changes. It can change from cart
to requested
, but can't change from requested
to cart
, for example.
You can use state machine gems, such as aasm to write validation.
But the gem introduces new DSL to write state transition. It's too much just for validation.
Solution
It can be written with basic ActiveRecord validation this way:
validates :status,
inclusion: { in: %w[requested canceled] },
if: -> { status_changed? && status_was == 'cart' }
If you like to write from -> to
direction, just use hash:
class Order < ApplicationRecord
# ...
{
'cart' => %w[requested canceled],
'requested' => %w[confirmed complete canceled],
'confirmed' => %w[complete canceled],
'complete' => %w[],
}.each do |status_from, status_to|
validates :status,
inclusion: {
in: status_to,
message: " cannot change from '#{status_from}' to '%{value}'"
},
if: -> { persisted? && status_changed? && status_was == status_from }
end
A few notes:
- Use
if: -> { persisted? ...
to avoid validation when creating a model instance in tests - Set customized message because default ones are not friendly
💖 💪 🙅 🚩
zato
Posted on November 18, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.