Created
March 27, 2012 23:56
-
-
Save alco/2221616 to your computer and use it in GitHub Desktop.
Updated Elixir chat demo
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
# !!! | |
# 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 |
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 |
Done. Thanks for pointing that out.
Code needs to be fixed a bit, there's a new syntax for receive
: no more match: { ... }
/after: 1000
, but { ... } ->
and after 1000 ->
:)
(Rant: If there'd be some changelog on such things... There're lots of Elixir code samples floating around the 'net, but they're mostly broken.)
I have a forked and updated version at: https://gist.github.com/2783092 :)
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
For consistency with the Getting Started Guide, should this sample be reindented to use 2 spaces?