Compile error: module Any is reserved and cannot be defined

Say I have the following resources: City:
# myapp/lib/myapp/data/resources/city.ex
defmodule MyApp.Data.City do
use Ash.Resource

actions do
defaults [:create, :update, :read, :destroy]
end

attributes do
uuid_primary_key :id
attribute :name, :string do
allow_nil? false
end
end

relationships do
has_one :state, MyApp.Data.State
end
end
# myapp/lib/myapp/data/resources/city.ex
defmodule MyApp.Data.City do
use Ash.Resource

actions do
defaults [:create, :update, :read, :destroy]
end

attributes do
uuid_primary_key :id
attribute :name, :string do
allow_nil? false
end
end

relationships do
has_one :state, MyApp.Data.State
end
end
State:
defmodule MyApp.Data.State do
@moduledoc false
use Ash.Resource

actions do
defaults [:create, :update, :read, :destroy]
end

attributes do
uuid_primary_key :state_id
attribute :name, :string do
allow_nil? false
end
attribute :url, :string do
allow_nil? true
end
end

relationships do
has_one :region, MyApp.Data.Region
end
end
defmodule MyApp.Data.State do
@moduledoc false
use Ash.Resource

actions do
defaults [:create, :update, :read, :destroy]
end

attributes do
uuid_primary_key :state_id
attribute :name, :string do
allow_nil? false
end
attribute :url, :string do
allow_nil? true
end
end

relationships do
has_one :region, MyApp.Data.Region
end
end
Region:
defmodule MyApp.Data.Region do
@moduledoc false
use Ash.Resource

actions do
defaults [:create, :update, :read, :destroy]
end

attributes do
uuid_primary_key :region_id
attribute :name, :string do
allow_nil? false
end
end
end
defmodule MyApp.Data.Region do
@moduledoc false
use Ash.Resource

actions do
defaults [:create, :update, :read, :destroy]
end

attributes do
uuid_primary_key :region_id
attribute :name, :string do
allow_nil? false
end
end
end
Credo seems to complain about state, saying:
** (CompileError) iex:1: module Any is reserved and cannot be defined
Elixir reserves the following module names: Elixir, Any, BitString, PID, and Reference.

(ArgumentError) schema does not have the field :id used by association :region, please set the :references option accordingly

Stacktrace:
│ (ecto 3.9.4) lib/ecto/association.ex:696: Ecto.Association.Has.struct/3
│ (ecto 3.9.4) lib/ecto/schema.ex:1879: Ecto.Schema.association/5
│ (ecto 3.9.4) lib/ecto/schema.ex:2006: Ecto.Schema.__has_one__/4
│ /Users/alec-s/Projects/myapp/lib/myapp/data/resources/state.ex:1: (file)
│ /Users/alec-s/Projects/myapp/lib/myapp/data/resources/state.ex:1: (file)
│ (stdlib 4.2) erl_eval.erl:748: :erl_eval.do_apply/7
│ (stdlib 4.2) erl_eval.erl:136: :erl_eval.exprs/6Elixir
** (CompileError) iex:1: module Any is reserved and cannot be defined
Elixir reserves the following module names: Elixir, Any, BitString, PID, and Reference.

(ArgumentError) schema does not have the field :id used by association :region, please set the :references option accordingly

Stacktrace:
│ (ecto 3.9.4) lib/ecto/association.ex:696: Ecto.Association.Has.struct/3
│ (ecto 3.9.4) lib/ecto/schema.ex:1879: Ecto.Schema.association/5
│ (ecto 3.9.4) lib/ecto/schema.ex:2006: Ecto.Schema.__has_one__/4
│ /Users/alec-s/Projects/myapp/lib/myapp/data/resources/state.ex:1: (file)
│ /Users/alec-s/Projects/myapp/lib/myapp/data/resources/state.ex:1: (file)
│ (stdlib 4.2) erl_eval.erl:748: :erl_eval.do_apply/7
│ (stdlib 4.2) erl_eval.erl:136: :erl_eval.exprs/6Elixir
Not really sure what to do about that.
13 Replies
jart
jart3y ago
Hey there. So it's because the relationships expect the related schemas to have an ID field called :id - so for example in your has_one :state State in City you need to add destination_attribute. See the DSL docs for more information: https://ash-hq.org/docs/dsl/ash-resource#relationships-has_one-destination_attribute
dblack
dblack3y ago
I'd use belongs_to instead of has_one in this example (has_one puts the foreign key on the other table)
jart
jart3y ago
right but you need to set source_attribute and destination_attribute appropriately so that ash knows which fields contain the foreign keys
dblack
dblack3y ago
Yep, absolutely
AlecStewart1#1125
Hmm I'm pretty sure I did that earlier and it still complained. Let me add it in again. This works:
belongs_to :region, MyApp.Data.Region do
source_attribute :region_id
destination_attribute :region
end
belongs_to :region, MyApp.Data.Region do
source_attribute :region_id
destination_attribute :region
end
So for when you want foreign keys, belongs_to is what's preferable?
dblack
dblack3y ago
Yeah, I think of has_one as being like has_many (you'd use on the other side of the relationship)
AlecStewart1#1125
Oh so has_one would be in the region resource, not the state resource?
dblack
dblack3y ago
also, you could use uuid_primary_key :id on all those resources, and not need to provide the source and destination attributes to belongs_to Yep, you could but i think you'll want has_many ie, a region has many states i might be misunderstanding the relationship though
AlecStewart1#1125
Okay, makes sense. I'm more or less trying to replicate something for work, because in the future we're considering moving away from our Java codebase and I'd like to have something to propose. So maybe has_many might work, considering a state has a region attribute that has the Java annotation @ManyToOne.
dblack
dblack3y ago
yep, so you'd have has_many on one side, and belongs_to on the other. If you've got a @OneToOne, you'll want a belongs_to on one side (the side with the foreign key) and a has_one on the other
AlecStewart1#1125
Ah, now it makes more sense.
dblack
dblack3y ago
I think of belongs_to as defining the relationship (it sets the foreign key on that resource) and has_one and has_many as utilities to go 'the other way' (if required) if that makes sense
AlecStewart1#1125
Makes sense.

Did you find this page helpful?