From 99840316d9cea5fdbe2a0df37a333621fc2ba557 Mon Sep 17 00:00:00 2001 From: supernullset Date: Sun, 14 Aug 2016 11:41:31 -0700 Subject: [PATCH 1/6] update score server to use ETS --- lib/bruce_hedwig/score_responder.ex | 2 +- lib/bruce_hedwig/score_server.ex | 132 +++++++++++++++++----------- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/lib/bruce_hedwig/score_responder.ex b/lib/bruce_hedwig/score_responder.ex index 65bea7f..1cdfcbe 100644 --- a/lib/bruce_hedwig/score_responder.ex +++ b/lib/bruce_hedwig/score_responder.ex @@ -25,6 +25,6 @@ defmodule BruceHedwig.ScoreResponder do hedwig help - 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 + reply msg, ScoreServer.html_scores end end diff --git a/lib/bruce_hedwig/score_server.ex b/lib/bruce_hedwig/score_server.ex index 2b0fe25..14ed4c8 100644 --- a/lib/bruce_hedwig/score_server.ex +++ b/lib/bruce_hedwig/score_server.ex @@ -1,86 +1,112 @@ defmodule BruceHedwig.ScoreServer do - use GenServer - require Logger + use GenServer # 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__) + GenServer.start_link(__MODULE__, nil, name: :score_server) end - def inc(name, reason \\ "") do - GenServer.call(__MODULE__, {:inc, String.capitalize(name), reason}) + def init(_) do + :ets.new(:score_table, [:set, :protected, :named_table]) + {:ok, nil} end - def dec(name, reason \\ "") do - GenServer.call(__MODULE__, {:dec, String.capitalize(name), reason}) - end + def handle_call({:lookup, name}, _from, _) do + value = case :ets.lookup(:score_table, name) do + [{^name, score_list}] -> score_list + _ -> [{0, "existing"}] + end - def score(name) do - GenServer.call(__MODULE__, {:score, String.capitalize(name)}) + {:reply, value, nil} end - def raw_scores do - GenServer.call(__MODULE__, {:scores}) - end + def handle_call({:inc, name, reason}, _from, _) do + scores = case :ets.lookup(:score_table, name) do + [{^name, score_list}] -> score_list + _ -> [{0, "existing"}] + 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* ") + new_score = elem(Enum.at(scores, -1), 0) + 1 + new_scores = [ {new_score, reason} | scores ] + + :ets.insert(:score_table, {name, new_scores}) + + {:reply, total_score(new_scores), nil} 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 handle_call({:dec, name, reason}, _from, _) do + scores = case :ets.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 ] + + :ets.insert(:score_table, {name, new_scores}) + + {:reply, total_score(new_scores), nil} end - defp total_score(scores) do - Enum.reduce(scores, 0, fn ({s, _r}, acc) -> s + acc end) + def lookup(name) do + GenServer.call(:score_server, {:lookup, String.capitalize(name)}) end - ## Server Callbacks + def inc(name, reason \\ "") do + GenServer.call(:score_server, {:inc, String.capitalize(name), reason}) + end - def init(:ok) do - {:ok, %{}} + def dec(name, reason \\ "") do + GenServer.call(:score_server, {:inc, String.capitalize(name), reason}) 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 scores do + for key <- keys, into: %{}, do: {key, lookup(key)} 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 html_scores do + for key <- keys, into: "" do + scores = lookup(key) + 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 + end - {:reply, total_score(new_scores), Map.put(names, name, new_scores)} + def total_score(scores) do + Enum.reduce(scores, 0, fn ({s, _r}, acc) -> s + acc end) end - def handle_call({:score, name}, _from, names) do - state = Map.get(names, name, [{0, ""}]) - {:reply, state, names} + def keys do + Enum.map(get_ets_keys_lazy(:score_table), &(&1)) end - def handle_call({:scores}, _from, names) do - {:reply, names, names} + 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 :ets.first(table_name) do + ^eot -> {:halt, acc} + first_key -> {[first_key], first_key} + end + + acc -> + case :ets.next(table_name, acc) do + ^eot -> {:halt, acc} + next_key -> {[next_key], next_key} + end + end + end, + + fn _acc -> :ok end + ) end end From f8d341537bc7d949de9b8d34aacd95dae823ea29 Mon Sep 17 00:00:00 2001 From: supernullset Date: Sun, 14 Aug 2016 11:51:51 -0700 Subject: [PATCH 2/6] convert to use dets --- lib/bruce_hedwig/score_server.ex | 66 ++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/lib/bruce_hedwig/score_server.ex b/lib/bruce_hedwig/score_server.ex index 14ed4c8..06a7cf8 100644 --- a/lib/bruce_hedwig/score_server.ex +++ b/lib/bruce_hedwig/score_server.ex @@ -9,21 +9,22 @@ defmodule BruceHedwig.ScoreServer do end def init(_) do - :ets.new(:score_table, [:set, :protected, :named_table]) - {:ok, nil} + {:ok, score_table} = :dets.open_file(:score_table, [type: :set]) + + {:ok, score_table} end - def handle_call({:lookup, name}, _from, _) do - value = case :ets.lookup(:score_table, name) do + 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, nil} + {:reply, value, score_table} end - def handle_call({:inc, name, reason}, _from, _) do - scores = case :ets.lookup(:score_table, name) do + 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 @@ -31,13 +32,13 @@ defmodule BruceHedwig.ScoreServer do new_score = elem(Enum.at(scores, -1), 0) + 1 new_scores = [ {new_score, reason} | scores ] - :ets.insert(:score_table, {name, new_scores}) + :dets.insert(score_table, {name, new_scores}) - {:reply, total_score(new_scores), nil} + {:reply, total_score(new_scores), score_table} end - def handle_call({:dec, name, reason}, _from, _) do - scores = case :ets.lookup(:score_table, name) do + 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 @@ -45,9 +46,30 @@ defmodule BruceHedwig.ScoreServer do new_score = elem(Enum.at(scores, -1), 0) - 1 new_scores = [ {new_score, reason} | scores ] - :ets.insert(:score_table, {name, new_scores}) + :dets.insert(score_table, {name, new_scores}) + + {:reply, total_score(new_scores), score_table} + end - {:reply, total_score(new_scores), nil} + 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({: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, formatted, score_table} end def lookup(name) do @@ -63,25 +85,19 @@ defmodule BruceHedwig.ScoreServer do end def scores do - for key <- keys, into: %{}, do: {key, lookup(key)} + GenServer.call(:score_server, {:scores}) end def html_scores do - for key <- keys, into: "" do - scores = lookup(key) - 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 + GenServer.call(:score_server, {:html_scores}) end def total_score(scores) do Enum.reduce(scores, 0, fn ({s, _r}, acc) -> s + acc end) end - def keys do - Enum.map(get_ets_keys_lazy(:score_table), &(&1)) + 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 @@ -93,13 +109,13 @@ defmodule BruceHedwig.ScoreServer do fn acc -> case acc do [] -> - case :ets.first(table_name) do + case :dets.first(table_name) do ^eot -> {:halt, acc} first_key -> {[first_key], first_key} end acc -> - case :ets.next(table_name, acc) do + case :dets.next(table_name, acc) do ^eot -> {:halt, acc} next_key -> {[next_key], next_key} end From 983eca9ba8d170fc8f9a6b0e068eb9a4eae0527d Mon Sep 17 00:00:00 2001 From: supernullset Date: Sun, 14 Aug 2016 11:56:17 -0700 Subject: [PATCH 3/6] reorganize and properly handle down messages --- lib/bruce_hedwig/score_server.ex | 111 +++++++++++++++++-------------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/lib/bruce_hedwig/score_server.ex b/lib/bruce_hedwig/score_server.ex index 06a7cf8..674c540 100644 --- a/lib/bruce_hedwig/score_server.ex +++ b/lib/bruce_hedwig/score_server.ex @@ -2,6 +2,62 @@ defmodule BruceHedwig.ScoreServer do require Logger use GenServer + + + def lookup(name) do + GenServer.call(:score_server, {:lookup, String.capitalize(name)}) + end + + def inc(name, reason \\ "") do + GenServer.call(:score_server, {:inc, String.capitalize(name), reason}) + end + + def dec(name, reason \\ "") do + GenServer.call(:score_server, {:inc, String.capitalize(name), reason}) + end + + def scores do + GenServer.call(:score_server, {:scores}) + end + + def html_scores do + GenServer.call(:score_server, {:html_scores}) + end + + 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 @@ -72,57 +128,12 @@ defmodule BruceHedwig.ScoreServer do {:reply, formatted, score_table} end - def lookup(name) do - GenServer.call(:score_server, {:lookup, String.capitalize(name)}) - end - - def inc(name, reason \\ "") do - GenServer.call(:score_server, {:inc, String.capitalize(name), reason}) + def handle_info({:DOWN, _, _, _, _}, score_table) do + :dets.close(score_table) + {:noreply, score_table} end - def dec(name, reason \\ "") do - GenServer.call(:score_server, {:inc, String.capitalize(name), reason}) - end - - def scores do - GenServer.call(:score_server, {:scores}) - end - - def html_scores do - GenServer.call(:score_server, {:html_scores}) - end - - 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 - ) + def handle_info(_msg, score_table) do + {:noreply, score_table} end end From d7a4484c9b3d65e6fe7464d3188e8f030f21b9b7 Mon Sep 17 00:00:00 2001 From: supernullset Date: Sun, 14 Aug 2016 12:05:54 -0700 Subject: [PATCH 4/6] updates to the responder --- lib/bruce_hedwig/score_responder.ex | 20 ++++++++++++++++++-- lib/bruce_hedwig/score_server.ex | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/bruce_hedwig/score_responder.ex b/lib/bruce_hedwig/score_responder.ex index 1cdfcbe..df823bf 100644 --- a/lib/bruce_hedwig/score_responder.ex +++ b/lib/bruce_hedwig/score_responder.ex @@ -13,11 +13,27 @@ defmodule BruceHedwig.ScoreResponder do reply msg, "Noted!" end + @usage """ + hedwig help - Gives a thing a positive point + """ + hear ~r/(?\A.+)\s*\+\+/i, msg do + ScoreServer.inc(msg.matches["subject"], "Just Because") + reply msg, "Noted!" + end + @usage """ hedwig help - Gives a thing a negative point """ - hear ~r/(?\A.+)\s*\-\-\s*for\s+(?.+\Z)/i, msg do - ScoreServer.dec(msg.matches["subject"], msg.matches["reason"]) + hear ~r/(?\A.+)\s*\-\-/i, msg do + ScoreServer.dec(msg.matches["subject"], "Just Because") + reply msg, "Noted!" + end + + @usage """ + hedwig help - Gives a thing a positive point + """ + hear ~r/(?\A.+)\s*\+\+/i, msg do + ScoreServer.inc(msg.matches["subject"], "Just Because") reply msg, "Noted!" end diff --git a/lib/bruce_hedwig/score_server.ex b/lib/bruce_hedwig/score_server.ex index 674c540..6161672 100644 --- a/lib/bruce_hedwig/score_server.ex +++ b/lib/bruce_hedwig/score_server.ex @@ -128,7 +128,7 @@ defmodule BruceHedwig.ScoreServer do {:reply, formatted, score_table} end - def handle_info({:DOWN, _, _, _, _}, score_table) do + def terminate(:shutdown, score_table) do :dets.close(score_table) {:noreply, score_table} end From 2bbf7b1331bdec33ca992ec1f065e4cfbffa7e5f Mon Sep 17 00:00:00 2001 From: supernullset Date: Sun, 14 Aug 2016 12:13:23 -0700 Subject: [PATCH 5/6] update listeners --- lib/bruce_hedwig/score_responder.ex | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/bruce_hedwig/score_responder.ex b/lib/bruce_hedwig/score_responder.ex index df823bf..5743edd 100644 --- a/lib/bruce_hedwig/score_responder.ex +++ b/lib/bruce_hedwig/score_responder.ex @@ -6,7 +6,7 @@ defmodule BruceHedwig.ScoreResponder do @usage """ - hedwig help - Gives a thing a positive point + subject++ - Gives a thing a positive point """ hear ~r/(?\A.+)\s*\+\+\s*for\s+(?.+\Z)/i, msg do ScoreServer.inc(msg.matches["subject"], msg.matches["reason"]) @@ -14,7 +14,7 @@ defmodule BruceHedwig.ScoreResponder do end @usage """ - hedwig help - Gives a thing a positive point + subject++ - Gives a thing a positive point """ hear ~r/(?\A.+)\s*\+\+/i, msg do ScoreServer.inc(msg.matches["subject"], "Just Because") @@ -22,25 +22,25 @@ defmodule BruceHedwig.ScoreResponder do end @usage """ - hedwig help - Gives a thing a negative point + subject-- - Gives a thing a negative point """ - hear ~r/(?\A.+)\s*\-\-/i, msg do - ScoreServer.dec(msg.matches["subject"], "Just Because") + hear ~r/(?\A.+)\s*\-\-\s*for\s+(?.+\Z)/i, msg do + ScoreServer.dec(msg.matches["subject"], msg.matches["reason"]) reply msg, "Noted!" end @usage """ - hedwig help - Gives a thing a positive point + subject-- - Gives a thing a positive point """ - hear ~r/(?\A.+)\s*\+\+/i, msg do - ScoreServer.inc(msg.matches["subject"], "Just Because") + hear ~r/(?\A.+)\s*\-\-/i, msg do + ScoreServer.dec(msg.matches["subject"], "Just Because") reply msg, "Noted!" 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 + respond ~r/(list scores)/i, msg do reply msg, ScoreServer.html_scores end end From 55680ace7d9bb8bbea6293c3d86316e5f9eebf3d Mon Sep 17 00:00:00 2001 From: supernullset Date: Sun, 14 Aug 2016 13:05:01 -0700 Subject: [PATCH 6/6] updates! --- lib/bruce_hedwig/score_responder.ex | 24 ++++-------------------- mix.exs | 4 ++-- mix.lock | 14 +++++++------- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/lib/bruce_hedwig/score_responder.ex b/lib/bruce_hedwig/score_responder.ex index 5743edd..2d99008 100644 --- a/lib/bruce_hedwig/score_responder.ex +++ b/lib/bruce_hedwig/score_responder.ex @@ -9,32 +9,16 @@ defmodule BruceHedwig.ScoreResponder do subject++ - Gives a thing a positive point """ hear ~r/(?\A.+)\s*\+\+\s*for\s+(?.+\Z)/i, msg do - ScoreServer.inc(msg.matches["subject"], msg.matches["reason"]) - reply msg, "Noted!" - end - - @usage """ - subject++ - Gives a thing a positive point - """ - hear ~r/(?\A.+)\s*\+\+/i, msg do - ScoreServer.inc(msg.matches["subject"], "Just Because") - reply msg, "Noted!" + current = ScoreServer.inc(msg.matches["subject"], msg.matches["reason"]) + reply msg, "Noted! #{msg.matches["subject"]} has #{current} points" end @usage """ subject-- - Gives a thing a negative point """ hear ~r/(?\A.+)\s*\-\-\s*for\s+(?.+\Z)/i, msg do - ScoreServer.dec(msg.matches["subject"], msg.matches["reason"]) - reply msg, "Noted!" - end - - @usage """ - subject-- - Gives a thing a positive point - """ - hear ~r/(?\A.+)\s*\-\-/i, msg do - ScoreServer.dec(msg.matches["subject"], "Just Because") - reply msg, "Noted!" + current = ScoreServer.dec(msg.matches["subject"], msg.matches["reason"]) + reply msg, "Noted! #{msg.matches["subject"]} has #{current} points" end @usage """ diff --git a/mix.exs b/mix.exs index 31f5751..ac6f731 100644 --- a/mix.exs +++ b/mix.exs @@ -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, @@ -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"}, ] diff --git a/mix.lock b/mix.lock index dbc3bf8..de587c2 100644 --- a/mix.lock +++ b/mix.lock @@ -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], []}}