Embedded Resource default value causes compilation error

I have a fairly simple embedded resource:
defmodule App.Organizations.OrganizationSettings do
@moduledoc false

use Ash.Resource,
data_layer: :embedded

alias __MODULE__.Validations

actions do
defaults [:read, :create, :update]
default_accept [:vat_exempt?, :timezone]
end

validations do
validate Validations.ValidateTimezone
end

attributes do
uuid_primary_key :id

attribute :vat_exempt?, :boolean, default: false, source: :has_vat_exempt, allow_nil?: false
attribute :timezone, :string, default: "Europe/Berlin", allow_nil?: false

timestamps()
end
end
defmodule App.Organizations.OrganizationSettings do
@moduledoc false

use Ash.Resource,
data_layer: :embedded

alias __MODULE__.Validations

actions do
defaults [:read, :create, :update]
default_accept [:vat_exempt?, :timezone]
end

validations do
validate Validations.ValidateTimezone
end

attributes do
uuid_primary_key :id

attribute :vat_exempt?, :boolean, default: false, source: :has_vat_exempt, allow_nil?: false
attribute :timezone, :string, default: "Europe/Berlin", allow_nil?: false

timestamps()
end
end
and a simple validation:
defmodule App.Organizations.OrganizationSettings.Validations.ValidateTimezone do
@moduledoc false

use Ash.Resource.Validation

@impl true
def validate(changeset, _opts, _context) do
timezone = Ash.Changeset.get_attribute(changeset, :timezone)

if Timex.Timezone.exists?(timezone) do
:ok
else
{:error, field: :timezone, message: "is invalid"}
end
end
end
defmodule App.Organizations.OrganizationSettings.Validations.ValidateTimezone do
@moduledoc false

use Ash.Resource.Validation

@impl true
def validate(changeset, _opts, _context) do
timezone = Ash.Changeset.get_attribute(changeset, :timezone)

if Timex.Timezone.exists?(timezone) do
:ok
else
{:error, field: :timezone, message: "is invalid"}
end
end
end
Solution:
default: fn -> %{} end in this case
Jump to solution
8 Replies
zimt28
zimt28OP2mo ago
In the parent resource, when I use it without a default value, everything works fine, but as soon as I give it a default value, it fails:
attribute :settings, OrganizationSettings, default: %{}
attribute :settings, OrganizationSettings, default: %{}
== Compilation error in file lib/app/organizations/organization.ex ==
** (Ash.Error.Unknown)
Bread Crumbs:
> building changeset for App.Organizations.OrganizationSettings.create

Unknown Error

* ** (ArgumentError) errors were found at the given arguments:

* 1st argument: the table identifier does not refer to an existing ETS table

(stdlib 6.2) :ets.lookup(:tzdata_current_release, :release_version)
(tzdata 1.1.3) lib/tzdata/release_reader.ex:74: Tzdata.ReleaseReader.current_release_from_table/0
(tzdata 1.1.3) lib/tzdata/release_reader.ex:17: Tzdata.ReleaseReader.simple_lookup/1
(tzdata 1.1.3) lib/tzdata/release_reader.ex:9: Tzdata.ReleaseReader.zone_and_link_list/0
(tzdata 1.1.3) lib/tzdata.ex:61: Tzdata.zone_exists?/1
(timex 3.7.13) lib/timezone/timezone.ex:28: Timex.Timezone.exists?/1
lib/app/.../validate_timezone.ex:12: App.Organizations.OrganizationSettings.Validations.ValidateTimezone.validate/3
(ash 3.5.34) lib/ash/resource/validation.ex:227: Ash.Resource.Validation.validate/4
(stdlib 6.2) :ets.lookup(:tzdata_current_release, :release_version)
(tzdata 1.1.3) lib/tzdata/release_reader.ex:74: Tzdata.ReleaseReader.current_release_from_table/0
(tzdata 1.1.3) lib/tzdata/release_reader.ex:17: Tzdata.ReleaseReader.simple_lookup/1
(tzdata 1.1.3) lib/tzdata/release_reader.ex:9: Tzdata.ReleaseReader.zone_and_link_list/0
(tzdata 1.1.3) lib/tzdata.ex:61: Tzdata.zone_exists?/1
(timex 3.7.13) lib/timezone/timezone.ex:28: Timex.Timezone.exists?/1
lib/app/organizations/organization_settings/validations/validate_timezone.ex:12: App.Organizations.OrganizationSettings.Validations.ValidateTimezone.validate/3
(ash 3.5.34) lib/ash/resource/validation.ex:227: Ash.Resource.Validation.validate/4
== Compilation error in file lib/app/organizations/organization.ex ==
** (Ash.Error.Unknown)
Bread Crumbs:
> building changeset for App.Organizations.OrganizationSettings.create

