Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions lib/bruce_hedwig/score_responder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ defmodule BruceHedwig.ScoreResponder do


@usage """
hedwig help - Gives a thing a positive point
subject++ <reason> - Gives a thing a positive point
"""
hear ~r/(?<subject>\A.+)\s*\+\+\s*for\s+(?<reason>.+\Z)/i, msg do
ScoreServer.inc(msg.matches["subject"], msg.matches["reason"])
reply msg, "Noted!"
current = ScoreServer.inc(msg.matches["subject"], msg.matches["reason"])
reply msg, "Noted! #{msg.matches["subject"]} has #{current} points"
end

@usage """
hedwig help - Gives a thing a negative point
subject-- <reason> - Gives a thing a negative point
"""
hear ~r/(?<subject>\A.+)\s*\-\-\s*for\s+(?<reason>.+\Z)/i, msg do
ScoreServer.dec(msg.matches["subject"], msg.matches["reason"])
reply msg, "Noted!"
current = ScoreServer.dec(msg.matches["subject"], msg.matches["reason"])
reply msg, "Noted! #{msg.matches["subject"]} has #{current} points"
end

@usage """
hedwig help - Lists the current scores in a semi formatted way
list scores - Lists the current scores in a semi formatted way
"""
hear ~r/(what is the score?|tell me the score|list scores)/i, msg do
reply msg, ScoreServer.scores
respond ~r/(list scores)/i, msg do
reply msg, ScoreServer.html_scores
end
end
159 changes: 106 additions & 53 deletions lib/bruce_hedwig/score_server.ex
Original file line number Diff line number Diff line change
@@ -1,86 +1,139 @@
defmodule BruceHedwig.ScoreServer do
require Logger

use GenServer

require Logger

# TODO: This registration will fail *in test* when this application is
# added to the supervisor tree. Look into OTP testing strategies
def start_link do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
def lookup(name) do
GenServer.call(:score_server, {:lookup, String.capitalize(name)})
end

def inc(name, reason \\ "") do
GenServer.call(__MODULE__, {:inc, String.capitalize(name), reason})
GenServer.call(:score_server, {:inc, String.capitalize(name), reason})
end

def dec(name, reason \\ "") do
GenServer.call(__MODULE__, {:dec, String.capitalize(name), reason})
GenServer.call(:score_server, {:inc, String.capitalize(name), reason})
end

def score(name) do
GenServer.call(__MODULE__, {:score, String.capitalize(name)})
def scores do
GenServer.call(:score_server, {:scores})
end

def raw_scores do
GenServer.call(__MODULE__, {:scores})
def html_scores do
GenServer.call(:score_server, {:html_scores})
end

def scores do
scores = GenServer.call(__MODULE__, {:scores})
"\n* " <> Enum.join(Enum.map(Map.keys(scores), fn k ->
format_score(k, scores[k])
end), "\n* ")
end

defp format_score(name, scores) do
total = total_score(scores)
score_by_reason = Enum.group_by(scores, fn {_s,r} -> r end)
sub_scores = Enum.map(Map.keys(score_by_reason), fn reason ->
if reason != "" do
scores = score_by_reason[reason]
"#{total_score(scores)} points for #{reason}"
end
end)

if sub_scores != [nil] do
"#{name} has #{total} points for the following reasons:#{Enum.join(sub_scores, "\n\t")}"
else
"#{name} has #{total} points"
def total_score(scores) do
Enum.reduce(scores, 0, fn ({s, _r}, acc) -> s + acc end)
end

def keys(score_table) do
Enum.map(get_ets_keys_lazy(score_table), &(&1))
end

def get_ets_keys_lazy(table_name) when is_atom(table_name) do
eot = :"$end_of_table"

Stream.resource(
fn -> [] end,

fn acc ->
case acc do
[] ->
case :dets.first(table_name) do
^eot -> {:halt, acc}
first_key -> {[first_key], first_key}
end

acc ->
case :dets.next(table_name, acc) do
^eot -> {:halt, acc}
next_key -> {[next_key], next_key}
end
end
end,

fn _acc -> :ok end
)
end

# TODO: This registration will fail *in test* when this application is
# added to the supervisor tree. Look into OTP testing strategies
def start_link do
GenServer.start_link(__MODULE__, nil, name: :score_server)
end

def init(_) do
{:ok, score_table} = :dets.open_file(:score_table, [type: :set])

{:ok, score_table}
end

