Adjusted haircut to new, better gen_ealirc interface.
authorStanislaw Klekot <dozzie@jarowit.net>
Sun, 21 Sep 2014 02:01:42 +0000 (04:01 +0200)
committerStanislaw Klekot <dozzie@jarowit.net>
Sun, 21 Sep 2014 02:01:42 +0000 (04:01 +0200)
src/haircut_bot.erl
src/haircut_bot_restarter.erl [deleted file]
src/haircut_bot_sup.erl

index bdfcb9b..569a8ef 100644 (file)
 
 %%% gen_ealirc callbacks
 -export([init/1, terminate/2]).
+-export([connected/2, disconnected/2]).
 -export([handle_call/3, handle_cast/2, handle_info/2, handle_message/4]).
 -export([code_change/3]).
 
 %%%---------------------------------------------------------------------------
 
--record(state, {nick}).
+-define(RECONNECT_INTERVAL, 10000). % reconnect after 10s
+-record(state, {
+  nick :: ealirc:nick(),
+  user :: {string(), string()},
+  channels :: [ealirc:channel()]
+}).
 
 %%%---------------------------------------------------------------------------
 %%% public API {{{
@@ -60,30 +66,7 @@ when is_list(User) ->
 
 start_link(Server, Port, Nick, User, FullName, Channels) ->
   Args = [Nick, User, FullName, Channels],
-  RegName = {local, ?MODULE},
-  case gen_ealirc:connect_link(Server, Port, RegName, ?MODULE, Args, []) of
-    % connected successfully
-    {ok, Pid} -> {ok, Pid};
-    % network error, to be restarted some other time
-    % TODO: log this event
-    {error, econnaborted} -> ignore();
-    {error, econnrefused} -> ignore();
-    {error, econnreset}   -> ignore();
-    {error, eintr}        -> ignore();
-    {error, enetdown}     -> ignore();
-    {error, enetunreach}  -> ignore();
-    {error, epipe}        -> ignore();
-    {error, erefused}     -> ignore();
-    {error, etimedout}    -> ignore();
-    {error, nxdomain}     -> ignore();
-    % non-network error, not a subject to restart
-    {error, Reason} -> {error, Reason}
-  end.
-
-ignore() ->
-  Message = "network problem, leaving restart to restarter",
-  error_logger:warning_report(haircut, Message),
-  ignore.
+  gen_ealirc:start_link({local, ?MODULE}, ?MODULE, Args, {Server, Port}, []).
 
 %%% }}}
 %%%---------------------------------------------------------------------------
@@ -96,11 +79,12 @@ ignore() ->
 %% @doc Initialize {@link gen_ealirc} state.
 
 init([Nick, User, FullName, Channels] = _Args) ->
