I have a Topic resource that has a name and a slug attribute. I have defined the following change, but it is not being executed, and I cannot figure out what I’m doing wrong. It’s using the AshSlug extension.
change slugify(:name, into: :slug) do
where [attribute_in(:slug, [nil, ""])]
on [:create]
end
A few things you can do to debug something like this.
Throw stuff at the wall and see what sticks
First thing would be to remove some of your options and see if they are affecting it. i.e remove the where and remove the on. If you have a test this helps. Obviously you’d want a more deterministic answer, which you’ll find below, but experimentation will often get you there faster
Use the debug log change
change debug_log()
See if anything jumps out at you about the action when you run it
Write a custom change and see what happens
change fn changeset, context ->
IO.inspect(context)
IO.inspect(changeset)
end do
where [attribute_in(:slug, [nil, ""])]
on [:create]
end
Define your own validation module and use that as a condition
If it is something with attribute_in misbehaving, you can write a custom validation using Ash.Validation and see if that sheds any light on what may have been happening.
Maybe its a bug
It’s possible that attribute_in isn’t playing nicely with nil being in the list? I’d have to look into it. You could try writing your own validation and using that as a condition etc. If the above steps don’t work, please create a reproduction of the behavior and open an issue 
2 Likes
@zachdaniel Thanks for the response. I tried adding the custom change as you suggested, and it executed. Below is the output.
%Ash.Resource.Change.Context{
actor: nil,
tenant: nil,
authorize?: true,
tracer: nil,
bulk?: false,
source_context: %{
private: %{
upsert?: false,
upsert_fields: nil,
upsert_identity: nil,
upsert_condition: nil,
return_skipped_upsert?: false,
authorize?: true
}
}
}
#Ash.Changeset<
domain: TrainingDirectory.Catalog,
action_type: :create,
action: :create,
attributes: %{name: "Foo"},
relationships: %{},
arguments: %{},
errors: [],
data: %TrainingDirectory.Catalog.Topic{
id: nil,
name: nil,
slug: nil,
description: nil,
path: nil,
inserted_at: nil,
updated_at: nil,
parent_id: nil,
parent: #Ash.NotLoaded<:relationship, field: :parent>,
children: #Ash.NotLoaded<:relationship, field: :children>,
__meta__: #Ecto.Schema.Metadata<:built, "topics">
},
valid?: true
>
I’m relatively new to Elixir but have a lot of experience with Ruby.
I am trying to approach this like I would in Ruby by adding debug statements in the deps and recompiling to trace my way through the code, but I am not having much success. I think I’m adding them in the wrong place.
Maybe let’s revisit this assumption. What is the exact problematic behavior that you’re seeing?
I am expecting that on create, if the slug attribute is not set, then the slug attribute will be set to the slugified value of the name attribute.
I think the problem is what led me to believe it was not working. I have a subsequent change that is using Ash.Changeset.get_attribute(changeset, :slug) to get the value, and it is returning nil.
I commented that change out, and the slug attribute was set on the Topic returned from the create action.
So, my question actually is, how do I access the value of an attribute that may have been set in a previous change?
I’ve figured out the why. AshSlug is using a before_action which if I understand it correctly will cause it to run after my other changes.
Would the correct solution be to wrap my other change, which is dependent on the slugify change, in a before_action so that it will run after and have access to the attribute?
1 Like