def handle_call({:lookup, name}, _from, score_table) do
value = case :dets.lookup(score_table, name) do
[{^name, score_list}] -> score_list
_ -> [{0, "existing"}]
end

{:reply, value, score_table}
end

defp total_score(scores) do
Enum.reduce(scores, 0, fn ({s, _r}, acc) -> s + acc end)
def handle_call({:inc, name, reason}, _from, score_table) do
scores = case :dets.lookup(score_table, name) do
[{^name, score_list}] -> score_list
_ -> [{0, "existing"}]
end

new_score = elem(Enum.at(scores, -1), 0) + 1
new_scores = [ {new_score, reason} | scores ]

:dets.insert(score_table, {name, new_scores})

{:reply, total_score(new_scores), score_table}
end

## Server Callbacks
def handle_call({:dec, name, reason}, _from, score_table) do
scores = case :dets.lookup(score_table, name) do
[{^name, score_list}] -> score_list
_ -> [{0, "existing"}]
end

new_score = elem(Enum.at(scores, -1), 0) - 1
new_scores = [ {new_score, reason} | scores ]

def init(:ok) do
{:ok, %{}}
:dets.insert(score_table, {name, new_scores})

{:reply, total_score(new_scores), score_table}
end

def handle_call({:inc, name, reason}, _from, names) do
state = Map.get(names, name, [{0, ""}])
new_score = elem(Enum.at(state, -1), 0) + 1
new_scores = [ {new_score, reason} | state ]

{:reply, total_score(new_scores), Map.put(names, name, new_scores)}
def handle_call({:scores}, _from, score_table) do
s = for key <- keys(score_table), into: %{}, do: {key, lookup(key)}
{:reply, s, score_table}
end

def handle_call({:dec, name, reason}, _from, names) do
state = Map.get(names, name, [{0, ""}])
new_score = elem(Enum.at(state, -1), 0) - 1
new_scores = [ {new_score, reason} | state ]
def handle_call({:html_scores}, _from, score_table) do
formatted = for key <- keys(score_table), into: "" do
scores = case :dets.lookup(score_table, key) do
[{^key, score_list}] -> score_list
_ -> [{0, "existing"}]
end

output = "\n #{key} has #{total_score(scores)} points for the following reasons:\n"
Enum.reduce(scores, output, fn {v, reason}, acc ->
acc <> "\t * #{v} for #{reason} \n"
end)
end

{:reply, total_score(new_scores), Map.put(names, name, new_scores)}
{:reply, formatted, score_table}
end

def handle_call({:score, name}, _from, names) do
state = Map.get(names, name, [{0, ""}])
{:reply, state, names}
def terminate(:shutdown, score_table) do
:dets.close(score_table)
{:noreply, score_table}
end

def handle_call({:scores}, _from, names) do
{:reply, names, names}
def handle_info(_msg, score_table) do
{:noreply, score_table}
end
end
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule BruceHedwig.Mixfile do

