Ecto and Access behaviour

Hi,
I’d be glad if someone explain to me why 1st statement is fine but second one shouts an error upon execution:
Details : world_state is a map, db is an Ecto schema

Fine:

put_in(world_state.db.current_date, world_state.target_time)

Error:

put_in(world_state,[:db, :current_date], world_state.target_time)

[error] GenServer :sim_1 terminating
** (UndefinedFunctionError) function Msim.Simulator.Instance.get_and_update/3 is undefined (Msim.Simulator.Instance does not implement the Access behaviour)
    (msim) Msim.Simulator.Instance.get_and_update(%Msim.Simulator.Instance{__meta__: #Ecto.Schema.Metadata<:loaded, "sim_instance">, current_date: ~N[2020-04-30 17:27:48], id: 1, inserted_at: ~N[2020-04-13 17:21:39], name: "First world", status: 2, type: %Msim.Simulator.Type{__meta__: #Ecto.Schema.Metadata<:loaded, "sim_type">, back_module: "Elixir.Msim.Worlds.World_A", description: "first simulator, under development", front_module: "Elixir.MsimWeb.LV.Worlds.World_A", id: 1, inserted_at: ~N[2020-04-13 17:21:39], name: "Simple world type - one type of bank only", updated_at: ~N[2020-04-13 17:21:39], worlds: #Ecto.Association.NotLoaded<association :worlds is not loaded>}, type_id: 1, updated_at: ~N[2020-04-13 17:21:39], visibility: 1}, :current_date, #Function<17.77964990/1 in Kernel.put_in/3>)
    (elixir) lib/access.ex:319: Access.get_and_update/3 
    (elixir) lib/map.ex:819: Map.get_and_update/3
    (elixir) lib/kernel.ex:2276: Kernel.put_in/3

Because the module of the schema does not implement Access, which is required for the list of accessors.

Expanding on this a little:

This works fine because it uses the put_in macro, which does all of the work via ., which works just fine on structs whether they have implemented access or not

BUT

uses the put_in function which requires some cooperation from the module.

I have the following in my ecto schemas (extracted via a macro of course)


      @behaviour Access

      defdelegate fetch(term, key), to: Map
      defdelegate get(term, key, default), to: Map
      defdelegate get_and_update(term, key, fun), to: Map
      defdelegate pop(term, key), to: Map
3 Likes

Thank you, very useful explanation. :grinning: