Skip to content

Instantly share code, notes, and snippets.

@alco
Created March 27, 2012 23:56
Show Gist options
  • Save alco/2221616 to your computer and use it in GitHub Desktop.
Save alco/2221616 to your computer and use it in GitHub Desktop.
Updated Elixir chat demo
# !!!
# This is an outdated version of the code. See https://gist.github.com/2783092.
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
match: { ^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
match: { ^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
match: {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])
match: {pid, {:say, message}}
notify_all room, pid, "#{inspect pid}: " <> message
pid <- { Process.self, :ok }
msg_loop room
match: {pid, :leave}
pid <- { Process.self, :ok }
# 'update_clients' is another automatically generated function
new_room = room.update_clients(fn(clients) -> :lists.delete(pid, clients) 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
@alco
Copy link
Author

alco commented May 28, 2012

I'll leave the current version as it is for historical record.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment