summary refs log tree commit diff stats
path: root/main.ex
diff options
context:
space:
mode:
Diffstat (limited to 'main.ex')
-rw-r--r--main.ex134
1 files changed, 134 insertions, 0 deletions
diff --git a/main.ex b/main.ex
new file mode 100644
index 0000000..acee7a0
--- /dev/null
+++ b/main.ex
@@ -0,0 +1,134 @@
+defmodule Message do
+    defstruct sender_name: "",
+              sender_pid:  nil,
+              type:        nil,
+              args:        []
+end
+
+defmodule Info do
+    defstruct name:   "",
+              others: %{}
+end
+
+defmodule Server do
+    defstruct pid:        nil,
+              client_pid: nil,
+              handler:    nil,
+              info:       %Info{}
+
+    def run(name, handler) do
+        state = %Server {
+            pid:     self(),
+            handler: handler,
+            info:    %Info{name: name}
+        }
+
+        server(state)
+    end
+
+    defp server(state) do
+        state = receive do msg ->
+            case msg.type do
+                :start -> start(state, msg)
+                :ping  -> pong(state, msg)
+                _      -> state.handler.(state, msg)
+            end
+        end
+
+        server(state)
+    end
+
+    defp start(state, msg) do
+        [others|_] = msg.args
+        state = put_in(state.info.others, others)
+
+        client_pid = spawn(Client, :run, [self(), state.info])
+        state = put_in(state.client_pid, client_pid)
+
+        state
+    end
+
+    defp pong(state, msg) do
+        IO.puts "<#{state.info.name}> #{msg.sender_name}: PONG!"
+        state
+    end
+end
+
+defmodule Client do
+    defstruct pid:        nil,
+              server_pid: nil,
+              info:       %Info{}
+
+    def run(server_pid, info) do
+        me = %Client{
+            pid:        self(),
+            server_pid: server_pid,
+            info:       info,
+        }
+
+        Enum.each info.others, fn other ->
+            ping(me, other)
+        end
+    end
+
+    defp ping(me, {recp_name, recp_pid}) do
+        msg = %Message{
+            sender_name: me.info.name,
+            sender_pid:  me.server_pid,
+            type:        :ping,
+            args:        []
+        }
+
+        IO.puts "<#{me.info.name}> #{recp_name}: PING!"
+        send(recp_pid, msg)
+    end
+end
+
+defmodule Person do
+    def run(name, handler) do
+        spawn(Server, :run, [name, handler])
+    end
+end
+
+defmodule Ref do
+    def run() do
+        get_names(4) |> get_people() |> start_people()
+    end
+
+    defp get_names(n) do
+        Enum.map (1..n), &(String.to_atom inspect &1)
+    end
+
+    defp get_people(names) do
+        default_handler = fn state, msg ->
+            IO.puts "unhandled #{inspect msg.type}"
+            state
+        end
+
+        Keyword.new names, fn name ->
+            {name, Person.run(name, default_handler)}
+        end
+    end
+
+    defp start_people(people) do
+        Enum.each people, fn person = {_name, server} ->
+            others = Enum.filter people, fn other ->
+                other != person
+            end
+
+            msg = %Message{
+                sender_name: "ref",
+                sender_pid:  self(),
+                type:        :start,
+                args:        [others]}
+
+            send(server, msg)
+        end
+
+        people
+    end
+end
+
+defmodule Main do
+    Ref.run()
+end