:one_of constraint for type string

gvanders
2023-02-24

gvanders:

There exists :one_of constraint for type :atoms on the resources, but this does not exist for type :string. Is there another way to restrict values to one of a list of strings?

ZachDaniel:

There are a few other ways, yes 🙂

ZachDaniel:

You can add a validation like so:

validations do
  validate one_of(:attribute, [foo, bar])
end

gvanders:

Thanks! With constraints I’m able to access the list (say for Option generation for select component) with Ash.Resource.Info.attribute(resource, :options).constraints[:one_of]

To do the same thing I guess I need to write a function which pulls it from Ash.Resource.Info.validations ?

ZachDaniel:

hmm…yeah that’s probably the only way at the moment

ZachDaniel:

what do you use that info for, out of curiosity?

ZachDaniel:

We could potentially add support for one_of on all types

gvanders:

Mainly for forms and building values of selects. For the form on the UI I can grab the list to generate the options and keep those two in sync

gvanders:

That would be great! Because from my understanding doing operations with changesets (Changeset.change_attributes), the constraints correctly limit it, but with validations it only kicks in once acted upon?

ZachDaniel:

You’ll get errors for all of them on validation

ZachDaniel:

I’ll have to think about it because there are some implementation considerations.

gvanders:

gvanders:

Errors with constraints:

gvanders:

Valid with validations

ZachDaniel:

yeah, but when you run the changeset action it will give you the errors, which is what happens on AshPhoenix.Form.validate/2 for example

ZachDaniel:

what I would suggest: write a custom type called YourApp.StringEnum that has a one_of constraint 😄

gvanders:

I’m gonna have a lot of them 😅 . As far as implementation, I just copied atom implementation over which works. If there are larger architecture decisions that I’d understand I’d be happy to make a PR with those too

ZachDaniel:

That looks right to me 🙂 I’m not convinced that we necessarily want to add the constraint to all types yet, so for now I think the custom type is the right answer. I’ll let the idea percolate though