ActiveRecord has many built-in callbacks that allow you to perform actions before or after model creation, deletion, update, and more. You can about them here.
Ecto once had this concept, but it was removed in version 2.0, citing the lack of fine-grained control they create. Of course, the replacement is to make a more complex changeset.
Let's take this example AR model, inspired by Discourse, that converts a user's username to lowercase and stores it in a separate field.
# user.rb
validates_presence_of :username
before_save :update_username_lower
def update_username_lower
self.username_lower = username.downcase
end
So how does this translate? We do want to mimic the functionality of it always running, and it's as simple as piping an additional function into the changeset. (More functions in functional programming, what has the world come to?)
Make sure the function returns the changeset as well.
# user.ex
def changeset(user, attrs)
user
|> cast(attrs, [:username])
|> validate_required([:username])
|> update_username_lower()
end
defp update_username_lower(changeset)
changeset
|> put_change(:username_lower, changeset.username |> String.downcase)
end
Remember to use this changeset (or a higher order changeset that uses it) whenever making changes to a user.