-
-
Save suzukaze/9306509 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Chat.Client do | |
# In all of the following functions 'server' stands for the server's pid | |
def join(server) do | |
send server, :join | |
end | |
def say(server, message) do | |
send server, { :say, message } | |
end | |
def leave(server) do | |
send server, :leave | |
end | |
# Receive a pending message from 'server' and print it | |
def flush(server) do | |
receive do | |
# The caret '^' is used to match against the value of 'server', | |
# it is a basic filtering based on the sender | |
{ ^server, {:message, message} } -> | |
IO.puts message | |
end | |
end | |
# Send 'message' to 'server' and wait for a reply | |
# Notice how this function is declared using 'defp' meaning | |
# it's private and can only be called inside this module | |
defp send(server, message) do | |
server <- { Process.self, message } | |
receive do | |
{ ^server, response } -> | |
response | |
after | |
1000 -> | |
IO.puts "Connection to room timed out" | |
:timeout | |
end | |
end | |
end | |
defmodule Chat.Server do | |
# A Room record acts as a reference to 'this' or 'self' in other langs | |
defrecord Room, clients: [] | |
# The main receive loop | |
def msg_loop(room) do | |
receive do | |
{pid, :join } -> | |
notify_all room, Process.self, "Some user with pid #{inspect pid} joined" | |
pid <- { Process.self, :ok } | |
# The 'prepend_clients' function is generated automatically. | |
# See section 4.1.1 in http://elixir-lang.org/getting_started/4.html | |
msg_loop room.prepend_clients([pid]) | |
{pid, {:say, message}} -> | |
notify_all room, pid, "#{inspect pid}: " <> message | |
pid <- { Process.self, :ok } | |
msg_loop room | |
{pid, :leave} -> | |
pid <- { Process.self, :ok } | |
# 'update_clients' is another automatically generated function | |
new_room = room.update_clients(fn(clients) -> List.delete(clients, pid) end) | |
notify_all new_room, Process.self, "User with pid #{inspect pid} left" | |
msg_loop new_room | |
end | |
end | |
# Send 'message' to all clients except 'sender' | |
defp notify_all({Room, clients}, sender, message) do | |
Enum.each clients, fn(pid) -> | |
if pid != sender do | |
pid <- { Process.self, {:message, message} } | |
end | |
end | |
end | |
end | |
# Init a Room record with the current process | |
room = Chat.Server.Room.new(clients: [Process.self]) | |
# Spawn a server process | |
server_pid = spawn fn() -> Chat.Server.msg_loop room end | |
# Spawn another process as client | |
spawn fn() -> | |
Chat.Client.join server_pid | |
Chat.Client.say server_pid, "Hi!" | |
Chat.Client.leave server_pid | |
end | |
# And another one | |
spawn fn() -> | |
Chat.Client.join server_pid | |
Chat.Client.say server_pid, "What's up?" | |
Chat.Client.leave server_pid | |
end | |
# By this time we have 6 notifications pending | |
# (corresponding to three 'notify_all' calls per each client) | |
Enum.times 6, fn() -> | |
Chat.Client.flush server_pid | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ elixir chat_demo.ex | |
Some user with pid <0.41.0> joined | |
Some user with pid <0.42.0> joined | |
<0.41.0>: Hi! | |
User with pid <0.41.0> left | |
<0.42.0>: What's up? | |
User with pid <0.42.0> left |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment