Merge remote-tracking branch 'upstream/master' into 1843-feature-bigcouch

Conflicts:
	src/config.erl
diff --git a/src/config.erl b/src/config.erl
index 20640bc..d123b12 100644
--- a/src/config.erl
+++ b/src/config.erl
@@ -18,9 +18,19 @@
 
 -module(config).
 -behaviour(gen_server).
+-vsn(1).
 
 -export([start_link/1, stop/0, reload/0]).
--export([all/0, get/1, get/2, get/3, set/3, set/4, delete/2, delete/3]).
+
+-export([all/0]).
+-export([get/1, get/2, get/3]).
+-export([set/3, set/4, set/5]).
+-export([delete/2, delete/3, delete/4]).
+
+-export([get_integer/3, set_integer/3]).
+-export([get_float/3, set_float/3]).
+-export([get_boolean/3, set_boolean/3]).
+
 -export([listen_for_changes/2]).
 -export([parse_ini_file/1]).
 
@@ -47,10 +57,78 @@
 all() ->
     lists:sort(gen_server:call(?MODULE, all, infinity)).
 
+get_integer(Section, Key, Default) when is_integer(Default) ->
+    try
+        to_integer(get(Section, Key, Default))
+    catch
+        error:badarg ->
+            Default
+    end.
+
+set_integer(Section, Key, Value) when is_integer(Value) ->
+    set(Section, Key, integer_to_list(Value));
+set_integer(_, _, _) ->
+    error(badarg).
+
+to_integer(List) when is_list(List) ->
+    list_to_integer(List);
+to_integer(Int) when is_integer(Int) ->
+    Int;
+to_integer(Bin) when is_binary(Bin) ->
+    binary_to_list(list_to_integer(Bin)).
+
+get_float(Section, Key, Default) when is_float(Default) ->
+    try
+        to_float(get(Section, Key, Default))
+    catch
+        error:badarg ->
+            Default
+    end.
+
+set_float(Section, Key, Value) when is_float(Value) ->
+    set(Section, Key, float_to_list(Value));
+set_float(_, _, _) ->
+    error(badarg).
+
+to_float(List) when is_list(List) ->
+    list_to_float(List);
+to_float(Float) when is_float(Float) ->
+    Float;
+to_float(Int) when is_integer(Int) ->
+    list_to_float(integer_to_list(Int));
+to_float(Bin) when is_binary(Bin) ->
+    binary_to_list(list_to_float(Bin)).
+
+get_boolean(Section, Key, Default) when is_boolean(Default) ->
+    try
+        to_boolean(get(Section, Key, Default))
+    catch
+        error:badarg ->
+            Default
+    end.
+
+set_boolean(Section, Key, true) ->
+    set(Section, Key, "true");
+set_boolean(Section, Key, false) ->
+    set(Section, Key, "false");
+set_boolean(_, _, _) ->
+    error(badarg).
+
+to_boolean(List) when is_list(List) ->
+    case list_to_existing_atom(List) of
+        true  ->
+            true;
+        false ->
+            false;
+        _ ->
+            error(badarg)
+    end;
+to_boolean(Bool) when is_boolean(Bool) ->
+    Bool.
 
 get(Section) when is_binary(Section) ->
     ?MODULE:get(binary_to_list(Section));
-get(Section) ->
+get(Section) when is_list(Section) ->
     Matches = ets:match(?MODULE, {{Section, '$1'}, '$2'}),
     [{Key, Value} || [Key, Value] <- Matches].
 
@@ -59,30 +137,42 @@
 
 get(Section, Key, Default) when is_binary(Section) and is_binary(Key) ->
     ?MODULE:get(binary_to_list(Section), binary_to_list(Key), Default);
-get(Section, Key, Default) ->
+get(Section, Key, Default) when is_list(Section), is_list(Key) ->
     case ets:lookup(?MODULE, {Section, Key}) of
         [] -> Default;
         [{_, Match}] -> Match
     end.
 
 set(Section, Key, Value) ->
-    ?MODULE:set(Section, Key, Value, true).
+    ?MODULE:set(Section, Key, Value, true, nil).
 