def project do
[app: :bruce_hedwig,
version: "0.0.1",
version: "0.0.3",
elixir: "~> 1.2",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
Expand All @@ -30,7 +30,7 @@ defmodule BruceHedwig.Mixfile do
defp deps do
[
{:hedwig, github: "hedwig-im/hedwig"},
{:hedwig_flowdock, "~> 0.1.1"},
{:hedwig_flowdock, "~> 0.1.2"},
{:httpoison, "~> 0.9.0"},
{:exrm, "~> 1.0"},
]
Expand Down
14 changes: 7 additions & 7 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
%{"bbmustache": {:hex, :bbmustache, "1.0.4", "7ba94f971c5afd7b6617918a4bb74705e36cab36eb84b19b6a1b7ee06427aa38", [:rebar], []},
"certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []},
"cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []},
"connection": {:hex, :connection, "1.0.2", "f4a06dd3ecae4141aa66f94ce92ea4c4b8753069472814932f1cadbc3078ab80", [:mix], []},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []},
"cowlib": {:hex, :cowlib, "1.3.0", "6c80ca7f2863c0d7a21c946312baf473432b56a79e46e20bc27a3bac07c40439", [:make], []},
"erlware_commons": {:hex, :erlware_commons, "0.19.0", "7b43caf2c91950c5f60dc20451e3c3afba44d3d4f7f27bcdc52469285a5a3e70", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]},
"exrm": {:hex, :exrm, "1.0.5", "53ecb20da2f4e5b4c82ea6776824fbc677c8d287bf20efc9fc29cacc2cca124f", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]},
"getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []},
"gproc": {:hex, :gproc, "0.5.0", "2df2d886f8f8a7b81a4b04aa17972b5965bbc5bf0100ea6d8e8ac6a0e7389afe", [:rebar], []},
"gun": {:hex, :gun, "1.0.0-pre.1", "28514327a7572234633e127c1c14c5d91991bc26ec16d3707f05962a31f0c1b5", [:rebar, :make], [{:ranch, "1.1.0", [hex: :ranch, optional: false]}, {:cowlib, "1.3.0", [hex: :cowlib, optional: false]}]},
"hackney": {:hex, :hackney, "1.6.0", "8d1e9440c9edf23bf5e5e2fe0c71de03eb265103b72901337394c840eec679ac", [:rebar3], [{:ssl_verify_fun, "1.1.0", [hex: :ssl_verify_fun, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:certifi, "0.4.0", [hex: :certifi, optional: false]}]},
"hedwig": {:git, "https://github.com/hedwig-im/hedwig.git", "2cf3ad2b881bdb81e7b2754d70ba1904e5c7a83e", []},
"hedwig_flowdock": {:hex, :hedwig_flowdock, "0.1.1", "f5b4cfca92e9502da5027b0f1cbbe2087b54290ad1f61ba5e7a669b8cb8fe728", [:mix], [{:poison, "~> 2.0", [hex: :poison, optional: false]}, {:gun, "1.0.0-pre.1", [hex: :gun, optional: false]}, {:connection, "~> 1.0", [hex: :connection, optional: false]}]},
"gun": {:hex, :gun, "1.0.0-pre.1", "28514327a7572234633e127c1c14c5d91991bc26ec16d3707f05962a31f0c1b5", [:rebar, :make], [{:cowlib, "1.3.0", [hex: :cowlib, optional: false]}, {:ranch, "1.1.0", [hex: :ranch, optional: false]}]},
"hackney": {:hex, :hackney, "1.6.0", "8d1e9440c9edf23bf5e5e2fe0c71de03eb265103b72901337394c840eec679ac", [:rebar3], [{:certifi, "0.4.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.0", [hex: :ssl_verify_fun, optional: false]}]},
"hedwig": {:git, "https://github.com/hedwig-im/hedwig.git", "230a4c241fdf38a3eaaaafe8820ce9ddd72482ba", []},
"hedwig_flowdock": {:hex, :hedwig_flowdock, "0.1.2", "4a5b3b34794233d05b23d1d86e6640642b15ad335c314e4c06ee18e247d54bb8", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:gun, "1.0.0-pre.1", [hex: :gun, optional: false]}, {:poison, "~> 2.0", [hex: :poison, optional: false]}]},
"httpoison": {:hex, :httpoison, "0.9.0", "68187a2daddfabbe7ca8f7d75ef227f89f0e1507f7eecb67e4536b3c516faddb", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]},
"idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
"poison": {:hex, :poison, "2.1.0", "f583218ced822675e484648fa26c933d621373f01c6c76bd00005d7bd4b82e27", [:mix], []},
"poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []},
"providers": {:hex, :providers, "1.6.0", "db0e2f9043ae60c0155205fcd238d68516331d0e5146155e33d1e79dc452964a", [:rebar3], [{:getopt, "0.8.2", [hex: :getopt, optional: false]}]},
"ranch": {:hex, :ranch, "1.1.0", "f7ed6d97db8c2a27cca85cacbd543558001fc5a355e93a7bff1e9a9065a8545b", [:make], []},
"relx": {:hex, :relx, "3.19.0", "286dd5244b4786f56aac75d5c8e2d1fb4cfd306810d4ec8548f3ae1b3aadb8f7", [:rebar3], [{:providers, "1.6.0", [hex: :providers, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:erlware_commons, "0.19.0", [hex: :erlware_commons, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}]},
"relx": {:hex, :relx, "3.19.0", "286dd5244b4786f56aac75d5c8e2d1fb4cfd306810d4ec8548f3ae1b3aadb8f7", [:rebar3], [{:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:erlware_commons, "0.19.0", [hex: :erlware_commons, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:providers, "1.6.0", [hex: :providers, optional: false]}]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.0", "edee20847c42e379bf91261db474ffbe373f8acb56e9079acb6038d4e0bf414f", [:rebar, :make], []}}