Just in time for Christmas, Reform 2.1 is ready for you. It has two great new additions: we now support the awesome Piotr Solnica’s dry-validation gem, and I introduced validation groups.
Traditional validation gems like ActiveModel::Validations only allow a linear flow of validations. All defined validations will be run, even though in specific cases it doesn’t make sense. We get around that limitation now in Reform with validation groups.
Here’s a very simplified example.
class SessionForm < Reform::Form property :username property :email validation :default do validates :username, :email, presence: true end validation :email_format, if: :default do validates :email, email: true end end
You can now group sets of validation and name them using
Those can then be chained using
:after and, in this example,
:if. The second group
:email_format is only executed if the
:default group was valid, saving you any conditionals in the following validations.
Validation still happens by calling
This opens the way to a completely new understanding of validations as predicates and results.
Speaking of predicates and all those logic terms: We now support Dry-validation as another validation backend. Since this is a relatively new, fast and very strict implementation, we will use it as default in future Reforms.
class LoginForm < Reform::Form property :password property :confirm_password validation :default do key(:password, &:filled?) # :password required. key(:confirm_password) do |str| str.filled? & str.correct? end def correct?(str) str == form.password end end
Without going into dry-validation’s API details too much: In a validation group you can use the exact same API as in a
Dry::Validation::Schema, with chaining, predicates, custom validations, and so on.
Error messages in dry-validation are generated via YAML file that can easily be extended, ending the age of ActiveModel’s translation logic madness.
In Reform 2.1, all populators now receive one options hash, which allows using Ruby’s keyword arguments.
class SessionForm (fragment:, **) do self.user = fragment["id"] ? User.find(1) : User.new end, # ..
The old API still works, but is deprecated.
If you ever had the need to make Reform suppress deserialization of a fragment, this is simpler now with the new
property :user, populator: ->(fragment:, **) do return skip! if fragment["id"] # more code end, # ..
What used to be a combination of
:skip_if can now be combined. Once
skip! has returned, Reform will ignore the currently processed fragment, as if it hadn’t been in the incoming hash at all.
Documentation for skip and populators is here.
Good Bye, ActiveModel!
What sounds bewildering to many of you is a consequent step in tidying up the Ruby world: We will drop support for ActiveModel::Validations in Reform 2.2. Don’t you worry, everything will still work the way it did before, we just don’t want to waste time with AM:V and its prehistoric implementation anymore.
Most trouble we had with the way AM:V computes error messages. It gets worse when those have to be translated. AM:V has an extremely complex implementation, jumps between instance and class context, and makes wild assumptions about object interfaces. Since Rails core seems uninterested in changing anything, because it might break Basecamp, for us it’s easiest to just let it be and move on with an alternative.
Also, when using validators like
acceptance values in the form suddenly were changed because those implementations write to the validated object – a very wrong thing to do. You might also have a look into how AM:V finds validators: a cryptic, magic class traversal happens here and it is a nightmare to make AM:V use custom validators in a non-Rails environment.
We ended up with too many patches and hacks – very frustrating for the maintainers. Since there’s better, less constraining alternatives, we all will benefit from a better validation workflow.