Use after_action to trigger another action in Resource DSL

Can after_action be used to invoke another action inside the DSL for resource actions? Or what is the best approach when multiple attributes need to be updated (and one of them requires business logic to determine the value)?

Hey there! So, I think what you’re looking for are changes and potentially lifecycle hooks. Here is an example of setting an attribute based on other changes.

create :create do
  accept [:first_name, :last_name]

  change fn changeset, _ -> 
    first_name = Ash.Changeset.get_attribute(changeset, :first_name) || ""
    last_name = Ash.Changeset.get_attribute(changeset, :last_name) || ""
    Ash.Changeset.force_change_attribute(first_name <> " " <> last_name)
  end
end

If you want to do some “expensive” work, or work that should happen in the same transaction, you can use before_action and after_action hooks (probably before action in your case of affecting the attribute changes)

create :create do
  accept [:first_name, :last_name]

  change fn changeset, _ -> 
    Ash.Changeset.before_action(changeset, fn changeset -> 
      something = get_something_from_the_database()
      Ash.Changeset.force_change_attribute(changeset, :foo, something)
    end)
  end
end
1 Like

Thanks! My problem is similar so your proposal should work.

A similar example would be a guessing game where there would be a “problem” resource. I am tracking the count of incorrect attempts on the resource itself (it belongs to a user resource) but want to “lock” guessing for a certain period of time after a certain amount of incorrect guesses. I need them to happen in the same transaction, and I need the ability to unlock and reset the failure count for the resource.

Where I was getting confused on the documentation was adding a before_action attribute directly in the action instead of using the change DSL and manually adapting the changeset.