Hi to all. Sorry for short question, it may seem frivolous to you. But i’ve really googled it and didn’t find any confident response or example.
Can I replace ActiveModel with dry-schema, dry-struct, dry-validation in rails constructions with form_for, form_with? Does they suggest such use? Are there any pitfalls?
Anyway I’ll try to dig deeper while waiting an answer
Thank you
Sure, it’s certainly possible to do this. I’ve done it plenty of times with plain Ruby classes, so dry-struct would just be a shortcut for attribute definition and type-checking.
It’s important to understand the conceptual differences between ActiveModel and Dry-Struct. The Dry system is intended to work in a functional way, with immutable structs. The sort of mutation you typically do with activemodel objects is not supported, by design.
That means validation is an entirely separate step that takes place before instantiating the struct objects. You’ll need to stitch the two processes together in some way, which is easy with dry-monads. See also dry-rails for how you would integrate dry-schema into a Rails controller.
If you’re unfamiliar with all of this, I would recommend taking a piece at a time and solving a single problem with it. It will become clear over time how they work together, but in my experience trying to use it all at once is too hard.
The minimum-viable form object would look something like this:
module T
include Dry::Types(default: :strict)
Username = Strict::String.constrained(min_size: 3)
end
class Form < Dry::Struct
extend ActiveModel::Naming
include ActiveModel::AttributeMethods
attribute :username, T::Username
def persisted?
false
end
end
I am bugged by the same question and wonder of the lack of documentation or blog posts in this regard, as I would expect this to be a quite common problem.
@woto, how did you solve it in the end? Was this enough or are there any learnings to share from your side?
rom-rb has the notion of changesets, which are designed for the use case of validation, I wonder if something like Hanami has a reasonable alternative to form-for that integrates more natively with changesets.
My understanding is that a changset is a data structure that assists in the update of immutable data objects (like structs) by providing a mapping between new and old values. At least in the case of Ecto, invalid updates and their reason for being invalid are included in the changeset.
The implementation of Changeset is just combining the output of Transproc with a Command, so it deals with transformation and persistence. Validation would happen before the Changeset, using dry-validation.
Perhaps you could integrate a validation contract in a Changeset, but it is not obvious to me how you would do that, because Changeset doesn’t use Result types consistently.