diff --git a/README.md b/README.md
index 4070a1d..3c61a05 100644
--- a/README.md
+++ b/README.md
@@ -1,85 +1,86 @@
 # ExIrc
 
 ExIrc is a IRC client library for Elixir projects. It aims to have a clear, well
 documented API, with the minimal amount of code necessary to allow you to connect and
 communicate with IRC servers effectively. It aims to implement the full RFC2812 protocol,
 and relevant parts of RFC1459.
 
 ## Why?
 
 I had need of this in another project of mine, and found that there were no good libraries available, 
 documentation was always missing, it wasn't clear how to use, and in general were not easy to work
 with.
 
 ## Status
 
 [![wercker status](https://app.wercker.com/status/236e4911da7c4575c49b1b20b9ec775d/m/ "wercker status")](https://app.wercker.com/project/bykey/236e4911da7c4575c49b1b20b9ec775d)
 
 Alpha. The API is complete and everything is implemented, but little testing has been done (I've tested the API against my own local IRC server, but nothing robust enough to call this production ready). Any bugs you find, please report them in the issue tracker and I'll address them as soon as possible. If you have any questions, or if the documentation seems incomplete, let me know and I'll fill it in.
 
 ## Getting Started
 
 If you use expm, the ExIrc package is available
 [here](http://expm.co/exirc).
 
 Add ExIrc as a dependency to your project in mix.exs, and add it as an application:
 
 ```elixir
   defp deps do
-    [{:exirc, github: "bitwalker/exirc"}]
+    [{:exirc, "~> 0.4.0"}]
   end
 
   defp application do
     [applications: [:exirc],
      ...]
   end
 ```
 
 Then fetch it using `mix deps.get`.
 
 To use ExIrc, you need to start a new client process, and add event handlers. An example event handler module
 is located in `lib/exirc/example_handler.ex`. **The example handler is kept up to date with all events you can
 expect to receive from the client**. A simple module is defined below as an example of how you might
 use ExIrc in practice. ExampleHandler here is the one that comes bundled with ExIrc.
 
 ```elixir
 defmodule ExampleSupervisor do
-    defrecord State, 
-        host: "chat.freenode.net",
-        port: 6667,
-        pass: "",
-        nick: "bitwalker",
-        user: "bitwalker",
-        name: "Paul Schoenfelder",
-        client: nil,
-        handlers: []
+    defmodule State do
+        defstruct host: "chat.freenode.net",
+                  port: 6667,
+                  pass: "",
+                  nick: "bitwalker",
+                  user: "bitwalker",
+                  name: "Paul Schoenfelder",
+                  client: nil,
+                  handlers: []
+    end
 
     def start_link(_) do
-        :gen_server.start_link(__MODULE__, [State.new()])
+        :gen_server.start_link(__MODULE__, [%State{}])
     end
 
     def init(state) do
         # Start the client and handler processes, the ExIrc supervisor is automatically started when your app runs
         {:ok, client}  = ExIrc.start_client!()
         {:ok, handler} = ExampleHandler.start_link(nil)
 
         # Register the event handler with ExIrc
         ExIrc.Client.add_handler client, handler
 
         # Connect and logon to a server, join a channel and send a simple message
         ExIrc.Client.connect!   client, state.host, state.port
         ExIrc.Client.logon      client, state.pass, state.nick, state.user, state.name
         ExIrc.Client.join       client, "#elixir-lang"
         ExIrc.Client.msg        client, :privmsg, "#elixir-lang", "Hello world!"
 
-        {:ok, state.client(client).handlers([handler])}
+        {:ok, %{state | :client => client, :handlers => [handler]}}
     end
 
     def terminate(_, state) do
         # Quit the channel and close the underlying client connection when the process is terminating
         ExIrc.Client.quit state.client, "Goodbye, cruel world."
         ExIrc.Client.stop! state.client
         :ok
     end
 end
 ```
diff --git a/lib/exirc/channels.ex b/lib/exirc/channels.ex
index a725767..17f5d24 100644
--- a/lib/exirc/channels.ex
+++ b/lib/exirc/channels.ex
@@ -1,229 +1,230 @@
 defmodule ExIrc.Channels do
   @moduledoc """
   Responsible for managing channel state
   """
   use Irc.Commands
 
   import String, only: [downcase: 1]
 
-  defrecord Channel,
-    name:  '',
-    topic: '',
-    users: [],
-    modes: '',
-    type:  ''
+  defmodule Channel do
+    defstruct name:  '',
+              topic: '',
+              users: [],
+              modes: '',
+              type:  ''
+  end
 
   @doc """
   Initialize a new Channels data store
   """
   def init() do
     :gb_trees.empty()
   end
 
   ##################
   # Self JOIN/PART
   ##################
 
   @doc """
   Add a channel to the data store when joining a channel
   """
   def join(channel_tree, channel_name) do
     name = downcase(channel_name)
     case :gb_trees.lookup(name, channel_tree) do
       {:value, _} ->
         channel_tree
       :none ->
-        :gb_trees.insert(name, Channel.new(name: name), channel_tree)
+        :gb_trees.insert(name, %Channel{name: name}, channel_tree)
     end
   end
 
   @doc """
   Remove a channel from the data store when leaving a channel
   """
   def part(channel_tree, channel_name) do
     name = downcase(channel_name)
     case :gb_trees.lookup(name, channel_tree) do
       {:value, _} ->
         :gb_trees.delete(name, channel_tree)
       :none ->
         channel_tree
     end
   end
 
   ###########################
   # Channel Modes/Attributes
   ###########################
 
   @doc """
   Update the topic for a tracked channel when it changes
   """
   def set_topic(channel_tree, channel_name, topic) do
     name = downcase(channel_name)
     case :gb_trees.lookup(name, channel_tree) do
       {:value, channel} ->
-        :gb_trees.enter(name, channel.topic(topic), channel_tree)
+        :gb_trees.enter(name, %{channel | :topic => topic}, channel_tree)
       :none ->
         channel_tree
     end
   end
 
   @doc """
   Update the type of a tracked channel when it changes
   """
   def set_type(channel_tree, channel_name, channel_type) when is_binary(channel_type) do
-    set_type(channel_tree, channel_name, String.to_char_list!(channel_type))
+    set_type(channel_tree, channel_name, List.from_char_data!(channel_type))
   end
   def set_type(channel_tree, channel_name, channel_type) do
     name = downcase(channel_name)
     case :gb_trees.lookup(name, channel_tree) do
       {:value, channel} ->
         type = case channel_type do
              '@' -> :secret
              '*' -> :private
              '=' -> :public
         end
-        :gb_trees.enter(name, channel.type(type), channel_tree)
+        :gb_trees.enter(name, %{channel | :type => type}, channel_tree)
       :none ->
         channel_tree
     end
   end
 
   ####################################
   # Users JOIN/PART/AKAs(namechange)
   ####################################
 
   @doc """
   Add a user to a tracked channel when they join
   """
   def user_join(channel_tree, channel_name, nick) when not is_list(nick) do
     users_join(channel_tree, channel_name, [nick])
   end
 
   @doc """
   Add multiple users to a tracked channel (used primarily in conjunction with the NAMES command)
   """
   def users_join(channel_tree, channel_name, nicks) do
     pnicks = strip_rank(nicks)
     manipfn = fn(channel_nicks) -> :lists.usort(channel_nicks ++ pnicks) end
     users_manip(channel_tree, channel_name, manipfn)
   end
 
   @doc """
   Remove a user from a tracked channel when they leave
   """
   def user_part(channel_tree, channel_name, nick) do
     pnick = strip_rank([nick])
     manipfn = fn(channel_nicks) -> :lists.usort(channel_nicks -- pnick) end
     users_manip(channel_tree, channel_name, manipfn)
   end
 
   @doc """
   Update the nick of a user in a tracked channel when they change their nick
   """
   def user_rename(channel_tree, nick, new_nick) do
     manipfn = fn(channel_nicks) ->
       case Enum.member?(channel_nicks, nick) do
         true  -> [new_nick | channel_nicks -- [nick]] |> Enum.uniq |> Enum.sort
         false -> channel_nicks
        end
     end
     foldl = fn(channel_name, new_channel_tree) ->
       name = downcase(channel_name)
       users_manip(new_channel_tree, name, manipfn)
     end
     :lists.foldl(foldl, channel_tree, channels(channel_tree))
   end
 
   ################
   # Introspection
   ################
 
   @doc """
   Get a list of all currently tracked channels
   """
   def channels(channel_tree) do
-    (lc {channel_name, _chan} inlist :gb_trees.to_list(channel_tree), do: channel_name) |> Enum.reverse
+    (for {channel_name, _chan} <- :gb_trees.to_list(channel_tree), do: channel_name) |> Enum.reverse
   end
 
   @doc """
   Get a list of all users in a tracked channel
   """
   def channel_users(channel_tree, channel_name) do
-    get_attr(channel_tree, channel_name, fn(Channel[users: users]) -> users end) |> Enum.reverse
+    get_attr(channel_tree, channel_name, fn(%Channel{:users => users}) -> users end) |> Enum.reverse
   end
 
   @doc """
   Get the current topic for a tracked channel
   """
   def channel_topic(channel_tree, channel_name) do
-    case get_attr(channel_tree, channel_name, fn(Channel[topic: topic]) -> topic end) do
+    case get_attr(channel_tree, channel_name, fn(%Channel{:topic => topic}) -> topic end) do
       []    -> "No topic"
       topic -> topic
     end
   end
 
   @doc """
   Get the type of a tracked channel
   """
   def channel_type(channel_tree, channel_name) do
-    case get_attr(channel_tree, channel_name, fn(Channel[type: type]) -> type end) do
+    case get_attr(channel_tree, channel_name, fn(%Channel{:type => type}) -> type end) do
       []   -> :unknown
       type -> type
     end
   end
 
   @doc """
   Determine if a user is present in a tracked channel
   """
   def channel_has_user?(channel_tree, channel_name, nick) do
-    get_attr(channel_tree, channel_name, fn(Channel[users: users]) -> :lists.member(nick, users) end)
+    get_attr(channel_tree, channel_name, fn(%Channel{:users => users}) -> :lists.member(nick, users) end)
   end
 
   @doc """
   Get all channel data as a tuple of the channel name and a proplist of metadata.
 
   Example Result:
 
       [{"#testchannel", [users: ["userA", "userB"], topic: "Just a test channel.", type: :public] }]
   """
   def to_proplist(channel_tree) do
-    (lc {channel_name, chan} inlist :gb_trees.to_list(channel_tree), do: {
-      channel_name, [users: chan.users, topic: chan.topic, type: chan.type]
-    }) |> Enum.reverse
+    for {channel_name, chan} <- :gb_trees.to_list(channel_tree) do
+      {channel_name, [users: chan.users, topic: chan.topic, type: chan.type]}
+    end |> Enum.reverse
   end
 
   ####################
   # Internal API
   ####################
   defp users_manip(channel_tree, channel_name, manipfn) do
     name = downcase(channel_name)
     case :gb_trees.lookup(name, channel_tree) do
       {:value, channel} ->
         channel_list = manipfn.(channel.users)
-        :gb_trees.enter(channel_name, channel.users(channel_list), channel_tree)
+        :gb_trees.enter(channel_name, %{channel | :users => channel_list}, channel_tree)
       :none ->
         channel_tree
     end
   end
 
   defp strip_rank(nicks) do
     nicks |> Enum.map(fn(n) -> case n do
         << "@", nick :: binary >> -> nick
         << "+", nick :: binary >> -> nick
         << "%", nick :: binary >> -> nick
         << "&", nick :: binary >> -> nick
         << "~", nick :: binary >> -> nick
         nick -> nick
       end
     end)
   end
 
   defp get_attr(channel_tree, channel_name, getfn) do
     name = downcase(channel_name)
     case :gb_trees.lookup(name, channel_tree) do
       {:value, channel} -> getfn.(channel)
       :none -> {:error, :no_such_channel}
     end
   end
 
 end
diff --git a/lib/exirc/client.ex b/lib/exirc/client.ex
index 1b7bb99..031b2bb 100644
--- a/lib/exirc/client.ex
+++ b/lib/exirc/client.ex
@@ -1,646 +1,648 @@
 defmodule ExIrc.Client do
   @moduledoc """
   Maintains the state and behaviour for individual IRC client connections
   """
   use    Irc.Commands
   import ExIrc.Logger
-  import String, only: [to_char_list!: 1, from_char_list!: 1]
 
   alias ExIrc.Channels, as: Channels
   alias ExIrc.Utils,    as: Utils
 
-  # Records
-  defrecord ClientState,
-    event_handlers:   [],
-    server:           "localhost",
-    port:             6667,
-    socket:           nil,
-    nick:             "",
-    pass:             "",
-    user:             "",
-    name:             "",
-    connected?:       false,
-    logged_on?:       false,
-    autoping:         true,
-    channel_prefixes: "",
-    network:          "",
-    user_prefixes:    "",
-    login_time:       "",
-    channels:         [],
-    debug?:           false
+  # Client internal state
+  defmodule ClientState do
+    defstruct event_handlers:   [],
+              server:           "localhost",
+              port:             6667,
+              socket:           nil,
+              nick:             "",
+              pass:             "",
+              user:             "",
+              name:             "",
+              connected?:       false,
+              logged_on?:       false,
+              autoping:         true,
+              channel_prefixes: "",
+              network:          "",
+              user_prefixes:    "",
+              login_time:       "",
+              channels:         [],
+              debug?:           false
+  end
 
   #################
   # External API
   #################
 
   @doc """
   Start a new IRC client process
 
   Returns either {:ok, pid} or {:error, reason}
   """
   @spec start!(options :: list() | nil) :: {:ok, pid} | {:error, term}
   def start!(options \\ []) do
     start_link(options)
   end
   @doc """
   Start a new IRC client process.
 
   Returns either {:ok, pid} or {:error, reason}
   """
   @spec start_link(options :: list() | nil) :: {:ok, pid} | {:error, term}
   def start_link(options \\ []) do
     :gen_server.start_link(__MODULE__, options, [])
   end
   @doc """
   Stop the IRC client process
   """
   @spec stop!(client :: pid) :: {:stop, :normal, :ok, ClientState.t}
   def stop!(client) do
     :gen_server.call(client, :stop)
   end
   @doc """
   Connect to a server with the provided server and port
 
   Example:
     Client.connect! pid, "localhost", 6667
   """
   @spec connect!(client :: pid, server :: binary, port :: non_neg_integer) :: :ok
   def connect!(client, server, port) do
     :gen_server.call(client, {:connect, server, port}, :infinity)
   end
   @doc """
   Determine if the provided client process has an open connection to a server
   """
   @spec is_connected?(client :: pid) :: true | false
   def is_connected?(client) do
     :gen_server.call(client, :is_connected?)
   end
   @doc """
   Logon to a server
 
   Example:
     Client.logon pid, "password", "mynick", "username", "My Name"
   """
   @spec logon(client :: pid, pass :: binary, nick :: binary, user :: binary, name :: binary) :: :ok | {:error, :not_connected}
   def logon(client, pass, nick, user, name) do
     :gen_server.call(client, {:logon, pass, nick, user, name}, :infinity)
   end
   @doc """
   Determine if the provided client is logged on to a server
   """
   @spec is_logged_on?(client :: pid) :: true | false
   def is_logged_on?(client) do
     :gen_server.call(client, :is_logged_on?)
   end
   @doc """
   Send a message to a nick or channel
   Message types are:
     :privmsg
     :notice
     :ctcp
   """
   @spec msg(client :: pid, type :: atom, nick :: binary, msg :: binary) :: :ok | {:error, atom}
   def msg(client, type, nick, msg) do
     :gen_server.call(client, {:msg, type, nick, msg}, :infinity)
   end
   @doc """
   Change the client's nick
   """
   @spec nick(client :: pid, new_nick :: binary) :: :ok | {:error, atom}
   def nick(client, new_nick) do
     :gen_server.call(client, {:nick, new_nick}, :infinity)
   end
   @doc """
   Send a raw IRC command
   """
   @spec cmd(client :: pid, raw_cmd :: binary) :: :ok | {:error, atom}
   def cmd(client, raw_cmd) do
     :gen_server.call(client, {:cmd, raw_cmd})
   end
   @doc """
   Join a channel, with an optional password
   """
   @spec join(client :: pid, channel :: binary, key :: binary | nil) :: :ok | {:error, atom}
   def join(client, channel, key \\ "") do
     :gen_server.call(client, {:join, channel, key}, :infinity)
   end
   @doc """
   Leave a channel
   """
   @spec part(client :: pid, channel :: binary) :: :ok | {:error, atom}
   def part(client, channel) do
     :gen_server.call(client, {:part, channel}, :infinity)
   end
   @doc """
   Kick a user from a channel
   """
   @spec kick(client :: pid, channel :: binary, nick :: binary, message :: binary | nil) :: :ok | {:error, atom}
   def kick(client, channel, nick, message \\ "") do
     :gen_server.call(client, {:kick, channel, nick, message}, :infinity)
   end
   @doc """
   Change mode for a user or channel
   """
   @spec mode(client :: pid, channel_or_nick :: binary, flags :: binary, args :: binary | nil) :: :ok | {:error, atom}
   def mode(client, channel_or_nick, flags, args \\ "") do
     :gen_server.call(client, {:mode, channel_or_nick, flags, args}, :infinity)
   end
   @doc """
   Invite a user to a channel
   """
   @spec invite(client :: pid, nick :: binary, channel :: binary) :: :ok | {:error, atom}
   def invite(client, nick, channel) do
     :gen_server.call(client, {:invite, nick, channel}, :infinity)
   end
   @doc """
   Quit the server, with an optional part message
   """
   @spec quit(client :: pid, msg :: binary | nil) :: :ok | {:error, atom}
   def quit(client, msg \\ "Leaving..") do
     :gen_server.call(client, {:quit, msg}, :infinity)
   end
   @doc """
   Get details about each of the client's currently joined channels
   """
   @spec channels(client :: pid) :: list(binary) | [] | {:error, atom}
   def channels(client) do
     :gen_server.call(client, :channels)
   end
   @doc """
   Get a list of users in the provided channel
   """
   @spec channel_users(client :: pid, channel :: binary) :: list(binary) | [] | {:error, atom}
   def channel_users(client, channel) do
     :gen_server.call(client, {:channel_users, channel})
   end
   @doc """
   Get the topic of the provided channel
   """
   @spec channel_topic(client :: pid, channel :: binary) :: binary | {:error, atom}
   def channel_topic(client, channel) do
     :gen_server.call(client, {:channel_topic, channel})
   end
   @doc """
   Get the channel type of the provided channel
   """
   @spec channel_type(client :: pid, channel :: binary) :: atom | {:error, atom}
   def channel_type(client, channel) do
     :gen_server.call(client, {:channel_type, channel})
   end
   @doc """
   Determine if a nick is present in the provided channel
   """
   @spec channel_has_user?(client :: pid, channel :: binary, nick :: binary) :: true | false | {:error, atom}
   def channel_has_user?(client, channel, nick) do
     :gen_server.call(client, {:channel_has_user?, channel, nick})
   end
   @doc """
   Add a new event handler process
   """
   @spec add_handler(client :: pid, pid) :: :ok
   def add_handler(client, pid) do
     :gen_server.call(client, {:add_handler, pid})
   end
   @doc """
   Add a new event handler process, asynchronously
   """
   @spec add_handler_async(client :: pid, pid) :: :ok
   def add_handler_async(client, pid) do
     :gen_server.cast(client, {:add_handler, pid})
   end
   @doc """
   Remove an event handler process
   """
   @spec remove_handler(client :: pid, pid) :: :ok
   def remove_handler(client, pid) do
     :gen_server.call(client, {:remove_handler, pid})
   end
   @doc """
   Remove an event handler process, asynchronously
   """
   @spec remove_handler_async(client :: pid, pid) :: :ok
   def remove_handler_async(client, pid) do
     :gen_server.cast(client, {:remove_handler, pid})
   end
   @doc """
   Get the current state of the provided client
   """
   @spec state(client :: pid) :: [{atom, any}]
   def state(client) do
     state = :gen_server.call(client, :state)
     [server:            state.server,
      port:              state.port,
      nick:              state.nick,
      pass:              state.pass,
      user:              state.user,
      name:              state.name,
      autoping:          state.autoping,
      connected?:        state.connected?,
      logged_on?:        state.logged_on?,
      channel_prefixes:  state.channel_prefixes,
      user_prefixes:     state.user_prefixes,
      channels:          Channels.to_proplist(state.channels),
      network:           state.network,
      login_time:        state.login_time,
      debug?:            state.debug?,
      event_handlers:    state.event_handlers]
   end
 
   ###############
   # GenServer API
   ###############
 
   @doc """
   Called when :gen_server initializes the client
   """
   @spec init(list(any) | []) :: {:ok, ClientState.t}
   def init(options \\ []) do
     autoping = Keyword.get(options, :autoping, true)
     debug    = Keyword.get(options, :debug, false)
     # Add event handlers
     handlers = 
       Keyword.get(options, :event_handlers, []) 
       |> List.foldl([], &do_add_handler/2)
     # Return initial state
-    {:ok, ClientState[
+    {:ok, %ClientState{
       event_handlers: handlers,
       autoping:       autoping,
       logged_on?:     false,
       debug?:         debug,
-      channels:       ExIrc.Channels.init()]}
+      channels:       ExIrc.Channels.init()}}
   end
   @doc """
   Handle calls from the external API. It is not recommended to call these directly.
   """
   # Handle call to get the current state of the client process
   def handle_call(:state, _from, state), do: {:reply, state, state}
   # Handle call to stop the current client process
   def handle_call(:stop, _from, state) do
     # Ensure the socket connection is closed if stop is called while still connected to the server
     if state.connected?, do: :gen_tcp.close(state.socket)
-    {:stop, :normal, :ok, state.update [connected?: false, logged_on?: false, socket: nil]}
+    {:stop, :normal, :ok, %{state | :connected? => false, :logged_on? => false, :socket => nil}}
   end
   # Handles call to add a new event handler process
   def handle_call({:add_handler, pid}, _from, state) do
     handlers = do_add_handler(pid, state.event_handlers)
-    {:reply, :ok, state.update [event_handlers: handlers]}
+    {:reply, :ok, %{state | :event_handlers => handlers}}
   end
   # Handles call to remove an event handler process
   def handle_call({:remove_handler, pid}, _from, state) do
     handlers = do_remove_handler(pid, state.event_handlers)
-    {:reply, :ok, state.update [event_handlers: handlers]}
+    {:reply, :ok, %{state | :event_handlers => handlers}}
   end
   # Handle call to connect to an IRC server
   def handle_call({:connect, server, port}, _from, state) do
     # If there is an open connection already, close it.
     if state.socket != nil, do: :gen_tcp.close(state.socket)
     # Open a new connection
-    case :gen_tcp.connect(to_char_list!(server), port, [:list, {:packet, :line}, {:keepalive, true}]) do
+    case :gen_tcp.connect(List.from_char_data!(server), port, [:list, {:packet, :line}, {:keepalive, true}]) do
       {:ok, socket} ->
         send_event {:connected, server, port}, state
-        {:reply, :ok, state.connected?(true).server(server).port(port).socket(socket)}
+        {:reply, :ok, %{state | :connected? => true, :server => server, :port => port, :socket => socket}}
       error ->
         {:reply, error, state}
     end
   end
   # Handle call to determine if the client is connected
   def handle_call(:is_connected?, _from, state), do: {:reply, state.connected?, state}
   # Prevents any of the following messages from being handled if the client is not connected to a server.
   # Instead, it returns {:error, :not_connected}.
-  def handle_call(_, _from, ClientState[connected?: false] = state), do: {:reply, {:error, :not_connected}, state}
+  def handle_call(_, _from, %ClientState{:connected? => false} = state), do: {:reply, {:error, :not_connected}, state}
   # Handle call to login to the connected IRC server
-  def handle_call({:logon, pass, nick, user, name}, _from, ClientState[logged_on?: false] = state) do
+  def handle_call({:logon, pass, nick, user, name}, _from, %ClientState{:logged_on? => false} = state) do
     send! state.socket, pass!(pass)
     send! state.socket, nick!(nick)
     send! state.socket, user!(user, name)
-    {:reply, :ok, state.update [pass: pass, nick: nick, user: user, name: name] }
+    {:reply, :ok, %{state | :pass => pass, :nick => nick, :user => user, :name => name} }
   end
   # Handle call to determine if client is logged on to a server
   def handle_call(:is_logged_on?, _from, state), do: {:reply, state.logged_on?, state}
   # Prevents any of the following messages from being handled if the client is not logged on to a server.
   # Instead, it returns {:error, :not_logged_in}.
-  def handle_call(_, _from, ClientState[logged_on?: false] = state), do: {:reply, {:error, :not_logged_in}, state}
+  def handle_call(_, _from, %ClientState{:logged_on? => false} = state), do: {:reply, {:error, :not_logged_in}, state}
   # Handles call to send a message
   def handle_call({:msg, type, nick, msg}, _from, state) do
     data = case type do
       :privmsg -> privmsg!(nick, msg)
       :notice  -> notice!(nick, msg)
       :ctcp    -> notice!(nick, ctcp!(msg))
     end
     send! state.socket, data
     {:reply, :ok, state}
   end
   # Handles call to join a channel
   def handle_call({:join, channel, key}, _from, state)      do send!(state.socket, join!(channel, key)); {:reply, :ok, state} end
   # Handles a call to leave a channel
   def handle_call({:part, channel}, _from, state)           do send!(state.socket, part!(channel)); {:reply, :ok, state} end
   # Handles a call to kick a client
   def handle_call({:kick, channel, nick, message}, _from, state) do
     send!(state.socket, kick!(channel, nick, message))
     {:reply, :ok, state}
   end
   # Handles a call to change mode for a user or channel
   def handle_call({:mode, channel_or_nick, flags, args}, _from, state) do
     send!(state.socket, mode!(channel_or_nick, flags, args))
     {:reply, :ok, state}
   end
   # Handle call to invite a user to a channel
   def handle_call({:invite, nick, channel}, _from, state) do
     send!(state.socket, invite!(nick, channel))
     {:reply, :ok, state}
   end
   # Handle call to quit the server and close the socket connection
   def handle_call({:quit, msg}, _from, state) do
     if state.connected? do
       send! state.socket, quit!(msg)
       send_event :disconnected, state
       :gen_tcp.close state.socket
     end
-    {:reply, :ok, state.update [connected?: false, logged_on?: false, socket: nil]}
+    {:reply, :ok, %{state | :connected? => false, :logged_on? => false, :socket => nil}}
   end
   # Handles call to change the client's nick
   def handle_call({:nick, new_nick}, _from, state) do send!(state.socket, nick!(new_nick)); {:reply, :ok, state} end
   # Handles call to send a raw command to the IRC server
   def handle_call({:cmd, raw_cmd}, _from, state) do send!(state.socket, command!(raw_cmd)); {:reply, :ok, state} end
   # Handles call to return the client's channel data
   def handle_call(:channels, _from, state), do: {:reply, Channels.channels(state.channels), state}
   # Handles call to return a list of users for a given channel
   def handle_call({:channel_users, channel}, _from, state), do: {:reply, Channels.channel_users(state.channels, channel), state}
   # Handles call to return the given channel's topic
   def handle_call({:channel_topic, channel}, _from, state), do: {:reply, Channels.channel_topic(state.channels, channel), state}
   # Handles call to return the type of the given channel
   def handle_call({:channel_type, channel}, _from, state), do: {:reply, Channels.channel_type(state.channels, channel), state}
   # Handles call to determine if a nick is present in the given channel
   def handle_call({:channel_has_user?, channel, nick}, _from, state) do
     {:reply, Channels.channel_has_user?(state.channels, channel, nick), state}
   end
   # Handles message to add a new event handler process asynchronously
   def handle_cast({:add_handler, pid}, state) do
     handlers = do_add_handler(pid, state.event_handlers)
-    {:noreply, state.update [event_handlers: handlers]}
+    {:noreply, %{state | :event_handlers => handlers}}
   end
   @doc """
   Handles asynchronous messages from the external API. Not recommended to call these directly.
   """
   # Handles message to remove an event handler process asynchronously
   def handle_cast({:remove_handler, pid}, state) do
     handlers = do_remove_handler(pid, state.event_handlers)
-    {:noreply, state.update [event_handlers: handlers]}
+    {:noreply, %{state | :event_handlers => handlers}}
   end
   @doc """
   Handle messages from the TCP socket connection.
   """
   # Handles the client's socket connection 'closed' event
-  def handle_info({:tcp_closed, _socket}, ClientState[server: server, port: port] = state) do
+  def handle_info({:tcp_closed, _socket}, %ClientState{:server => server, :port => port} = state) do
     info "Connection to #{server}:#{port} closed!"
     send_event :disconnected, state
-    new_state = state.update [
-      socket:     nil,
-      connected?: false,
-      logged_on?: false,
-      channels:   Channels.init()
-    ]
+    new_state = %{state |
+      :socket =>     nil,
+      :connected? => false,
+      :logged_on? => false,
+      :channels =>   Channels.init()
+    }
     {:noreply, new_state}
   end
   # Handles any TCP errors in the client's socket connection
-  def handle_info({:tcp_error, socket, reason}, ClientState[server: server, port: port] = state) do
+  def handle_info({:tcp_error, socket, reason}, %ClientState{:server => server, :port => port} = state) do
     error "TCP error in connection to #{server}:#{port}:\r\n#{reason}\r\nClient connection closed."
-    new_state = state.update [
-      socket: nil,
-      connected?: false,
-      logged_on?: false,
-      channels: Channels.init()
-    ]
+    new_state = %{state |
+      :socket =>     nil,
+      :connected? => false,
+      :logged_on? => false,
+      :channels =>   Channels.init()
+    }
     {:stop, {:tcp_error, socket}, new_state}
   end
   # General handler for messages from the IRC server
   def handle_info({:tcp, _, data}, state) do
     debug? = state.debug?
     case Utils.parse(data) do
-      IrcMessage[ctcp: true] = msg ->
+      %IrcMessage{:ctcp => true} = msg ->
         send_event msg, state
         {:noreply, state}
-      IrcMessage[ctcp: false] = msg ->
+      %IrcMessage{:ctcp => false} = msg ->
         send_event msg, state
         handle_data msg, state
-      IrcMessage[ctcp: :invalid] = msg when debug? ->
+      %IrcMessage{:ctcp => :invalid} = msg when debug? ->
         send_event msg, state
         {:noreply, state}
       _ ->
         {:noreply, state}
     end
   end
   # If an event handler process dies, remove it from the list of event handlers
   def handle_info({'DOWN', _, _, pid, _}, state) do
     handlers = do_remove_handler(pid, state.event_handlers)
-    {:noreply, state.update [event_handlers: handlers]}
+    {:noreply, %{state | :event_handlers => handlers}}
   end
   # Catch-all for unrecognized messages (do nothing)
   def handle_info(_, state) do
     {:noreply, state}
   end
   @doc """
   Handle termination
   """
   def terminate(_reason, state) do
     if state.socket != nil do
       :gen_tcp.close state.socket
-      state.update [socket: nil]
+      %{state | :socket => nil}
     end
     :ok
   end
   @doc """
   Transform state for hot upgrades/downgrades
   """
   def code_change(_old, state, _extra), do: {:ok, state}
 
   ################
   # Data handling
   ################
 
   @doc """
   Handle IrcMessages received from the server.
   """
   # Called upon successful login
-  def handle_data(IrcMessage[cmd: @rpl_welcome] = _msg, ClientState[logged_on?: false] = state) do
+  def handle_data(%IrcMessage{:cmd => @rpl_welcome}, %ClientState{:logged_on? => false} = state) do
     if state.debug?, do: debug "SUCCESFULLY LOGGED ON"
-    new_state = state.update [logged_on?: true, login_time: :erlang.now()]
+    new_state = %{state | :logged_on? => true, :login_time => :erlang.now()}
     send_event :logged_in, new_state
     {:noreply, new_state}
   end
   # Called when the server sends it's current capabilities
-  def handle_data(IrcMessage[cmd: @rpl_isupport] = msg, state) do
+  def handle_data(%IrcMessage{:cmd => @rpl_isupport} = msg, state) do
     if state.debug?, do: debug "RECEIVING SERVER CAPABILITIES"
     {:noreply, Utils.isup(msg.args, state)}
   end
   # Called when the client enters a channel
-  def handle_data(IrcMessage[nick: nick, cmd: "JOIN"] = msg, ClientState[nick: nick] = state) do
+  def handle_data(%IrcMessage{:nick => nick, :cmd => "JOIN"} = msg, %ClientState{:nick => nick} = state) do
     channel = msg.args |> List.first
     if state.debug?, do: debug "JOINED A CHANNEL #{channel}"
     channels  = Channels.join(state.channels, channel)
-    new_state = state.update [channels: channels]
+    new_state = %{state | :channels => channels}
     send_event {:joined, channel}, new_state
-    {:noreply, state.channels(channels)}
+    {:noreply, new_state}
   end
   # Called when another user joins a channel the client is in
-  def handle_data(IrcMessage[nick: user_nick, cmd: "JOIN"] = msg, state) do
+  def handle_data(%IrcMessage{:nick => user_nick, :cmd => "JOIN"} = msg, state) do
     channel = msg.args |> List.first
     if state.debug?, do: debug "ANOTHER USER JOINED A CHANNEL: #{channel} - #{user_nick}"
     channels  = Channels.user_join(state.channels, channel, user_nick)
-    new_state = state.update [channels: channels]
+    new_state = %{state | :channels => channels}
     send_event {:joined, channel, user_nick}, new_state
-    {:noreply, state.channels(channels)}
+    {:noreply, new_state}
   end
   # Called on joining a channel, to tell us the channel topic
   # Message with three arguments is not RFC compliant but very common
   # Message with two arguments is RFC compliant
-  def handle_data(IrcMessage[cmd: @rpl_topic] = msg, state) do
+  def handle_data(%IrcMessage{:cmd => @rpl_topic} = msg, state) do
     if state.debug?, do: debug "INITIAL TOPIC MSG"
     {channel, topic} = case msg.args do
       [_nick, channel, topic] -> debug("1. TOPIC SET FOR #{channel} TO #{topic}"); {channel, topic}
       [channel, topic]        -> debug("2. TOPIC SET FOR #{channel} TO #{topic}"); {channel, topic}
     end
     channels  = Channels.set_topic(state.channels, channel, topic)
-    new_state = state.update [channels: channels]
+    new_state = %{state | :channels => channels}
     send_event {:topic_changed, channel, topic}, new_state
     {:noreply, new_state}
   end
   # Called when the topic changes while we're in the channel
-  def handle_data(IrcMessage[cmd: "TOPIC", args: [channel, topic]], state) do
+  def handle_data(%IrcMessage{:cmd => "TOPIC", :args => [channel, topic]}, state) do
     if state.debug?, do: debug "TOPIC CHANGED FOR #{channel} TO #{topic}"
     channels  = Channels.set_topic(state.channels, channel, topic)
-    new_state = state.update [channels: channels]
+    new_state = %{state | :channels => channels}
     send_event {:topic_changed, channel, topic}, new_state
-    {:noreply, state.channels(channels)}
+    {:noreply, new_state}
   end
   # Called when joining a channel with the list of current users in that channel, or when the NAMES command is sent
-  def handle_data(IrcMessage[cmd: @rpl_namereply] = msg, state) do
+  def handle_data(%IrcMessage{:cmd => @rpl_namereply} = msg, state) do
     if state.debug?, do: debug "NAMES LIST RECEIVED"
     {_nick, channel_type, channel, names} = case msg.args do
       [nick, channel_type, channel, names]  -> {nick, channel_type, channel, names}
       [channel_type, channel, names]        -> {nil, channel_type, channel, names}
     end
     channels = Channels.set_type(
       Channels.users_join(state.channels, channel, String.split(names, " ", trim: true)),
       channel,
       channel_type)
-    {:noreply, state.update [channels: channels]}
+    {:noreply, %{state | :channels => channels}}
   end
   # Called when our nick has succesfully changed
-  def handle_data(IrcMessage[cmd: "NICK", nick: nick, args: [new_nick]], ClientState[nick: nick] = state) do
+  def handle_data(%IrcMessage{:cmd => "NICK", :nick => nick, :args => [new_nick]}, %ClientState{:nick => nick} = state) do
     if state.debug?, do: debug "NICK CHANGED FROM #{nick} TO #{new_nick}"
-    new_state = state.update [nick: new_nick]
+    new_state = %{state | :nick => new_nick}
     send_event {:nick_changed, new_nick}, new_state
     {:noreply, new_state}
   end
   # Called when someone visible to us changes their nick
-  def handle_data(IrcMessage[cmd: "NICK", nick: nick, args: [new_nick]], state) do
+  def handle_data(%IrcMessage{:cmd => "NICK", :nick => nick, :args => [new_nick]}, state) do
     if state.debug?, do: debug "#{nick} CHANGED THEIR NICK TO #{new_nick}"
     channels  = Channels.user_rename(state.channels, nick, new_nick)
-    new_state = state.update [channels: channels]
+    new_state = %{state | :channels => channels}
     send_event {:nick_changed, nick, new_nick}, new_state
     {:noreply, new_state}
   end
   # Called when we leave a channel
-  def handle_data(IrcMessage[cmd: "PART", nick: nick] = msg, ClientState[nick: nick] = state) do
+  def handle_data(%IrcMessage{:cmd => "PART", :nick => nick} = msg, %ClientState{:nick => nick} = state) do
     channel = msg.args |> List.first
     if state.debug?, do: debug "WE LEFT A CHANNEL: #{channel}"
     channels  = Channels.part(state.channels, channel)
-    new_state = state.update [channels: channels]
+    new_state = %{state | :channels => channels}
     send_event {:parted, channel}, new_state
     {:noreply, new_state}
   end
   # Called when someone else in our channel leaves
-  def handle_data(IrcMessage[cmd: "PART", nick: user_nick] = msg, state) do
+  def handle_data(%IrcMessage{:cmd => "PART", :nick => user_nick} = msg, state) do
     channel = msg.args |> List.first
     if state.debug?, do: debug "#{user_nick} LEFT A CHANNEL: #{channel}"
     channels  = Channels.user_part(state.channels, channel, user_nick)
-    new_state = state.update [channels: channels]
+    new_state = %{state | :channels => channels}
     send_event {:parted, channel, user_nick}, new_state
     {:noreply, new_state}
   end
   # Called when we receive a PING
-  def handle_data(IrcMessage[cmd: "PING"] = msg, ClientState[autoping: true] = state) do
+  def handle_data(%IrcMessage{:cmd => "PING"} = msg, %ClientState{:autoping => true} = state) do
     if state.debug?, do: debug "RECEIVED A PING!"
     case msg do
-      IrcMessage[args: [from]] -> debug("SENT PONG2"); send!(state.socket, pong2!(state.nick, from))
-                             _ -> debug("SENT PONG1"); send!(state.socket, pong1!(state.nick))
+      %IrcMessage{:args => [from]} ->
+        debug("SENT PONG2"); send!(state.socket, pong2!(state.nick, from))
+      _ ->
+        debug("SENT PONG1"); send!(state.socket, pong1!(state.nick))
     end
     {:noreply, state};
   end
   # Called when we are invited to a channel
-  def handle_data(IrcMessage[cmd: "INVITE", args: [nick, channel], nick: by] = msg, ClientState[nick: nick] = state) do
+  def handle_data(%IrcMessage{:cmd => "INVITE", :args => [nick, channel], :nick => by} = msg, %ClientState{:nick => nick} = state) do
     if state.debug?, do: debug "RECEIVED AN INVITE: #{msg.args |> Enum.join(" ")}"
     send_event {:invited, by, channel}, state
     {:noreply, state}
   end
   # Called when we are kicked from a channel
-  def handle_data(IrcMessage[cmd: "KICK", args: [channel, nick], nick: by] = _msg, ClientState[nick: nick] = state) do
+  def handle_data(%IrcMessage{:cmd => "KICK", :args => [channel, nick], :nick => by} = _msg, %ClientState{:nick => nick} = state) do
     if state.debug?, do: debug "WE WERE KICKED FROM #{channel} BY #{by}"
     send_event {:kicked, by, channel}, state
     {:noreply, state}
   end
   # Called when someone else was kicked from a channel
-  def handle_data(IrcMessage[cmd: "KICK", args: [channel, nick], nick: by] = _msg, state) do
+  def handle_data(%IrcMessage{:cmd => "KICK", :args => [channel, nick], :nick => by} = _msg, state) do
     if state.debug?, do: debug "#{nick} WAS KICKED FROM #{channel} BY #{by}"
     send_event {:kicked, nick, by, channel}, state
     {:noreply, state}
   end
   # Called when someone sends us a message
-  def handle_data(IrcMessage[nick: from, cmd: "PRIVMSG", args: [nick, message]] = _msg, ClientState[nick: nick] = state) do
+  def handle_data(%IrcMessage{:nick => from, :cmd => "PRIVMSG", :args => [nick, message]} = _msg, %ClientState{:nick => nick} = state) do
     if state.debug?, do: debug "#{from} SENT US #{message}"
     send_event {:received, message, from}, state
     {:noreply, state}
   end
   # Called when someone sends a message to a channel we're in, or a list of users
-  def handle_data(IrcMessage[nick: from, cmd: "PRIVMSG", args: [to, message]] = _msg, ClientState[nick: nick] = state) do
+  def handle_data(%IrcMessage{:nick => from, :cmd => "PRIVMSG", :args => [to, message]} = _msg, %ClientState{:nick => nick} = state) do
     if state.debug?, do: debug "#{from} SENT #{message} TO #{to}"
     case String.contains?(to, nick) do
       # Treat it like a private message if message was sent to a list of users that includes us
       true -> send_event {:received, message, from}, state
       # Otherwise it was just a channel message
       _ ->
         send_event {:received, message, from, to}, state
         # If we were mentioned, fire that event as well
         if String.contains?(message, nick), do: send_event({:mentioned, message, from, to}, state)
     end
     {:noreply, state}
   end
   # Called any time we receive an unrecognized message
   def handle_data(msg, state) do
     if state.debug? do debug "UNRECOGNIZED MSG: #{msg.cmd}"; IO.inspect(msg) end
     {:noreply, state}
   end
 
   ###############
   # Internal API
   ###############
-  defp send_event(msg, ClientState[event_handlers: handlers]) when is_list(handlers) do
+  defp send_event(msg, %ClientState{:event_handlers => handlers}) when is_list(handlers) do
     Enum.each(handlers, fn({pid, _}) -> Kernel.send(pid, msg) end)
   end
 
   defp do_add_handler(pid, handlers) do
     case Process.alive?(pid) and not Enum.member?(handlers, pid) do
       true ->
         ref = Process.monitor(pid)
         [{pid, ref} | handlers]
       false ->
         handlers
     end
   end
 
   defp do_remove_handler(pid, handlers) do
     case List.keyfind(handlers, pid, 0) do
       {pid, ref} ->
         Process.demonitor(ref)
         List.keydelete(handlers, pid, 0)
       nil ->
           handlers
     end
   end
 
   defp debug(msg) do
     IO.puts(IO.ANSI.green() <> msg <> IO.ANSI.reset())
   end
 
 end
diff --git a/lib/exirc/commands.ex b/lib/exirc/commands.ex
index f66da2b..c3e5b25 100644
--- a/lib/exirc/commands.ex
+++ b/lib/exirc/commands.ex
@@ -1,262 +1,262 @@
 defmodule Irc.Commands do
   @moduledoc """
   Defines IRC command constants, and methods for generating valid commands to send to an IRC server.
   """
 
   defmacro __using__(_) do
 
     quote do
       import Irc.Commands
 
       ####################
       # IRC Numeric Codes
       ####################
 
       @rpl_welcome "001"
       @rpl_yourhost "002"
       @rpl_created "003"
       @rpl_myinfo "004"
       @rpl_isupport "005" # Defacto standard for server support
       @rpl_bounce "010"   # Defacto replacement of "005" in RFC2812
       @rpl_statsdline "250"
       #@doc """
       #":There are <integer> users and <integer> invisible on <integer> servers"
       #"""
       @rpl_luserclient "251"
       #@doc """
       # "<integer> :operator(s) online"
       #"""
       @rpl_luserop "252"
       #@doc """
       #"<integer> :unknown connection(s)"
       #"""
       @rpl_luserunknown "253"
       #@doc """
       #"<integer> :channels formed"
       #"""
       @rpl_luserchannels "254"
       #@doc """
       #":I have <integer> clients and <integer> servers"
       #"""
       @rpl_luserme "255"
       #@doc """
       #Local/Global user stats
       #"""
       @rpl_localusers "265"
       @rpl_globalusers "266"
       #@doc """
       #When sending a TOPIC message to determine the channel topic, 
       #one of two replies is sent. If the topic is set, RPL_TOPIC is sent back else
       #RPL_NOTOPIC.
       #"""
       @rpl_notopic "331"
       @rpl_topic "332"
       #@doc """
       #To reply to a NAMES message, a reply pair consisting
       #of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
       #server back to the client. If there is no channel
       #found as in the query, then only RPL_ENDOFNAMES is
       #returned. The exception to this is when a NAMES
       #message is sent with no parameters and all visible
       #channels and contents are sent back in a series of
       #RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
       #the end.
 
       #Format: "<channel> :[[@|+]<nick> [[@|+]<nick> [...]]]"
       #"""
       @rpl_namereply "353"
       @rpl_endofnames "366"
       #@doc """
       #When responding to the MOTD message and the MOTD file
       #is found, the file is displayed line by line, with
       #each line no longer than 80 characters, using
       #RPL_MOTD format replies. These should be surrounded
       #by a RPL_MOTDSTART (before the RPL_MOTDs) and an
       #RPL_ENDOFMOTD (after).
       #"""
       @rpl_motd "372"
       @rpl_motdstart "375"
       @rpl_endofmotd "376"
 
       ################
       # Error Codes
       ################
 
       #@doc """
       #Used to indicate the nickname parameter supplied to a command is currently unused.
       #"""
       @err_no_such_nick "401"
       #@doc """
       #Used to indicate the server name given currently doesn"t exist.
       #"""
       @err_no_such_server "402"
       #@doc """
       #Used to indicate the given channel name is invalid.
       #"""
       @err_no_such_channel "403"
       #@doc """
       #Sent to a user who is either (a) not on a channel which is mode +n or (b),
       #not a chanop (or mode +v) on a channel which has mode +m set, and is trying 
       #to send a PRIVMSG message to that channel.
       #"""
       @err_cannot_send_to_chan "404"
       #@doc """
       #Sent to a user when they have joined the maximum number of allowed channels 
       #and they try to join another channel.
       #"""
       @err_too_many_channels "405"
       #@doc """
       #Returned to a registered client to indicate that the command sent is unknown by the server.
       #"""
       @err_unknown_command "421"
       #@doc """
       #Returned when a nickname parameter expected for a command and isn"t found.
       #"""
       @err_no_nickname_given "431"
       #@doc """
       #Returned after receiving a NICK message which contains characters which do not fall in the defined set.
       #"""
       @err_erroneus_nickname "432"
       #@doc """
       #Returned when a NICK message is processed that results in an attempt to 
       #change to a currently existing nickname.
       #"""
       @err_nickname_in_use "433"
       #@doc """
       #Returned by a server to a client when it detects a nickname collision
       #(registered of a NICK that already exists by another server).
       #"""
       @err_nick_collision "436"
       #@doc """
       #"""
       @err_unavail_resource "437"
       #@doc """
       #Returned by the server to indicate that the client must be registered before 
       #the server will allow it to be parsed in detail.
       #"""
       @err_not_registered "451"
       #"""
       # Returned by the server by numerous commands to indicate to the client that 
       # it didn"t supply enough parameters.
       #"""
       @err_need_more_params "461"
       #@doc """
       #Returned by the server to any link which tries to change part of the registered 
       #details (such as password or user details from second USER message).
       #"""
       @err_already_registered "462"
       #@doc """
       #Returned by the server to the client when the issued command is restricted
       #"""
       @err_restricted "484"
 
       ###############
       # Code groups
       ###############
 
       @logon_errors [ @err_no_nickname_given,   @err_erroneus_nickname,
                       @err_nickname_in_use,     @err_nick_collision,
                       @err_unavail_resource,    @err_need_more_params,
                       @err_already_registered,  @err_restricted ]
     end
 
   end
 
   ############
   # Helpers
   ############
 
   @doc """
   Send data to a TCP socket.
 
   Example:
 
       command = pass! "password"
       send! socket, command
   """
   def send!(socket, data) do
     :gen_tcp.send(socket, data)
   end
 
   @doc """
   Builds a valid IRC command.
   """
   def command!(cmd) when is_list(cmd), do: [cmd, '\r\n']
-  def command!(cmd) when is_binary(cmd), do: command! String.to_char_list!(cmd)
+  def command!(cmd) when is_binary(cmd), do: command! List.from_char_data!(cmd)
   @doc """
   Builds a valid CTCP command.
   """
   def ctcp!(cmd), do: [1, '#{cmd}', 1]
 
   # IRC Commands
 
   @doc """
   Send password to server
   """
   def pass!(pwd), do: command! ['PASS ', '#{pwd}']
   @doc """
   Send nick to server. (Changes or sets your nick)
   """
   def nick!(nick), do: command! ['NICK ', '#{nick}']
   @doc """
   Send username to server. (Changes or sets your username)
   """
   def user!(user, name) do
     command! ['USER ', '#{user}', ' 0 * :', '#{name}']
   end
   @doc """
   Send PONG in response to PING
   """
   def pong1!(nick), do: command! ['PONG ', '#{nick}']
   @doc """
   Send a targeted PONG in response to PING
   """
   def pong2!(nick, to), do: command! ['PONG ', '#{nick}', ' ', '#{to}']
   @doc """
   Send message to channel or user
   """
   def privmsg!(nick, msg), do: command! ['PRIVMSG ', '#{nick}', ' :', '#{msg}']
   @doc """
   Send notice to channel or user
   """
   def notice!(nick, msg), do: command! ['NOTICE ', '#{nick}', ' :', '#{msg}']
   @doc """
   Send join command to server (join a channel)
   """
   def join!(channel, key \\ ""), do: command! ['JOIN ', '#{channel}', ' ', '#{key}']
   @doc """
   Send part command to server (leave a channel)
   """
   def part!(channel), do: command! ['PART ', '#{channel}']
   @doc """
   Send quit command to server (disconnect from server)
   """
   def quit!(msg \\ "Leaving"), do: command! ['QUIT :', '#{msg}']
   @doc """
   Send kick command to server
   """
   def kick!(channel, nick, message \\ "") do
     case "#{message}" |> String.length do
       0 -> command! ['KICK ', '#{channel}', ' ', '#{nick}']
       _ -> command! ['KICK ', '#{channel}', ' ', '#{nick}', ' ', '#{message}']
     end
   end
   @doc """
   Send mode command to server
   MODE <nick> <flags>
   MODE <channel> <flags> [<args>]
   """
   def mode!(channel_or_nick, flags, args \\ "") do
     case "#{args}" |> String.length do
       0 -> command! ['MODE ', '#{channel_or_nick}', ' ', '#{flags}']
       _ -> command! ['MODE ', '#{channel_or_nick}', ' ', '#{flags}', ' ', '#{args}']
     end
   end
   @doc """
   Send an invite command
   """
   def invite!(nick, channel) do
     command! ['INVITE ', '#{nick}', ' ', '#{channel}']
   end
 
 end
diff --git a/lib/exirc/example_handler.ex b/lib/exirc/example_handler.ex
index 24b4852..bd3f142 100644
--- a/lib/exirc/example_handler.ex
+++ b/lib/exirc/example_handler.ex
@@ -1,121 +1,121 @@
 defmodule ExampleHandler do
 	@moduledoc """
 	This is an example event handler that you can attach to the client using
 	`add_handler` or `add_handler_async`. To remove, call `remove_handler` or
 	`remove_handler_async` with the pid of the handler process.
 	"""
 	def start! do
 		start_link([])
 	end
 
 	def start_link(_) do
 	    :gen_server.start_link(__MODULE__, nil, [])
 	end
 
 	def init(_) do
 		{:ok, nil}
 	end
 
 	@doc """
 	Handle messages from the client
 
 	Examples:
 
 		def handle_info({:connected, server, port}, _state) do
 			IO.puts "Connected to \#{server}:\#{port}"
 		end
 		def handle_info(:logged_in, _state) do
 			IO.puts "Logged in!"
 		end
-		def handle_info(IrcMessage[nick: from, cmd: "PRIVMSG", args: ["mynick", msg]], _state) do
+		def handle_info(%IrcMessage{:nick => from, :cmd => "PRIVMSG", :args => ["mynick", msg]}, _state) do
 			IO.puts "Received a private message from \#{from}: \#{msg}"
 		end
-		def handle_info(IrcMessage[nick: from, cmd: "PRIVMSG", args: [to, msg]], _state) do
+		def handle_info(%IrcMessage{:nick => from, :cmd => "PRIVMSG", :args => [to, msg]}, _state) do
 			IO.puts "Received a message in \#{to} from \#{from}: \#{msg}"
 		end
 
 	"""
 	def handle_info({:connected, server, port}, _state) do
 		debug "Connected to #{server}:#{port}"
 		{:noreply, nil}
 	end
 	def handle_info(:logged_in, _state) do
 		debug "Logged in to server"
 		{:noreply, nil}
 	end
 	def handle_info(:disconnected, _state) do
 		debug "Disconnected from server"
 		{:noreply, nil}
 	end
 	def handle_info({:joined, channel}, _state) do
 		debug "Joined #{channel}"
 		{:noreply, nil}
 	end
 	def handle_info({:joined, channel, user}, _state) do
 		debug "#{user} joined #{channel}"
 		{:noreply, nil}
 	end
 	def handle_info({:topic_changed, channel, topic}, _state) do
 		debug "#{channel} topic changed to #{topic}"
 		{:noreply, nil}
 	end
 	def handle_info({:nick_changed, nick}, _state) do
 		debug "We changed our nick to #{nick}"
 		{:noreply, nil}
 	end
 	def handle_info({:nick_changed, old_nick, new_nick}, _state) do
 		debug "#{old_nick} changed their nick to #{new_nick}"
 		{:noreply, nil}
 	end
 	def handle_info({:parted, channel}, _state) do
 		debug "We left #{channel}"
 		{:noreply, nil}
 	end
 	def handle_info({:parted, channel, nick}, _state) do
 		debug "#{nick} left #{channel}"
 		{:noreply, nil}
 	end
 	def handle_info({:invited, by, channel}, _state) do
 		debug "#{by} invited us to #{channel}"
 		{:noreply, nil}
 	end
 	def handle_info({:kicked, by, channel}, _state) do
 		debug "We were kicked from #{channel} by #{by}"
 		{:noreply, nil}
 	end
 	def handle_info({:kicked, nick, by, channel}, _state) do
 		debug "#{nick} was kicked from #{channel} by #{by}"
 		{:noreply, nil}
 	end
 	def handle_info({:received, message, from}, _state) do
 		debug "#{from} sent us a private message: #{message}"
 		{:noreply, nil}
 	end
 	def handle_info({:received, message, from, channel}, _state) do
 		debug "#{from} sent a message to #{channel}: #{message}"
 		{:noreply, nil}
 	end
 	def handle_info({:mentioned, message, from, channel}, _state) do
 		debug "#{from} mentioned us in #{channel}: #{message}"
 		{:noreply, nil}
 	end
 	# This is an example of how you can manually catch commands if ExIrc.Client doesn't send a specific message for it
-	def handle_info(IrcMessage[nick: from, cmd: "PRIVMSG", args: ["testnick", msg]], _state) do
+	def handle_info(%IrcMessage{:nick => from, :cmd => "PRIVMSG", :args => ["testnick", msg]}, _state) do
 		debug "Received a private message from #{from}: #{msg}"
 		{:noreply, nil}
 	end
-	def handle_info(IrcMessage[nick: from, cmd: "PRIVMSG", args: [to, msg]], _state) do
+	def handle_info(%IrcMessage{:nick => from, :cmd => "PRIVMSG", :args => [to, msg]}, _state) do
 		debug "Received a message in #{to} from #{from}: #{msg}"
 		{:noreply, nil}
 	end
 	# Catch-all for messages you don't care about
 	def handle_info(msg, _state) do
 		debug "Received IrcMessage:"
 		IO.inspect msg
 		{:noreply, nil}
 	end
 
 	defp debug(msg) do
 		IO.puts IO.ANSI.yellow() <> msg <> IO.ANSI.reset()
 	end
 end
\ No newline at end of file
diff --git a/lib/exirc/irc_message.ex b/lib/exirc/irc_message.ex
index 4023463..ad865fc 100644
--- a/lib/exirc/irc_message.ex
+++ b/lib/exirc/irc_message.ex
@@ -1,8 +1,9 @@
-defrecord IrcMessage,
-  server:  '',
-  nick:    '',
-  user:    '',
-  host:    '',
-  ctcp:    nil,
-  cmd:     '',
-  args:    []
+defmodule IrcMessage do
+  defstruct server:  '',
+            nick:    '',
+            user:    '',
+            host:    '',
+            ctcp:    nil,
+            cmd:     '',
+            args:    []
+end
diff --git a/lib/exirc/logger.ex b/lib/exirc/logger.ex
index 6c8f301..7a6817b 100644
--- a/lib/exirc/logger.ex
+++ b/lib/exirc/logger.ex
@@ -1,29 +1,29 @@
 defmodule ExIrc.Logger do
   @moduledoc """
   A simple abstraction of :error_logger
   """
 
   @doc """
   Log an informational message report
   """
   @spec info(binary) :: :ok
   def info(msg) do
-    :error_logger.info_report String.to_char_list!(msg)
+    :error_logger.info_report List.from_char_data!(msg)
   end
 
   @doc """
   Log a warning message report
   """
   @spec warning(binary) :: :ok
   def warning(msg) do
-    :error_logger.warning_report String.to_char_list!("#{IO.ANSI.yellow()}#{msg}#{IO.ANSI.reset()}")
+    :error_logger.warning_report List.from_char_data!("#{IO.ANSI.yellow()}#{msg}#{IO.ANSI.reset()}")
   end
 
   @doc """
   Log an error message report
   """
   @spec error(binary) :: :ok
   def error(msg) do
-    :error_logger.error_report String.to_char_list!("#{IO.ANSI.red()}#{msg}#{IO.ANSI.reset()}")
+    :error_logger.error_report List.from_char_data!("#{IO.ANSI.red()}#{msg}#{IO.ANSI.reset()}")
   end
 end
\ No newline at end of file
diff --git a/lib/exirc/utils.ex b/lib/exirc/utils.ex
index 01d8b68..45e6c76 100644
--- a/lib/exirc/utils.ex
+++ b/lib/exirc/utils.ex
@@ -1,171 +1,171 @@
 defmodule ExIrc.Utils do
 
-  import String, only: [from_char_list!: 1]
+  import String, only: [from_char_data!: 1]
 
   ######################
   # IRC Message Parsing
   ######################
 
   @doc """
   Parse an IRC message
 
   Example:
 
       data    = ':irc.example.org 005 nick NETWORK=Freenode PREFIX=(ov)@+ CHANTYPES=#&'
       message = ExIrc.Utils.parse data
       assert "irc.example.org" = message.server
   """
   @spec parse(raw_data :: char_list) :: IrcMessage.t
   def parse(raw_data) do
     data = :string.substr(raw_data, 1, length(raw_data))
     case data do
       [?:|_] ->
           [[?:|from]|rest] = :string.tokens(data, ' ')
-          get_cmd(rest, parse_from(from, IrcMessage[ctcp: false]))
+          get_cmd(rest, parse_from(from, %IrcMessage{ctcp: false}))
       data ->
-          get_cmd(:string.tokens(data, ' '), IrcMessage[ctcp: false])
+          get_cmd(:string.tokens(data, ' '), %IrcMessage{ctcp: false})
     end
   end
 
   defp parse_from(from, msg) do
-    case Regex.split(~r/(!|@|\.)/, iolist_to_binary(from)) do
+    case Regex.split(~r/(!|@|\.)/, iodata_to_binary(from)) do
       [nick, "!", user, "@", host | host_rest] ->
-        msg.update [nick: nick, user: user, host: host <> host_rest]
+        %{msg | :nick => nick, :user => user, :host => host <> host_rest}
       [nick, "@", host | host_rest] ->
-        msg.update [nick: nick, host: host <> host_rest]
+        %{msg | :nick => nick, :host => host <> host_rest}
       [_, "." | _] ->
         # from is probably a server name
-        msg.update [server: from_char_list!(from)]
+        %{msg | :server => from_char_data!(from)}
       [nick] ->
-        msg.update [nick: nick]
+        %{msg | :nick => nick}
     end
   end
 
   # Parse command from message
   defp get_cmd([cmd, arg1, [?:, 1 | ctcp_trail] | restargs], msg) when cmd == 'PRIVMSG' or cmd == 'NOTICE' do
     get_cmd([cmd, arg1, [1 | ctcp_trail] | restargs], msg)
   end
 
   defp get_cmd([cmd, _arg1, [1 | ctcp_trail] | restargs], msg) when cmd == 'PRIVMSG' or cmd == 'NOTICE' do
-    args = ctcp_trail ++ lc arg inlist restargs, do: ' ' ++ arg
+    args = ctcp_trail ++ for arg <- restargs, do: ' ' ++ arg
       |> Enum.flatten
       |> Enum.reverse
     case args do
       [1 | ctcp_rev] ->
         [ctcp_cmd | args] = ctcp_rev |> Enum.reverse |> :string.tokens(' ')
-        msg.update [cmd: from_char_list!(ctcp_cmd), args: args, ctcp: true]
+        %{msg | :cmd => from_char_data!(ctcp_cmd), :args => args, :ctcp => true}
       _ ->
-        msg.update [cmd: from_char_list!(cmd), ctcp: :invalid]
+        %{msg | :cmd => from_char_data!(cmd), :ctcp => :invalid}
     end
   end
 
   defp get_cmd([cmd | rest], msg) do
-    get_args(rest, msg.cmd(from_char_list!(cmd)))
+    get_args(rest, %{msg | :cmd => from_char_data!(cmd)})
   end
 
 
   # Parse command args from message
   defp get_args([], msg) do
-    msg.args
-    |> Enum.reverse 
-    |> Enum.filter(fn(arg) -> arg != [] end)
-    |> Enum.map(&String.from_char_list!/1)
-    |> msg.args
+    args = msg.args
+      |> Enum.reverse 
+      |> Enum.filter(fn(arg) -> arg != [] end)
+      |> Enum.map(&String.from_char_data!/1)
+    %{msg | :args => args}
   end
 
   defp get_args([[?: | first_arg] | rest], msg) do
-    args = (lc arg inlist [first_arg | rest], do: ' ' ++ trim_crlf(arg)) |> List.flatten
+    args = (for arg <- [first_arg | rest], do: ' ' ++ trim_crlf(arg)) |> List.flatten
     case args do
       [_ | []] ->
-          get_args [], msg.update([args: [msg.args]])
+          get_args [], %{msg | :args => [msg.args]}
       [_ | full_trail] ->
-          get_args [], msg.update([args: [full_trail | msg.args]])
+          get_args [], %{msg | :args => [full_trail | msg.args]}
     end
   end
 
   defp get_args([arg | []], msg) do
-    get_args [], msg.update([args: [arg | msg.args]])
+    get_args [], %{msg | :args => [arg | msg.args]}
   end
 
   defp get_args([arg | rest], msg) do
-    get_args rest, msg.update([args: [arg | msg.args]])
+    get_args rest, %{msg | :args => [arg | msg.args]}
   end
 
   ############################
   # Parse RPL_ISUPPORT (005)
   ############################
 
   @doc """
   Parse RPL_ISUPPORT message.
 
   If an empty list is provided, do nothing, otherwise parse CHANTYPES,
   NETWORK, and PREFIX parameters for relevant data.
   """
   @spec isup(parameters :: list(binary), state :: ExIrc.Client.ClientState.t) :: ExIrc.Client.ClientState.t
   def isup([], state), do: state
   def isup([param | rest], state) do
     try do
       isup(rest, isup_param(param, state))
     rescue
       _ -> isup(rest, state)
     end
   end
 
   defp isup_param("CHANTYPES=" <> channel_prefixes, state) do
     prefixes = channel_prefixes |> String.split("", trim: true)
-    state.update [channel_prefixes: prefixes]
+    %{state | :channel_prefixes => prefixes}
   end
   defp isup_param("NETWORK=" <> network, state) do
-    state.update [network: network]
+    %{state | :network => network}
   end
   defp isup_param("PREFIX=" <> user_prefixes, state) do
     prefixes = Regex.run(~r/\((.*)\)(.*)/, user_prefixes, capture: :all_but_first)
-               |> Enum.map(&String.to_char_list!/1)
+               |> Enum.map(&List.from_char_data!/1)
                |> List.zip
-    state.update [user_prefixes: prefixes]
+    %{state | :user_prefixes => prefixes}
   end
   defp isup_param(_, state) do
     state
   end
 
   ###################
   # Helper Functions
   ###################
 
   @days_of_week   ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
   @months_of_year ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
   @doc """
   Get CTCP formatted time from a tuple representing the current calendar time:
 
   Example:
 
       iex> local_time = {{2013,12,6},{14,5,00}}
       {{2013,12,6},{14,5,00}}
       iex> ExIrc.Utils.ctcp_time local_time
       "Fri Dec 06 14:05:00 2013"
   """
   @spec ctcp_time(datetime :: {{integer, integer, integer}, {integer, integer, integer}}) :: binary
   def ctcp_time({{y, m, d}, {h, n, s}} = _datetime) do
     [:lists.nth(:calendar.day_of_the_week(y,m,d), @days_of_week),
      ' ',
      :lists.nth(m, @months_of_year),
      ' ',
      :io_lib.format("~2..0s", [integer_to_list(d)]),
      ' ',
      :io_lib.format("~2..0s", [integer_to_list(h)]),
      ':',
      :io_lib.format("~2..0s", [integer_to_list(n)]),
      ':',
      :io_lib.format("~2..0s", [integer_to_list(s)]),
      ' ',
-     integer_to_list(y)] |> List.flatten |> String.from_char_list!
+     integer_to_list(y)] |> List.flatten |> String.from_char_data!
   end
 
   defp trim_crlf(charlist) do
     case Enum.reverse(charlist) do
       [?\n, ?\r | text] -> Enum.reverse(text)
       _ -> charlist
     end
   end
 
 end
\ No newline at end of file
diff --git a/mix.exs b/mix.exs
index 14581bf..4254986 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,27 +1,26 @@
-Code.ensure_loaded?(Hex) and Hex.start
-
 defmodule ExIrc.Mixfile do
   use Mix.Project
 
   def project do
     [ app: :exirc,
-      version: "0.3.2",
+      version: "0.4.0",
+      elixir: "~> 0.13.0",
       description: "An IRC client library for Elixir.",
       package: package,
       deps: [] ]
   end
 
   # Configuration for the OTP application
   def application do
     [mod: {ExIrc.App, []}]
   end
 
   defp package do
     [ files: ["lib", "mix.exs", "README.md", "LICENSE"],
       contributors: ["Paul Schoenfelder"],
       licenses: ["MIT"],
       links: [ { "GitHub", "https://github.com/bitwalker/exirc" },
                { "Home Page", "http://bitwalker.org/exirc"} ] ]
   end
 
 end
diff --git a/test/utils_test.exs b/test/utils_test.exs
index 784cb69..34d4e2d 100644
--- a/test/utils_test.exs
+++ b/test/utils_test.exs
@@ -1,54 +1,54 @@
 defmodule ExIrc.UtilsTest do
   use ExUnit.Case, async: true
 
   use Irc.Commands
 
   alias ExIrc.Utils, as: Utils
   alias ExIrc.Client.ClientState, as: ClientState
 
   doctest ExIrc.Utils
 
   test "Given a local date/time as a tuple, can retrieve get the CTCP formatted time" do
   	local_time = {{2013,12,6},{14,5,00}} # Mimics output of :calendar.local_time()
   	assert Utils.ctcp_time(local_time) == "Fri Dec 06 14:05:00 2013"
   end
 
   test "Can parse an IRC message" do
   	message = ':irc.example.org 005 nick NETWORK=Freenode PREFIX=(ov)@+ CHANTYPES=#&'
-  	assert IrcMessage[
-      server: "irc.example.org",
-      cmd:    @rpl_isupport,
-      args:   ["nick", "NETWORK=Freenode", "PREFIX=(ov)@+", "CHANTYPES=#&"]
-    ] = Utils.parse(message)
+  	assert %IrcMessage{
+      :server => "irc.example.org",
+      :cmd =>    @rpl_isupport,
+      :args =>   ["nick", "NETWORK=Freenode", "PREFIX=(ov)@+", "CHANTYPES=#&"]
+    } = Utils.parse(message)
   end
 
   test "Parse INVITE message" do
     message = ':pschoenf INVITE testuser #awesomechan'
-    assert IrcMessage[
-      nick: "pschoenf",
-      cmd:  "INVITE",
-      args: ["testuser", "#awesomechan"]
-    ] = Utils.parse(message)
+    assert %IrcMessage{
+      :nick => "pschoenf",
+      :cmd =>  "INVITE",
+      :args => ["testuser", "#awesomechan"]
+    } = Utils.parse(message)
   end
 
   test "Parse KICK message" do
     message = ':pschoenf KICK #testchan lameuser'
-    assert IrcMessage[
-      nick: "pschoenf",
-      cmd:  "KICK",
-      args: ["#testchan", "lameuser"]
-    ] = Utils.parse(message)
+    assert %IrcMessage{
+      :nick => "pschoenf",
+      :cmd =>  "KICK",
+      :args => ["#testchan", "lameuser"]
+    } = Utils.parse(message)
   end
 
   test "Can parse RPL_ISUPPORT commands" do
     message = ':irc.example.org 005 nick NETWORK=Freenode PREFIX=(ov)@+ CHANTYPES=#&'
     parsed  = Utils.parse(message)
-    state   = ClientState.new()
-    assert ClientState[
-      channel_prefixes: ["#", "&"],
-      user_prefixes:    [{?o, ?@}, {?v, ?+}],
-      network:          "Freenode"
-    ] = Utils.isup(parsed.args, state)
+    state   = %ClientState{}
+    assert %ClientState{
+      :channel_prefixes => ["#", "&"],
+      :user_prefixes =>    [{?o, ?@}, {?v, ?+}],
+      :network =>          "Freenode"
+    } = Utils.isup(parsed.args, state)
   end
 
 end