Print all data out

todo_server = [
%{date: ~D[2020-05-19], id: 1 ,time: ~T[16:52:00], title: "Shopping"},
%{date: ~D[2020-05-19], id: 2 ,time: ~T[16:52:00], title: "thinking"},
%{date: ~D[2020-05-20], id: 3 ,time: ~T[16:52:00], title: "Drinking"},
%{date: ~D[2020-05-20], id: 4 ,time: ~T[16:53:00], title: "swimming"}
]

i have 4 different value, so i can use

def time_check(todo_server, time) do
    GenServer.call(todo_server, {:time_check, time})
  end

i have a function can print out the whole result with the time i asked for
Todo.Server.time_check(todo_server, ~T[16:52:00])
return

[
%{date: ~D[2020-05-19], id: 1 ,time: ~T[16:52:00], title: "Shopping"},
%{date: ~D[2020-05-19], id: 2 ,time: ~T[16:52:00], title: "thinking"},
%{date: ~D[2020-05-20], id: 3 ,time: ~T[16:52:00], title: "Drinking"}
]

but how to print our the whole todo_server? by

def show_all(x) do
    IO.inspect(x)
  end

?

what does your handle_call callback look like?

@impl GenServer
def handle_call({:show_all}, _, {name, todo_list}) do
{:reply,Todo.List.show_all(todo_list),{name, todo_list}}
end

What do you mean by “print out the whole todo server”? What would you like to see in the output?

i want to see all the value of todo_server
[
%{date: ~D[2020-05-19], id: 1 ,time: ~T[16:52:00], title: “Shopping”},
%{date: ~D[2020-05-19], id: 2 ,time: ~T[16:52:00], title: “thinking”},
%{date: ~D[2020-05-20], id: 3 ,time: ~T[16:52:00], title: “Drinking”},
%{date: ~D[2020-05-20], id: 4 ,time: ~T[16:53:00], title: “swimming”}
]

So you want to see all values in the todo_list part of the GenServer state (that’s different from “the value of todo_server”, as todo_server in your time_check function is the pid or name of a GenServer process).

Here is the code from your previous message:

def handle_call({:show_all}, _, {name, todo_list}) do
  {:reply, Todo.List.show_all(todo_list), {name, todo_list}}
end

You are matching on the message {:show_all}, so you need to issue the following call: GenServer.call(todo_server, {:show_all}).

Note that there are a couple of more things you could improve, not related to the main question though:

  1. Your message is a tuple of a single element, {:show_all}. It’s not really necessary to wrap the message in a tuple, if it’s made of a single value: you could just send directly the atom :show_all instead. Of course, if you change this, the message that you send in GenServer.call should match the one that you pattern match in handle_call.

  2. Instead of printing the list with IO.inspect, you could just return it, and in case let the caller print it.

2 Likes

i settle up like this in server.ex

def show_all(todo_server) do
    GenServer.call(todo_server, :show_all)
  end
  
  def handle_call({:show_all}, _, {name, todo_list}) do
	{:reply, Todo.List.show_all(todo_list), {name, todo_list}}
  end

and list.ex

def show_all(todo_list) do
    todo_list.show_all
    |> Enum.map(fn {_, entry} -> entry end)
  end

which i got a return of this

iex(7)> Todo.Server.show_all(testy)

00:18:25.365 [error] GenServer {Todo.ProcessRegistry, {Todo.Server, "testy"}} terminating
** (FunctionClauseError) no function clause matching in Todo.Server.handle_call/3
    (todo) lib/todo/server.ex:71: Todo.Server.handle_call(:show_all, {#PID<0.139.0>, #Reference<0.1044764218.1942487041.128436>}, {"testy", %Todo.List{auto_id: 4, entries: %{1 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 1, time: ~T[16:52:00], title: "Shopping"}, 2 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 2, time: ~T[16:52:00], title: "cleaning"}, 3 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 3, time: ~T[16:52:00], title: "sweeping"}}}})
    (stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
    (stdlib) gen_server.erl:690: :gen_server.handle_msg/6
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.139.0>): :show_all
State: {"testy", %Todo.List{auto_id: 4, entries: %{1 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 1, time: ~T[16:52:00], title: "Shopping"}, 2 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 2, time: ~T[16:52:00], title: "cleaning"}, 3 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 3, time: ~T[16:52:00], title: "sweeping"}}}}
Client #PID<0.139.0> is alive

    (stdlib) gen.erl:169: :gen.do_call/4
    (elixir) lib/gen_server.ex:1006: GenServer.call/3
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir) src/elixir.erl:275: :elixir.eval_forms/4
    (iex) lib/iex/evaluator.ex:257: IEx.Evaluator.handle_eval/5
    (iex) lib/iex/evaluator.ex:237: IEx.Evaluator.do_eval/3
    (iex) lib/iex/evaluator.ex:215: IEx.Evaluator.eval/3
    (iex) lib/iex/evaluator.ex:103: IEx.Evaluator.loop/1
** (exit) exited in: GenServer.call(#PID<0.158.0>, :show_all, 5000)
    ** (EXIT) an exception was raised:
        ** (FunctionClauseError) no function clause matching in Todo.Server.handle_call/3
            (todo) lib/todo/server.ex:71: Todo.Server.handle_call(:show_all, {#PID<0.139.0>, #Reference<0.1044764218.1942487041.128436>}, {"testy", %Todo.List{auto_id: 4, entries: %{1 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 1, time: ~T[16:52:00], title: "Shopping"}, 2 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 2, time: ~T[16:52:00], title: "cleaning"}, 3 => %{__struct__: Todo.Server.Entry, date: ~D[2020-05-19], id: 3, time: ~T[16:52:00], title: "sweeping"}}}})
            (stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
            (stdlib) gen_server.erl:690: :gen_server.handle_msg/6
            (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
    (elixir) lib/gen_server.ex:1009: GenServer.call/3

As @lucaong pointed out, the message you send must have a matching function head. In this cas you are sending the message :show_all but you are only expecting a message {:show_all}. They are not the same.

For this class of error the messages are pretty clear I think so its good to get comfortable with reading function clause errors.

2 Likes