Using attributes in built-in validations

The built-in validations are amazing and work great with input args.

I’ve been trying to get them to validate against current attributes but can’t seem to get them to work. Currently using a manual change function but would be great to just use one of the built-ins.

For context this is what I’m trying to achieve:

      validate numericality(:total_redemptions,
         less_than: attribute(:max_redemptions) <-- a way to get attribute here
       ),
       message: "this coupon has reached its redemption limit"

You should be able to use :max_redemptions. It uses fetch_argument_or_attribute IIRC

Ah, it’s using Ash.Changeset.fetch_argument_or_change. It should be fetching the argument or the change or the current value

Ah, nvm that is only for the subject of comparison. What we actually do is: Ash.Changeset.get_argument_or_attribute(changeset, attribute)

EDIT: so my point is that it should effectively “just work”. Assuming the subject of the comparison is changing. Perhaps that is the issue though, I think we may need to check if any relevant value is changing… Yeah, that might be the problem. Instead of using fetch_attribute_or_change, we need to grab all fields involved and see if any of them are changing attributes or provided as attributes?

Yeah I was thinking we had something similar to what we have for ^arg(:arg_name) ie ^attribute(:max_redemptions) that would return that particular attribute in the changeset/data

For context, only total_redemptions is changing via an atomic change down the line, but :max_redemptions is just used for comparison.

Ah, gotcha. At the moment the builtins just expect an atom or a value. And you can use the magic arg(:arg) to place the arg value there. Did you try it with just an atom?

I tried just using the atom like so:

      validate compare(:total_redemptions,
                 less_than: :max_redemptions
               ),
               message: "this coupon has reached its redemption limit"

But it just skips over the validation? Doesnt throw an error, and my tests fail (doesnt pass the validation tests)

Tried using arg instead but also getting same results:

      validate compare(:total_redemptions,
                 less_than: arg(:max_redemptions)
               ),
               message: "this coupon has reached its redemption limit"

Hmm…do you also have an argument with the same name, :max_redemptions?

I do not. What I did is:

 argument :max_redemptions, :integer

validate compare(:total_redemptions,
           less_than: :max_redemptions
         ),
         message: "this coupon has reached its redemption limit"

The arg will still have to be ‘filled’ with the current attribute no?

In the mean time I’m just doing it manually like so:

      validate fn changeset ->
        max_redemptions = Ash.Changeset.get_attribute(changeset, :max_redemptions)
        total_redemptions = Ash.Changeset.get_attribute(changeset, :total_redemptions)

        if total_redemptions >= max_redemptions do
          {:error,
           field: :max_redemptions, message: "this coupon has reached its redemption limit"}
        else
          :ok
        end
      end

Yeah, I was suggesting that if you had an argument with the same name that it could break the behavior, not that you should do it, sorry about the miscommunication. So, this is definitely strange, and the builtin validations really ought to be rock solid, but I’m not really sure what’s going on at this point. A failing test in ash core would go a long way, if you have a chance. Otherwise I’ll put it on the stack and figure it out when I get a chance :slight_smile: