defmodule Mix.Tasks.Phoenix.Gen.Instachat do # This module is a copy paste of Gen.Channel with a few additions use Mix.Task import Mix.Generator import Phoenix.Gen.Utils @shortdoc "Generate an instant chatroom for a Phoenix Application" @moduledoc """ Generates a zero configuration chat room mix phoenix.gen.instachat ## Command line options ## Examples mix phoenix.gen.instachat """ def run(_opts) do channel_name = "instachat" bindings = [ app_name: app_name_camel, module_name: Mix.Utils.camelize(channel_name)<>"Channel", channel_name: channel_name ] # Add the Channel channel_file = Path.join channels_path, "#{channel_name}_channel.ex" create_file channel_file, channel_template(bindings) # Add the Channel Route route_file = Path.join ~w|web router.ex| route_contents = File.read! route_file [_ | captures] = Regex.run(~r/(.*defmodule.*)(\nend)/s, route_contents) route_contents = Enum.join captures, channel_route_template(bindings) File.write! route_file, route_contents Mix.Shell.IO.info "A route was added for this channel." # Generate the Controller and Action Mix.Tasks.Phoenix.Gen.Controller.run ["instachat", "index"] # Overwrite the Template template_path = Path.join [templates_path, "instachat", "index.html.eex"] File.write! template_path, html_template([]) # Add the Config config_path = Path.join ~w|config config.exs| File.write! config_path, config_template([]), [:append] Mix.Shell.IO.info "Your config/config.exs was edited." Mix.Shell.IO.info "An ets table will be started to manage nicknames.\n" # For some reason router changes don't trigger a recompile # so we must manually clean the projec Mix.Tasks.Clean.run [] Mix.Shell.IO.info """ A chat room has been generated. Run: mix phoenix.server Visit: http://localhost:4000/instachat It takes a minute for the chat to start working, I'm not sure why. :/ """ end embed_template :channel_route, """ socket "/instachatsocket", <%= @app_name %> do channel "instachat:*", InstachatChannel end """ embed_template :config, """ # Start an ets table to manage chat nick names :ets.new(:instachat, [:named_table, :public, :set]) """ embed_template :channel, """ defmodule <%= @app_name %>.<%= @module_name %> do use Phoenix.Channel @doc "Handles when a user first joins the channel." def join(topic, nick, socket) do case :ets.lookup(:instachat, nick) do [{^nick, _}] -> reply socket, "error", %{nickTaken: true} {:error, :nick_taken, socket} _ -> socket = Phoenix.Socket.put_topic(socket, topic) socket = Phoenix.Socket.assign(socket, :nick, nick) :ets.insert(:instachat, {nick, socket[:pid]}) reply socket, topic, %{start: "Welcome to the chat!"} end end @doc "Handles a user sending a chat message" def handle_in(topic, %{"body" => body}, socket) do broadcast socket, topic, %{body: body, nick: socket.assigns[:nick], room: socket.assigns[:room]} end @doc "Handles a user joining a room" def handle_in(topic, %{"room" => room}, socket) do socket = Phoenix.Socket.assign(socket, :room, room) broadcast socket, topic, %{join: socket.assigns[:nick], room: room} reply socket, topic, %{entered: room} end @doc "Handles outgoing broadcasts of chat messages" def handle_out(topic, %{body: body, nick: nick, room: room}, socket) do cond do # don't send the message to people not in the room socket.assigns[:room] !== room -> {:ok, socket} # send it to everyone else true -> reply socket, topic, %{body: body, nick: nick} end end @doc "Handles outgoing broadcasts of room joins" def handle_out(topic, %{join: nick, room: room}, socket) do cond do # don't send the join message to people not in the room socket.assigns[:room] !== room -> {:ok, socket} # don't send the join message to the person who joined socket.assigns[:nick] == nick -> {:ok, socket} # send it to everyone else true -> reply socket, topic, %{join: nick} end end @doc "Catchall for broadcasts, anything not handlede gets forwarded" def handle_out(topic, msg, socket) do reply socket, topic, msg end @doc "Remove nicknames when a user disconnects" def leave(_, socket) do :ets.delete(:instachat, socket.assigns[:nick]) {:ok, socket} end end """ embed_template :html, """



""" end