-set(Section, Key, Value, Persist) when is_binary(Section) and is_binary(Key)  ->
-    ?MODULE:set(binary_to_list(Section), binary_to_list(Key), Value, Persist);
-set(Section, Key, Value, Persist) ->
-    gen_server:call(?MODULE, {set, Section, Key, Value, Persist}).
+set(Section, Key, Value, Persist) when is_boolean(Persist) ->
+    ?MODULE:set(Section, Key, Value, Persist, nil);
+set(Section, Key, Value, Reason) ->
+    ?MODULE:set(Section, Key, Value, true, Reason).
+
+set(Sec, Key, Val, Persist, Reason) when is_binary(Sec) and is_binary(Key) ->
+    ?MODULE:set(binary_to_list(Sec), binary_to_list(Key), Val, Persist, Reason);
+set(Section, Key, Value, Persist, Reason)
+        when is_list(Section), is_list(Key), is_list(Value) ->
+    gen_server:call(?MODULE, {set, Section, Key, Value, Persist, Reason}).
 
 
 delete(Section, Key) when is_binary(Section) and is_binary(Key) ->
     delete(binary_to_list(Section), binary_to_list(Key));
 delete(Section, Key) ->
-    delete(Section, Key, true).
+    delete(Section, Key, true, nil).
 
-delete(Section, Key, Persist) when is_binary(Section) and is_binary(Key) ->
-    delete(binary_to_list(Section), binary_to_list(Key), Persist);
-delete(Section, Key, Persist) ->
-    gen_server:call(?MODULE, {delete, Section, Key, Persist}).
+delete(Section, Key, Persist) when is_boolean(Persist) ->
+    delete(Section, Key, Persist, nil);
+delete(Section, Key, Reason) ->
+    delete(Section, Key, true, Reason).
+
+delete(Sec, Key, Persist, Reason) when is_binary(Sec) and is_binary(Key) ->
+    delete(binary_to_list(Sec), binary_to_list(Key), Persist, Reason);
+delete(Section, Key, Persist, Reason) when is_list(Section), is_list(Key) ->
+    gen_server:call(?MODULE, {delete, Section, Key, Persist, Reason}).
+
 
 listen_for_changes(CallbackModule, InitialState) ->
     config_listener:start(CallbackModule, InitialState).
@@ -108,9 +198,10 @@
 handle_call(all, _From, Config) ->
     Resp = lists:sort((ets:tab2list(?MODULE))),
     {reply, Resp, Config};
-handle_call({set, Sec, Key, Val, Persist}, _From, Config) ->
+handle_call({set, Sec, Key, Val, Persist, Reason}, _From, Config) ->
     true = ets:insert(?MODULE, {{Sec, Key}, Val}),
-    couch_log:notice("~p: [~s] ~s set to ~s", [?MODULE, Sec, Key, Val]),
+    couch_log:notice("~p: [~s] ~s set to ~s for reason ~p",
+        [?MODULE, Sec, Key, Val, Reason]),
     case {Persist, Config#config.write_filename} of
         {true, undefined} ->
             ok;
@@ -122,9 +213,10 @@
     Event = {config_change, Sec, Key, Val, Persist},
     gen_event:sync_notify(config_event, Event),
     {reply, ok, Config};
-handle_call({delete, Sec, Key, Persist}, _From, Config) ->
+handle_call({delete, Sec, Key, Persist, Reason}, _From, Config) ->
     true = ets:delete(?MODULE, {Sec,Key}),
-    couch_log:notice("~p: [~s] ~s deleted", [?MODULE, Sec, Key]),
+    couch_log:notice("~p: [~s] ~s deleted for reason ~p",
+        [?MODULE, Sec, Key, Reason]),
     case {Persist, Config#config.write_filename} of
         {true, undefined} ->
             ok;
diff --git a/src/config_listener.erl b/src/config_listener.erl
index 3335b45..79d3806 100644
--- a/src/config_listener.erl
+++ b/src/config_listener.erl
@@ -13,6 +13,7 @@
 -module(config_listener).
 
 -behaviour(gen_event).
+-vsn(1).
 
 %% Public interface
 -export([start/2]).