-  gen_ealirc:nick(self(), Nick),
-  gen_ealirc:user(self(), User, none, FullName),
-  gen_ealirc:join(self(), Channels),
-  % TODO: `Nick' could be already in use
-  {ok, #state{nick = Nick}}.
+  State = #state{
+    nick = Nick,
+    user = {User, FullName},
+    channels = Channels
+  },
+  {ok, State}.
 
 %% @private
 %% @doc Clean up {@link gen_ealirc} state.
@@ -108,6 +92,29 @@ init([Nick, User, FullName, Channels] = _Args) ->
 terminate(_Reason, _State) ->
   ok.
 
+%% @private
+%% @doc Configure IRC connection.
+
+connected(_Socket, State = #state{nick = Nick, user = {User, FullName},
+                                  channels = Channels}) ->
+  error_logger:info_report(bot, [{event, connected}]),
+  % TODO: `Nick' could be already in use
+  gen_ealirc:nick(self(), Nick),
+  gen_ealirc:user(self(), User, none, FullName),
+  gen_ealirc:join(self(), Channels),
+  {ok, State}.
+
+%% @private
+%% @doc Handle the case when the connection was lost.
+
+disconnected({connect,_} = _Reason, State) ->
+  error_logger:info_report(bot, [{event, no_connection}]),
+  {reconnect, ?RECONNECT_INTERVAL, State};
+
+disconnected(_Reason, State) ->
+  error_logger:info_report(bot, [{event, connection_lost}]),
+  {reconnect, ?RECONNECT_INTERVAL, State}.
+
 %% }}}
 %%----------------------------------------------------------
 %% communication {{{
diff --git a/src/haircut_bot_restarter.erl b/src/haircut_bot_restarter.erl
deleted file mode 100644 (file)
index 403f600..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-%%%---------------------------------------------------------------------------
-%%% @doc
-%%%   Restarter for {@link haircut_bot}. The process expects to be a sibling
-%%%   of {@link haircut_bot} processes (supervisors are ignored, only workers
-%%%   count here) and talks with its supervisor about them.
-%%% @end
-%%%---------------------------------------------------------------------------
-
--module(haircut_bot_restarter).
-
--behaviour(gen_server).
-
-%%% public API
--export([start_link/0]).
-
-%%% gen_server callbacks
--export([init/1, terminate/2]).
--export([handle_call/3, handle_cast/2, handle_info/2]).
--export([code_change/3]).
-
-%%%---------------------------------------------------------------------------
-
--record(state, {parent, init}).
--define(MONITOR_TABLE, nabla). % TODO: change the table name
--define(RESTART_INTERVAL, 10000).
-
-%%%---------------------------------------------------------------------------
-%%% public API {{{
-
-%% @doc Start haircut bot restarter.
-
--spec start_link() ->
-  {ok, pid()} | {error, term()}.
-
-start_link() ->
-  Parent = self(),
-  gen_server:start_link(?MODULE, [Parent], []).
-
-%%% }}}
-%%%---------------------------------------------------------------------------
-%%% gen_server callbacks
-
-%%----------------------------------------------------------
-%% initialization and cleanup {{{
-
-%% @private
-%% @doc Initialize {@link gen_server} state.
-
-init([Parent] = _Args) ->
-  ets:new(?MONITOR_TABLE, [set, named_table]),
-  {ok, #state{parent = Parent, init = true}, 0}.
-
-%% @private
-%% @doc Clean up {@link gen_server} state.
-
-terminate(_Reason, _State) ->
-  ets:delete(?MONITOR_TABLE),
-  ok.
-
-%% }}}
-%%----------------------------------------------------------
-%% communication {{{
-
-%% @private
-%% @doc Handle {@link gen_server:call/2}.
-
-handle_call(_Request, _From, State) ->
-  {reply, {error, unknown}, State}.
-
-%% @private
-%% @doc Handle {@link gen_server:cast/2}.
-
-handle_cast(_Request, State) ->
-  {noreply, State}.
-
-%% @private
-%% @doc Handle incoming messages.
-
-% retrieve siblings' pids and monitor them
-handle_info(timeout = _Msg, State = #state{parent = Parent, init = true}) ->
-  Siblings = [
-    {Name, Pid} ||
-    {Name, Pid, worker, _Modules} <- supervisor:which_children(Parent), 
-    Pid /= self()
-  ],
-  MonitorRefs = monitor_all(Siblings),
-  ets:insert(?MONITOR_TABLE, MonitorRefs),
-  {noreply, State#state{init = false}};
-
-% bot went down, setup a restart timer for it
-handle_info({'DOWN', Ref, process, Pid, _Info} = _Msg, State) ->
-  case ets:lookup(?MONITOR_TABLE, Ref) of
-    [] ->
-      % not a thing that was intentionally monitored (?!?)
-      ignore;
-    [{Ref, Name}] ->
-      ets:delete(?MONITOR_TABLE, Ref),
-      case Pid of
-        undefined ->
-          ok;
-        _ when is_pid(Pid) ->
-          Report = [{bot, Name}, {restart_after, ?RESTART_INTERVAL}],
-          log_info("bot went down", Report)
-      end,
-      erlang:send_after(?RESTART_INTERVAL, self(), {restart, Name})
-  end,
-  {noreply, State};
-
-% timer expired, restart the sibling
-handle_info({restart, SiblingName} = _Msg, State = #state{parent = Parent}) ->
-  log_info("restart timer fired", [{bot, SiblingName}]),
-  case supervisor:restart_child(Parent, SiblingName) of
-    {ok, undefined = Pid} ->
-      log_info("bot not started: network problem", [{bot, SiblingName}]),
-      % pretend the sibling started and try to monitor it
-      MonitorRefs = monitor_all([{SiblingName, Pid}]),
-      ets:insert(?MONITOR_TABLE, MonitorRefs);
-    {ok, Pid} ->
-      log_info("bot started successfully", [{bot, SiblingName}, {pid, Pid}]),
-      MonitorRefs = monitor_all([{SiblingName, Pid}]),
-      ets:insert(?MONITOR_TABLE, MonitorRefs);
-    {error, running} ->
-      log_info("bot already started", [{bot, SiblingName}]),
-      Siblings = [
-        {Name, Pid} ||
-        {Name, Pid, worker, _Modules} <- supervisor:which_children(Parent), 
-        Pid /= self(), Name == SiblingName
-      ],
-      MonitorRefs = monitor_all(Siblings),
-      ets:insert(?MONITOR_TABLE, MonitorRefs);
-    {error, Reason} ->
-      log_error("some error", [{bot, SiblingName}, {error, Reason}]),
-      'TODO' % stop
-  end,
-  {noreply, State};
-
-handle_info(_Msg, State) ->
-  {noreply, State}.
-
-%% }}}
-%%----------------------------------------------------------
-%% code change {{{
-
-%% @private
-%% @doc Handle code change.
-
-code_change(_OldVsn, State, _Extra) ->
-  {ok, State}.
-
-%% }}}
-%%----------------------------------------------------------
-%% support functions {{{
-
-monitor_all([] = _Processes) ->
-  [];
-
-monitor_all([{Name, Pid} | Rest] = _Processes) ->
-  case Pid of
-    undefined ->
-      % send a fake DOWN message about missing child
-      MonitorRef = make_ref(),
-      self() ! {'DOWN', MonitorRef, process, undefined, undefined};
-    _ when is_pid(Pid) ->
-      MonitorRef = erlang:monitor(process, Pid)
-  end,
-  Record = {MonitorRef, Name},
-  [Record | monitor_all(Rest)].
-
-
-log_info(Message, Report) ->
-  error_logger:info_report(haircut_restarter, [{msg, Message} | Report]).
-
-%log_warn(Message, Report) ->
-%  error_logger:warning_report(haircut_restarter, [{msg, Message} | Report]).
-
-log_error(Message, Report) ->
-  error_logger:error_report(haircut_restarter, [{msg, Message} | Report]).
-
-%% }}}
-%%----------------------------------------------------------
-
-%%%---------------------------------------------------------------------------
-%%% vim:ft=erlang:foldmethod=marker
index 67e62a1..cded32a 100644 (file)
@@ -27,10 +27,7 @@ init([] = _Args) ->
   Children = [
     {haircut_bot,
       {haircut_bot, start_link, []},
-      permanent, 5000, worker, [haircut_bot]},
-    {haircut_bot_restarter,
-      {haircut_bot_restarter, start_link, []},
-      permanent, 5000, worker, [haircut_bot_restarter]}
+      permanent, 5000, worker, [haircut_bot]}
   ],
   {ok, {
     {one_for_one, 5, 10},