Skip to content

Instantly share code, notes, and snippets.

@freefrancisco
Forked from josevalim/sample output
Created September 13, 2012 18:56
Show Gist options
  • Save freefrancisco/3716727 to your computer and use it in GitHub Desktop.
Save freefrancisco/3716727 to your computer and use it in GitHub Desktop.
Updated Elixir chat demo
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
$ 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