Unknown Error

* ** (ArgumentError) errors were found at the given arguments:

* 1st argument: the table identifier does not refer to an existing ETS table

(stdlib 6.2) :ets.lookup(:tzdata_current_release, :release_version)
(tzdata 1.1.3) lib/tzdata/release_reader.ex:74: Tzdata.ReleaseReader.current_release_from_table/0
(tzdata 1.1.3) lib/tzdata/release_reader.ex:17: Tzdata.ReleaseReader.simple_lookup/1
(tzdata 1.1.3) lib/tzdata/release_reader.ex:9: Tzdata.ReleaseReader.zone_and_link_list/0
(tzdata 1.1.3) lib/tzdata.ex:61: Tzdata.zone_exists?/1
(timex 3.7.13) lib/timezone/timezone.ex:28: Timex.Timezone.exists?/1
lib/app/.../validate_timezone.ex:12: App.Organizations.OrganizationSettings.Validations.ValidateTimezone.validate/3
(ash 3.5.34) lib/ash/resource/validation.ex:227: Ash.Resource.Validation.validate/4
(stdlib 6.2) :ets.lookup(:tzdata_current_release, :release_version)
(tzdata 1.1.3) lib/tzdata/release_reader.ex:74: Tzdata.ReleaseReader.current_release_from_table/0
(tzdata 1.1.3) lib/tzdata/release_reader.ex:17: Tzdata.ReleaseReader.simple_lookup/1
(tzdata 1.1.3) lib/tzdata/release_reader.ex:9: Tzdata.ReleaseReader.zone_and_link_list/0
(tzdata 1.1.3) lib/tzdata.ex:61: Tzdata.zone_exists?/1
(timex 3.7.13) lib/timezone/timezone.ex:28: Timex.Timezone.exists?/1
lib/app/organizations/organization_settings/validations/validate_timezone.ex:12: App.Organizations.OrganizationSettings.Validations.ValidateTimezone.validate/3
(ash 3.5.34) lib/ash/resource/validation.ex:227: Ash.Resource.Validation.validate/4
ZachDaniel
ZachDaniel2mo ago
Interesting... yeah its trying to cast the default value to the embed at compile time but its not safe to do that in that instance because it needs the time zone database running
zimt28
zimt28OP2mo ago
I see. Is this something that needs to be considered in core Ash?
ZachDaniel
ZachDaniel2mo ago
I think you need to set the default value to a function perhaps
Solution
ZachDaniel
ZachDaniel2mo ago
default: fn -> %{} end in this case
zimt28
zimt28OP2mo ago
As I have a default value I just skip the call using
if timezone == "Europe/Berlin" || Timex.Timezone.exists?(timezone) do
:ok
else
{:error, field: :timezone, message: "is invalid"}
end
if timezone == "Europe/Berlin" || Timex.Timezone.exists?(timezone) do
:ok
else
{:error, field: :timezone, message: "is invalid"}
end
ZachDaniel
ZachDaniel2mo ago
oh, that should probably work too you're saying that works?
zimt28
zimt28OP2mo ago
Yes, this works Let me check the function default, too This works as well

Did you find this page helpful?