changeset 722:3e816cebed2c

Irccd: make plugin pure abstract, closes #796 @3h
author David Demelier <markand@malikania.fr>
date Mon, 16 Jul 2018 21:19:47 +0200
parents 2fa1f2c898ee
children e8c4ba5ed1c6
files libirccd-js/irccd/js/directory_jsapi.cpp libirccd-js/irccd/js/duktape.hpp libirccd-js/irccd/js/elapsed_timer_jsapi.cpp libirccd-js/irccd/js/file_jsapi.cpp libirccd-js/irccd/js/irccd_jsapi.cpp libirccd-js/irccd/js/js_plugin.cpp libirccd-js/irccd/js/js_plugin.hpp libirccd-js/irccd/js/logger_jsapi.cpp libirccd-js/irccd/js/plugin_jsapi.cpp libirccd-js/irccd/js/server_jsapi.cpp libirccd-js/irccd/js/system_jsapi.cpp libirccd-js/irccd/js/timer_jsapi.cpp libirccd-js/irccd/js/unicode_jsapi.cpp libirccd-js/irccd/js/util_jsapi.cpp libirccd-test/irccd/test/js_test.hpp libirccd-test/irccd/test/plugin_test.cpp libirccd-test/irccd/test/plugin_test.hpp libirccd/irccd/daemon/command/plugin_config_command.cpp libirccd/irccd/daemon/command/plugin_info_command.cpp libirccd/irccd/daemon/command/plugin_list_command.cpp libirccd/irccd/daemon/dynlib_plugin.cpp libirccd/irccd/daemon/dynlib_plugin.hpp libirccd/irccd/daemon/plugin.cpp libirccd/irccd/daemon/plugin.hpp libirccd/irccd/daemon/rule.cpp libirccd/irccd/daemon/rule.hpp libirccd/irccd/daemon/server_util.cpp libirccd/irccd/daemon/server_util.hpp libirccd/irccd/daemon/service/plugin_service.cpp libirccd/irccd/daemon/service/plugin_service.hpp libirccd/irccd/daemon/service/rule_service.cpp libirccd/irccd/daemon/service/rule_service.hpp libirccd/irccd/daemon/service/server_service.cpp plugins/ask/ask.js plugins/auth/auth.js plugins/hangman/hangman.js plugins/history/history.js plugins/joke/joke.js plugins/links/links.cpp plugins/logger/logger.js plugins/plugin/plugin.js plugins/roulette/roulette.js plugins/tictactoe/tictactoe.js tests/src/irccdctl/cli-plugin-config/main.cpp tests/src/irccdctl/cli-plugin-info/main.cpp tests/src/irccdctl/cli-plugin-list/main.cpp tests/src/irccdctl/cli-plugin-load/main.cpp tests/src/irccdctl/cli-plugin-reload/main.cpp tests/src/irccdctl/cli-plugin-unload/main.cpp tests/src/libirccd-js/js-plugin/main.cpp tests/src/libirccd-js/jsapi-directory/main.cpp tests/src/libirccd-js/jsapi-elapsedtimer/main.cpp tests/src/libirccd-js/jsapi-file/main.cpp tests/src/libirccd-js/jsapi-irccd/main.cpp tests/src/libirccd-js/jsapi-logger/empty.js tests/src/libirccd-js/jsapi-logger/main.cpp tests/src/libirccd-js/jsapi-system/main.cpp tests/src/libirccd-js/jsapi-timer/main.cpp tests/src/libirccd-js/jsapi-unicode/main.cpp tests/src/libirccd-js/jsapi-util/main.cpp tests/src/libirccd/command-plugin-config/main.cpp tests/src/libirccd/command-plugin-info/main.cpp tests/src/libirccd/command-plugin-list/main.cpp tests/src/libirccd/command-plugin-load/main.cpp tests/src/libirccd/command-plugin-reload/main.cpp tests/src/libirccd/command-plugin-unload/main.cpp tests/src/libirccd/dynlib-plugin/main.cpp tests/src/libirccd/dynlib-plugin/test_plugin.cpp tests/src/plugins/ask/CMakeLists.txt tests/src/plugins/ask/main.cpp tests/src/plugins/auth/CMakeLists.txt tests/src/plugins/auth/main.cpp tests/src/plugins/hangman/CMakeLists.txt tests/src/plugins/hangman/main.cpp tests/src/plugins/history/CMakeLists.txt tests/src/plugins/history/main.cpp tests/src/plugins/joke/CMakeLists.txt tests/src/plugins/joke/main.cpp tests/src/plugins/logger/CMakeLists.txt tests/src/plugins/logger/main.cpp tests/src/plugins/plugin/CMakeLists.txt tests/src/plugins/plugin/main.cpp tests/src/plugins/tictactoe/CMakeLists.txt tests/src/plugins/tictactoe/main.cpp
diffstat 84 files changed, 1302 insertions(+), 1083 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-js/irccd/js/directory_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/directory_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -351,26 +351,26 @@
 
 void directory_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), Directory_constructor, 2);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_put_function_list(plugin->context(), -1, functions);
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_c_function(plugin->get_context(), Directory_constructor, 2);
+    duk_put_number_list(plugin->get_context(), -1, constants);
+    duk_put_function_list(plugin->get_context(), -1, functions);
 
 #if BOOST_OS_WINDOWS
-    duk_push_string(plugin->context(), "\\");
+    duk_push_string(plugin->get_context(), "\\");
 #else
-    duk_push_string(plugin->context(), "/");
+    duk_push_string(plugin->get_context(), "/");
 #endif
 
-    duk_put_prop_string(plugin->context(), -2, "separator");
+    duk_put_prop_string(plugin->get_context(), -2, "separator");
 
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Directory");
-    duk_pop(plugin->context());
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, methods);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "Directory");
+    duk_pop(plugin->get_context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/js/duktape.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/duktape.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -32,6 +32,7 @@
 #include <iterator>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <type_traits>
 #include <utility>
 
@@ -750,6 +751,60 @@
 };
 
 /**
+ * \brief Specialization for C++ std::string_view.
+ */
+template <>
+class dukx_type_traits<std::string_view> : public std::true_type {
+public:
+    /**
+     * Push a C++ std::string_view.
+     *
+     * Uses duk_push_lstring
+     *
+     * \param ctx the Duktape context
+     * \param value the value
+     */
+    static void push(duk_context* ctx, std::string_view value)
+    {
+        duk_push_lstring(ctx, value.data(), value.size());
+    }
+
+    /**
+     * Get a C++ std::string_view.
+     *
+     * Uses duk_get_lstring.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static std::string_view get(duk_context* ctx, duk_idx_t index)
+    {
+        duk_size_t length;
+        const char* str = duk_get_lstring(ctx, index, &length);
+
+        return {str, length};
+    }
+
+    /**
+     * Require a C++ std::string_view.
+     *
+     * Uses duk_require_lstring.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static std::string_view require(duk_context* ctx, duk_idx_t index)
+    {
+        duk_size_t length;
+        const char* str = duk_require_lstring(ctx, index, &length);
+
+        return {str, length};
+    }
+};
+
+/**
  * \brief Specialization for dukx_error.
  */
 template <>
--- a/libirccd-js/irccd/js/elapsed_timer_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/elapsed_timer_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -156,17 +156,17 @@
 
 void elapsed_timer_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), ElapsedTimer_constructor, 0);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), ElapsedTimer_destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "ElapsedTimer");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_c_function(plugin->get_context(), ElapsedTimer_constructor, 0);
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, methods);
+    duk_push_c_function(plugin->get_context(), ElapsedTimer_destructor, 1);
+    duk_set_finalizer(plugin->get_context(), -2);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "ElapsedTimer");
+    duk_pop(plugin->get_context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/js/file_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/file_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -662,21 +662,21 @@
 
 void file_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), File_constructor, 2);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), File_destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_dup(plugin->context(), -1);
-    duk_put_global_string(plugin->context(), prototype);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "File");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_c_function(plugin->get_context(), File_constructor, 2);
+    duk_put_number_list(plugin->get_context(), -1, constants);
+    duk_put_function_list(plugin->get_context(), -1, functions);
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, methods);
+    duk_push_c_function(plugin->get_context(), File_destructor, 1);
+    duk_set_finalizer(plugin->get_context(), -2);
+    duk_dup(plugin->get_context(), -1);
+    duk_put_global_string(plugin->get_context(), prototype);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "File");
+    duk_pop(plugin->get_context());
 }
 
 using file_traits = dukx_type_traits<std::shared_ptr<file>>;
--- a/libirccd-js/irccd/js/irccd_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/irccd_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -178,44 +178,44 @@
 
 void irccd_jsapi::load(irccd& irccd, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
     // irccd.
-    duk_push_object(plugin->context());
+    duk_push_object(plugin->get_context());
 
     // Version.
-    duk_push_object(plugin->context());
-    dukx_push(plugin->context(), IRCCD_VERSION_MAJOR);
-    duk_put_prop_string(plugin->context(), -2, "major");
-    dukx_push(plugin->context(), IRCCD_VERSION_MINOR);
-    duk_put_prop_string(plugin->context(), -2, "minor");
-    dukx_push(plugin->context(), IRCCD_VERSION_PATCH);
-    duk_put_prop_string(plugin->context(), -2, "patch");
-    duk_put_prop_string(plugin->context(), -2, "version");
+    duk_push_object(plugin->get_context());
+    dukx_push(plugin->get_context(), IRCCD_VERSION_MAJOR);
+    duk_put_prop_string(plugin->get_context(), -2, "major");
+    dukx_push(plugin->get_context(), IRCCD_VERSION_MINOR);
+    duk_put_prop_string(plugin->get_context(), -2, "minor");
+    dukx_push(plugin->get_context(), IRCCD_VERSION_PATCH);
+    duk_put_prop_string(plugin->get_context(), -2, "patch");
+    duk_put_prop_string(plugin->get_context(), -2, "version");
 
     // Create the system_error that inherits from Error.
-    duk_push_c_function(plugin->context(), constructor, 2);
+    duk_push_c_function(plugin->get_context(), constructor, 2);
 
     // Put errno codes into the irccd.system_error object.
     for (const auto& pair : errors) {
-        duk_push_int(plugin->context(), pair.second);
-        duk_put_prop_string(plugin->context(), -2, pair.first.c_str());
+        duk_push_int(plugin->get_context(), pair.second);
+        duk_put_prop_string(plugin->get_context(), -2, pair.first.c_str());
     }
 
-    duk_push_object(plugin->context());
-    duk_get_global_string(plugin->context(), "Error");
-    duk_get_prop_string(plugin->context(), -1, "prototype");
-    duk_remove(plugin->context(), -2);
-    duk_set_prototype(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "SystemError");
+    duk_push_object(plugin->get_context());
+    duk_get_global_string(plugin->get_context(), "Error");
+    duk_get_prop_string(plugin->get_context(), -1, "prototype");
+    duk_remove(plugin->get_context(), -2);
+    duk_set_prototype(plugin->get_context(), -2);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "SystemError");
 
     // Set irccd as global.
-    duk_put_global_string(plugin->context(), "Irccd");
+    duk_put_global_string(plugin->get_context(), "Irccd");
 
     // Store global instance.
-    duk_push_pointer(plugin->context(), &irccd);
-    duk_put_global_string(plugin->context(), "\xff""\xff""irccd-ref");
+    duk_push_pointer(plugin->get_context(), &irccd);
+    duk_put_global_string(plugin->get_context(), "\xff""\xff""irccd-ref");
 }
 
 irccd& dukx_type_traits<irccd>::self(duk_context *ctx)
--- a/libirccd-js/irccd/js/js_plugin.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/js_plugin.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -41,42 +41,59 @@
 
 namespace irccd {
 
-const std::string js_plugin::config_property{"\xff""\xff""irccd-plugin-config"};
-const std::string js_plugin::format_property{"\xff""\xff""irccd-plugin-format"};
-const std::string js_plugin::paths_property{"\xff""\xff""irccd-plugin-paths"};
+namespace {
 
-std::unordered_map<std::string, std::string> js_plugin::get_table(const std::string& name) const
+auto get_metadata(dukx_context& ctx, std::string_view name) -> std::string_view
 {
-    dukx_stack_assert sa(context_);
-    std::unordered_map<std::string, std::string> result;
+    std::string_view ret;
+
+    dukx_stack_assert guard(ctx);
+    duk_get_global_string(ctx, "info");
 
-    duk_get_global_string(context_, name.c_str());
-    duk_enum(context_, -1, 0);
-
-    while (duk_next(context_, -1, true)) {
-        result.emplace(duk_to_string(context_, -2), duk_to_string(context_, -1));
-        duk_pop_n(context_, 2);
+    if (duk_get_type(ctx, -1) == DUK_TYPE_OBJECT) {
+        duk_get_prop_string(ctx, -1, name.data());
+        ret = duk_get_string(ctx, -1);
+        duk_pop(ctx);
     }
 
-    duk_pop_n(context_, 2);
+    duk_pop(ctx);
+
+    return ret;
+}
+
+auto get_table(dukx_context& ctx, std::string_view name) -> plugin::map
+{
+    plugin::map result;
+
+    dukx_stack_assert sa(ctx);
+    duk_get_global_string(ctx, name.data());
+    duk_enum(ctx, -1, 0);
+
+    while (duk_next(ctx, -1, true)) {
+        result.emplace(duk_to_string(ctx, -2), duk_to_string(ctx, -1));
+        duk_pop_n(ctx, 2);
+    }
+
+    duk_pop_n(ctx, 2);
 
     return result;
 }
 
-void js_plugin::put_table(const std::string& name, const std::unordered_map<std::string, std::string>& vars)
+void set_table(dukx_context& ctx, std::string_view name, const plugin::map& vars)
 {
-    dukx_stack_assert sa(context_);
-
-    duk_get_global_string(context_, name.c_str());
+    dukx_stack_assert sa(ctx);
+    duk_get_global_string(ctx, name.data());
 
     for (const auto& pair : vars) {
-        dukx_push(context_, pair.second);
-        duk_put_prop_string(context_, -2, pair.first.c_str());
+        dukx_push(ctx, pair.second);
+        duk_put_prop_string(ctx, -2, pair.first.c_str());
     }
 
-    duk_pop(context_);
+    duk_pop(ctx);
 }
 
+} // !namespace
+
 void js_plugin::push() noexcept
 {
 }
@@ -108,8 +125,8 @@
     duk_pop(context_);
 }
 
-js_plugin::js_plugin(std::string name, std::string path)
-    : plugin(name, path)
+js_plugin::js_plugin(std::string_view path)
+    : path_(path)
 {
     dukx_stack_assert sa(context_);
 
@@ -124,23 +141,81 @@
      * In js_plugin_module.cpp.
      */
     duk_push_object(context_);
-    duk_put_global_string(context_, config_property.c_str());
+    duk_put_global_string(context_, config_property.data());
     duk_push_object(context_);
-    duk_put_global_string(context_, format_property.c_str());
+    duk_put_global_string(context_, format_property.data());
     duk_push_object(context_);
-    duk_put_global_string(context_, paths_property.c_str());
+    duk_put_global_string(context_, paths_property.data());
 
     duk_push_pointer(context_, this);
     duk_put_global_string(context_, "\xff""\xff""plugin");
-    dukx_push(context_, name);
-    duk_put_global_string(context_, "\xff""\xff""name");
     dukx_push(context_, path);
     duk_put_global_string(context_, "\xff""\xff""path");
 }
 
+auto js_plugin::get_context() noexcept -> dukx_context&
+{
+    return context_;
+}
+
+auto js_plugin::get_name() const noexcept -> std::string_view
+{
+    return get_metadata(context_, "name");
+}
+
+auto js_plugin::get_author() const noexcept -> std::string_view
+{
+    return get_metadata(context_, "author");
+}
+
+auto js_plugin::get_license() const noexcept -> std::string_view
+{
+    return get_metadata(context_, "license");
+}
+
+auto js_plugin::get_summary() const noexcept -> std::string_view
+{
+    return get_metadata(context_, "summary");
+}
+
+auto js_plugin::get_version() const noexcept -> std::string_view
+{
+    return get_metadata(context_, "version");
+}
+
+auto js_plugin::get_options() const -> map
+{
+    return get_table(context_, config_property);
+}
+
+void js_plugin::set_options(const map& map)
+{
+    set_table(context_, config_property, map);
+}
+
+auto js_plugin::get_formats() const -> map
+{
+    return get_table(context_, format_property);
+}
+
+void js_plugin::set_formats(const map& map)
+{
+    set_table(context_, format_property, map);
+}
+
+auto js_plugin::get_paths() const -> map
+{
+    return get_table(context_, paths_property);
+}
+
+void js_plugin::set_paths(const map& map)
+{
+    set_table(context_, paths_property, map);
+}
+
 void js_plugin::open()
 {
-    std::ifstream input(get_path());
+    std::ifstream input(path_);
 
     if (!input)
         throw plugin_error(plugin_error::exec_error, get_name(), std::strerror(errno));
@@ -152,23 +227,6 @@
 
     if (duk_peval_string(context_, data.c_str()))
         throw plugin_error(plugin_error::exec_error, get_name(), dukx_stack(context_, -1).stack());
-
-    // Read metadata.
-    duk_get_global_string(context_, "info");
-
-    if (duk_get_type(context_, -1) == DUK_TYPE_OBJECT) {
-        duk_get_prop_string(context_, -1, "author");
-        set_author(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : get_author());
-        duk_get_prop_string(context_, -2, "license");
-        set_license(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : get_license());
-        duk_get_prop_string(context_, -3, "summary");
-        set_summary(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : get_summary());
-        duk_get_prop_string(context_, -4, "version");
-        set_version(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : get_version());
-        duk_pop_n(context_, 4);
-    }
-
-    duk_pop(context_);
 }
 
 void js_plugin::handle_command(irccd&, const message_event& event)
@@ -262,7 +320,7 @@
     call("onWhois", event.server, event.whois);
 }
 
-std::unique_ptr<js_plugin_loader> js_plugin_loader::defaults(irccd& irccd)
+auto js_plugin_loader::defaults(irccd& irccd) -> std::unique_ptr<js_plugin_loader>
 {
     auto self = std::make_unique<js_plugin_loader>(irccd);
 
@@ -289,13 +347,22 @@
 
 js_plugin_loader::~js_plugin_loader() noexcept = default;
 
-std::shared_ptr<plugin> js_plugin_loader::open(const std::string& id,
-                                               const std::string& path)
+auto js_plugin_loader::get_modules() const noexcept -> const modules_t&
+{
+    return modules_;
+}
+
+auto js_plugin_loader::get_modules() noexcept -> modules_t&
+{
+    return modules_;
+}
+
+auto js_plugin_loader::open(std::string_view, std::string_view path) -> std::shared_ptr<plugin>
 {
     if (path.rfind(".js") == std::string::npos)
         return nullptr;
 
-    auto plugin = std::make_shared<js_plugin>(id, path);
+    auto plugin = std::make_shared<js_plugin>(path);
 
     for (const auto& mod : modules_)
         mod->load(irccd_, plugin);
--- a/libirccd-js/irccd/js/js_plugin.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/js_plugin.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -24,8 +24,6 @@
  * \brief JavaScript plugins for irccd.
  */
 
-#include <vector>
-
 #include <irccd/daemon/plugin.hpp>
 #include <irccd/daemon/server.hpp>
 
@@ -44,25 +42,24 @@
     /**
      * Global property where to read/write plugin configuration (object).
      */
-    static const std::string config_property;
+    static inline const std::string_view config_property{"\xff\xff""config"};
 
     /**
      * Global property where to read/write plugin formats (object).
      */
-    static const std::string format_property;
+    static inline const std::string_view format_property{"\xff\xff""formats"};
 
     /**
      * Global property where paths are defined (object).
      */
-    static const std::string paths_property;
+    static inline const std::string_view paths_property{"\xff\xff""paths"};
 
 private:
-    // JavaScript context
-    dukx_context context_;
+    // JavaScript context.
+    mutable dukx_context context_;
 
-    // Private helpers.
-    std::unordered_map<std::string, std::string> get_table(const std::string&) const;
-    void put_table(const std::string&, const std::unordered_map<std::string, std::string>&);
+    // Path to Javascript script file.
+    std::string path_;
 
     /*
      * Helpers to call a Javascript function.
@@ -79,20 +76,16 @@
     /**
      * Constructor.
      *
-     * \param name the plugin name
      * \param path the path to the plugin
      */
-    js_plugin(std::string name, std::string path);
+    js_plugin(std::string_view path);
 
     /**
      * Access the Duktape context.
      *
      * \return the context
      */
-    inline dukx_context& context() noexcept
-    {
-        return context_;
-    }
+    auto get_context() noexcept -> dukx_context&;
 
     /**
      * Open the script file associated.
@@ -100,52 +93,59 @@
     void open();
 
     /**
-     * \copydoc plugin::config
+     * \copydoc plugin::get_name
      */
-    plugin_config get_config() override
-    {
-        return get_table(config_property);
-    }
+    auto get_name() const noexcept -> std::string_view override;
+
+    /**
+     * \copydoc plugin::get_author
+     */
+    auto get_author() const noexcept -> std::string_view override;
 
     /**
-     * \copydoc plugin::setConfig
+     * \copydoc plugin::get_license
+     */
+    auto get_license() const noexcept -> std::string_view override;
+
+    /**
+     * \copydoc plugin::get_summary
      */
-    void set_config(plugin_config config) override
-    {
-        put_table(config_property, config);
-    }
+    auto get_summary() const noexcept -> std::string_view override;
+
+    /**
+     * \copydoc plugin::get_version
+     */
+    auto get_version() const noexcept -> std::string_view override;
 
     /**
-     * \copydoc plugin::formats
+     * \copydoc plugin::get_options
      */
-    plugin_formats get_formats() override
-    {
-        return get_table(format_property);
-    }
+    auto get_options() const -> map override;
+
+    /**
+     * \copydoc plugin::set_options
+     */
+    void set_options(const map& map) override;
 
     /**
-     * \copydoc plugin::setFormats
+     * \copydoc plugin::get_formats
      */
-    void set_formats(plugin_formats formats) override
-    {
-        put_table(format_property, formats);
-    }
+    auto get_formats() const -> map override;
 
     /**
-     * \copydoc plugin::paths
+     * \copydoc plugin::set_formats
      */
-    plugin_paths get_paths() override
-    {
-        return get_table(paths_property);
-    }
+    void set_formats(const map& map) override;
 
     /**
-     * \copydoc plugin::set_paths
+     * \copydoc plugin::get_paths
      */
-    void set_paths(plugin_paths paths) override
-    {
-        put_table(paths_property, std::move(paths));
-    }
+    auto get_paths() const -> map override;
+
+    /**
+     * \copydoc plugin::set_path
+     */
+    void set_paths(const map& map) override;
 
     /**
      * \copydoc plugin::handle_command
@@ -256,7 +256,7 @@
      * \param irccd the irccd instance
      * \return a ready to use plugin_loader
      */
-    static std::unique_ptr<js_plugin_loader> defaults(irccd& irccd);
+    static auto defaults(irccd& irccd) -> std::unique_ptr<js_plugin_loader>;
 
     /**
      * Constructor.
@@ -275,26 +275,19 @@
      *
      * \return the modules
      */
-    inline const modules_t& get_modules() const noexcept
-    {
-        return modules_;
-    }
+    auto get_modules() const noexcept -> const modules_t&;
 
     /**
      * Overloaded function.
      *
      * \return the modules
      */
-    inline modules_t& get_modules() noexcept
-    {
-        return modules_;
-    }
+    auto get_modules() noexcept -> modules_t&;
 
     /**
-     * \copydoc PluginLoader::open
+     * \copydoc plugin_loader::open
      */
-    std::shared_ptr<plugin> open(const std::string& id,
-                                 const std::string& path) override;
+    auto open(std::string_view id, std::string_view path) -> std::shared_ptr<plugin>;
 };
 
 template <>
--- a/libirccd-js/irccd/js/logger_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/logger_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -120,13 +120,13 @@
 
 void logger_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Logger");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, functions);
+    duk_put_prop_string(plugin->get_context(), -2, "Logger");
+    duk_pop(plugin->get_context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/js/plugin_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/plugin_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -80,13 +80,13 @@
  *      path: "/var"
  * };
  */
-duk_ret_t set(duk_context* ctx, const std::string& name)
+duk_ret_t set(duk_context* ctx, std::string_view name)
 {
     if (!duk_is_object(ctx, 0))
-        duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name.c_str());
+        duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name.data());
 
     // Merge old table with new one.
-    duk_get_global_string(ctx, name.c_str());
+    duk_get_global_string(ctx, name.data());
     duk_enum(ctx, -1, 0);
 
     while (duk_next(ctx, -1, true))
@@ -96,7 +96,7 @@
     duk_pop_2(ctx);
 
     // Replace the old table with the new assigned one.
-    duk_put_global_string(ctx, name.c_str());
+    duk_put_global_string(ctx, name.data());
 
     return 0;
 }
@@ -107,9 +107,9 @@
  *
  * Get the Irccd.Plugin.(config|format|paths) property.
  */
-duk_ret_t get(duk_context* ctx, const std::string& name)
+duk_ret_t get(duk_context* ctx, std::string_view name)
 {
-    duk_get_global_string(ctx, name.c_str());
+    duk_get_global_string(ctx, name.data());
 
     return 1;
 }
@@ -252,8 +252,8 @@
 
     duk_push_array(ctx);
 
-    for (const auto& p : dukx_type_traits<irccd>::self(ctx).plugins().list()) {
-        dukx_push(ctx, p->get_name());
+    for (const auto& [k, _] : dukx_type_traits<irccd>::self(ctx).plugins().all()) {
+        dukx_push(ctx, k);
         duk_put_prop_index(ctx, -2, i++);
     }
 
@@ -384,11 +384,11 @@
 
 void plugin_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_push_pointer(plugin->context(), new std::weak_ptr<js_plugin>(plugin));
-    duk_push_object(plugin->context());
-    duk_push_c_function(plugin->context(), [] (auto ctx) -> duk_ret_t {
+    duk_push_pointer(plugin->get_context(), new std::weak_ptr<js_plugin>(plugin));
+    duk_push_object(plugin->get_context());
+    duk_push_c_function(plugin->get_context(), [] (auto ctx) -> duk_ret_t {
         duk_get_global_string(ctx, plugin_ref);
         delete static_cast<std::shared_ptr<js_plugin>*>(duk_to_pointer(ctx, -1));
         duk_pop(ctx);
@@ -396,43 +396,43 @@
         duk_put_global_string(ctx, plugin_ref);
         return 0;
     }, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_put_global_string(plugin->context(), "\xff""\xff""dummy-shared-ptr");
-    duk_put_global_string(plugin->context(), plugin_ref);
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
+    duk_set_finalizer(plugin->get_context(), -2);
+    duk_put_global_string(plugin->get_context(), "\xff""\xff""dummy-shared-ptr");
+    duk_put_global_string(plugin->get_context(), plugin_ref);
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, functions);
 
     // 'config' property.
-    duk_push_string(plugin->context(), "config");
-    duk_push_c_function(plugin->context(), get_config, 0);
-    duk_push_c_function(plugin->context(), set_config, 1);
-    duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
+    duk_push_string(plugin->get_context(), "config");
+    duk_push_c_function(plugin->get_context(), get_config, 0);
+    duk_push_c_function(plugin->get_context(), set_config, 1);
+    duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
 
     // 'format' property.
-    duk_push_string(plugin->context(), "format");
-    duk_push_c_function(plugin->context(), get_format, 0);
-    duk_push_c_function(plugin->context(), set_format, 1);
-    duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
+    duk_push_string(plugin->get_context(), "format");
+    duk_push_c_function(plugin->get_context(), get_format, 0);
+    duk_push_c_function(plugin->get_context(), set_format, 1);
+    duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
 
     // 'format' property.
-    duk_push_string(plugin->context(), "paths");
-    duk_push_c_function(plugin->context(), get_paths, 0);
-    duk_push_c_function(plugin->context(), set_paths, 1);
-    duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
+    duk_push_string(plugin->get_context(), "paths");
+    duk_push_c_function(plugin->get_context(), get_paths, 0);
+    duk_push_c_function(plugin->get_context(), set_paths, 1);
+    duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
 
     // PluginError function.
-    duk_push_c_function(plugin->context(), PluginError_constructor, 2);
-    duk_push_object(plugin->context());
-    duk_get_global_string(plugin->context(), "Error");
-    duk_get_prop_string(plugin->context(), -1, "prototype");
-    duk_remove(plugin->context(), -2);
-    duk_set_prototype(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "PluginError");
+    duk_push_c_function(plugin->get_context(), PluginError_constructor, 2);
+    duk_push_object(plugin->get_context());
+    duk_get_global_string(plugin->get_context(), "Error");
+    duk_get_prop_string(plugin->get_context(), -1, "prototype");
+    duk_remove(plugin->get_context(), -2);
+    duk_set_prototype(plugin->get_context(), -2);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "PluginError");
 
-    duk_put_prop_string(plugin->context(), -2, "Plugin");
-    duk_pop(plugin->context());
+    duk_put_prop_string(plugin->get_context(), -2, "Plugin");
+    duk_pop(plugin->get_context());
 }
 
 using plugin_traits = dukx_type_traits<js_plugin>;
--- a/libirccd-js/irccd/js/server_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/server_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -822,32 +822,32 @@
 
 void server_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
+    duk_get_global_string(plugin->get_context(), "Irccd");
 
     // ServerError function.
-    duk_push_c_function(plugin->context(), ServerError_constructor, 2);
-    duk_push_object(plugin->context());
-    duk_get_global_string(plugin->context(), "Error");
-    duk_get_prop_string(plugin->context(), -1, "prototype");
-    duk_remove(plugin->context(), -2);
-    duk_set_prototype(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "ServerError");
+    duk_push_c_function(plugin->get_context(), ServerError_constructor, 2);
+    duk_push_object(plugin->get_context());
+    duk_get_global_string(plugin->get_context(), "Error");
+    duk_get_prop_string(plugin->get_context(), -1, "prototype");
+    duk_remove(plugin->get_context(), -2);
+    duk_set_prototype(plugin->get_context(), -2);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "ServerError");
 
     // Server constructor.
-    duk_push_c_function(plugin->context(), Server_constructor, 1);
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), Server_destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_dup_top(plugin->context());
-    duk_put_global_string(plugin->context(), prototype);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Server");
-    duk_pop(plugin->context());
+    duk_push_c_function(plugin->get_context(), Server_constructor, 1);
+    duk_put_function_list(plugin->get_context(), -1, functions);
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, methods);
+    duk_push_c_function(plugin->get_context(), Server_destructor, 1);
+    duk_set_finalizer(plugin->get_context(), -2);
+    duk_dup_top(plugin->get_context());
+    duk_put_global_string(plugin->get_context(), prototype);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "Server");
+    duk_pop(plugin->get_context());
 }
 
 using server_traits = dukx_type_traits<std::shared_ptr<server>>;
--- a/libirccd-js/irccd/js/system_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/system_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -318,13 +318,13 @@
 
 void system_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "System");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, functions);
+    duk_put_prop_string(plugin->get_context(), -2, "System");
+    duk_pop(plugin->get_context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/js/timer_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/timer_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -80,7 +80,7 @@
     if (!plugin)
         return;
 
-    auto& ctx = plugin->context();
+    auto& ctx = plugin->get_context();
 
     duk_get_global_string(ctx, table);
     duk_get_prop_string(ctx, -1, key().c_str());
@@ -285,18 +285,18 @@
 
 void timer_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), Timer_constructor, 3);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Timer");
-    duk_pop(plugin->context());
-    duk_push_object(plugin->context());
-    duk_put_global_string(plugin->context(), table);
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_c_function(plugin->get_context(), Timer_constructor, 3);
+    duk_put_number_list(plugin->get_context(), -1, constants);
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, methods);
+    duk_put_prop_string(plugin->get_context(), -2, "prototype");
+    duk_put_prop_string(plugin->get_context(), -2, "Timer");
+    duk_pop(plugin->get_context());
+    duk_push_object(plugin->get_context());
+    duk_put_global_string(plugin->get_context(), table);
 }
 
 } // !irccd
--- a/libirccd-js/irccd/js/unicode_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/unicode_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -151,13 +151,13 @@
 
 void unicode_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Unicode");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, functions);
+    duk_put_prop_string(plugin->get_context(), -2, "Unicode");
+    duk_pop(plugin->get_context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/js/util_jsapi.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-js/irccd/js/util_jsapi.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -340,13 +340,13 @@
 
 void util_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    dukx_stack_assert sa(plugin->context());
+    dukx_stack_assert sa(plugin->get_context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Util");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin->get_context(), "Irccd");
+    duk_push_object(plugin->get_context());
+    duk_put_function_list(plugin->get_context(), -1, functions);
+    duk_put_prop_string(plugin->get_context(), -2, "Util");
+    duk_pop(plugin->get_context());
 }
 
 } // !irccd
--- a/libirccd-test/irccd/test/js_test.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-test/irccd/test/js_test.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -43,17 +43,10 @@
 class js_test {
 private:
     template <typename Module>
-    inline void add()
-    {
-        Module().load(irccd_, plugin_);
-    }
+    void add();
 
     template <typename M1, typename M2, typename... Tail>
-    inline void add()
-    {
-        add<M1>();
-        add<M2, Tail...>();
-    }
+    void add();
 
 public:
     boost::asio::io_service service_;
@@ -66,27 +59,48 @@
      *
      * Create a server and a test plugin.
      */
-    js_test(const std::string& plugin_path = "")
-        : plugin_(new js_plugin("test", plugin_path))
-        , server_(new journal_server(service_, "test"))
-    {
-        irccd_.set_log(std::make_unique<silent_logger>());
+    js_test(const std::string& plugin_path = "");
+};
 
-        // Irccd is mandatory at the moment.
-        add<irccd_jsapi>();
-        add<Modules...>();
+template <typename... Modules>
+template <typename Module>
+void js_test<Modules...>::add()
+{
+    Module().load(irccd_, plugin_);
+}
+
+template <typename... Modules>
+template <typename M1, typename M2, typename... Tail>
+void js_test<Modules...>::add()
+{
+    add<M1>();
+    add<M2, Tail...>();
+}
 
-        // Add some CMake variables.
-        duk_push_string(plugin_->context(), CMAKE_BINARY_DIR);
-        duk_put_global_string(plugin_->context(), "CMAKE_BINARY_DIR");
-        duk_push_string(plugin_->context(), CMAKE_SOURCE_DIR);
-        duk_put_global_string(plugin_->context(), "CMAKE_SOURCE_DIR");
-        duk_push_string(plugin_->context(), CMAKE_CURRENT_BINARY_DIR);
-        duk_put_global_string(plugin_->context(), "CMAKE_CURRENT_BINARY_DIR");
-        duk_push_string(plugin_->context(), CMAKE_CURRENT_SOURCE_DIR);
-        duk_put_global_string(plugin_->context(), "CMAKE_CURRENT_SOURCE_DIR");
-    }
-};
+template <typename... Modules>
+js_test<Modules...>::js_test(const std::string& plugin_path)
+    : plugin_(new js_plugin(plugin_path))
+    , server_(new journal_server(service_, "test"))
+{
+    irccd_.set_log(std::make_unique<silent_logger>());
+
+    // Irccd is mandatory at the moment.
+    add<irccd_jsapi>();
+    add<Modules...>();
+
+    // Add some CMake variables.
+    duk_push_string(plugin_->get_context(), CMAKE_BINARY_DIR);
+    duk_put_global_string(plugin_->get_context(), "CMAKE_BINARY_DIR");
+    duk_push_string(plugin_->get_context(), CMAKE_SOURCE_DIR);
+    duk_put_global_string(plugin_->get_context(), "CMAKE_SOURCE_DIR");
+    duk_push_string(plugin_->get_context(), CMAKE_CURRENT_BINARY_DIR);
+    duk_put_global_string(plugin_->get_context(), "CMAKE_CURRENT_BINARY_DIR");
+    duk_push_string(plugin_->get_context(), CMAKE_CURRENT_SOURCE_DIR);
+    duk_put_global_string(plugin_->get_context(), "CMAKE_CURRENT_SOURCE_DIR");
+
+    if (!plugin_path.empty())
+        plugin_->open();
+}
 
 } // !irccd
 
--- a/libirccd-test/irccd/test/plugin_test.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-test/irccd/test/plugin_test.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -40,15 +40,15 @@
 
 namespace irccd {
 
-plugin_test::plugin_test(std::string name, std::string path)
+plugin_test::plugin_test(std::string path)
     : server_(std::make_shared<journal_server>(service_, "test", "local"))
 {
     server_->set_nickname("irccd");
-    plugin_ = std::make_unique<js_plugin>(std::move(name), std::move(path));
+    plugin_ = std::make_unique<js_plugin>(std::move(path));
 
     irccd_.set_log(std::make_unique<silent_logger>());
     irccd_.get_log().set_verbose(false);
-    irccd_.plugins().add(plugin_);
+    irccd_.plugins().add("test", plugin_);
     irccd_.servers().add(server_);
 
     irccd_jsapi().load(irccd_, plugin_);
--- a/libirccd-test/irccd/test/plugin_test.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd-test/irccd/test/plugin_test.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -50,10 +50,9 @@
     /**
      * Construct the fixture test.
      *
-     * \param name the plugin name (e.g. ask)
-     * \param path the full plugin path (e.g. /usr/libexec/irccd/plugins/ask.js)
+     * \param path the full plugin path (e.g. /usr/lib64/irccd/ask.js)
      */
-    plugin_test(std::string name, std::string path);
+    plugin_test(std::string path);
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/command/plugin_config_command.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_config_command.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -42,10 +42,10 @@
     if (value == args.end() || !value->is_string())
         throw irccd_error(irccd_error::error::incomplete_message);
 
-    auto config = plugin.get_config();
+    auto config = plugin.get_options();
 
     config[*var] = *value;
-    plugin.set_config(config);
+    plugin.set_options(config);
     client.success("plugin-config");
 }
 
@@ -55,9 +55,9 @@
     auto var = args.find("variable");
 
     if (var != args.end() && var->is_string())
-        variables[var->get<std::string>()] = plugin.get_config()[*var];
+        variables[var->get<std::string>()] = plugin.get_options()[*var];
     else
-        for (const auto& pair : plugin.get_config())
+        for (const auto& pair : plugin.get_options())
             variables[pair.first] = pair.second;
 
     /*
--- a/libirccd/irccd/daemon/command/plugin_info_command.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_info_command.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -43,11 +43,11 @@
     const auto plugin = irccd.plugins().require(*id);
 
     client.write({
-        { "command",    "plugin-info"           },
-        { "author",     plugin->get_author()    },
-        { "license",    plugin->get_license()   },
-        { "summary",    plugin->get_summary()   },
-        { "version",    plugin->get_version()   }
+        { "command",    "plugin-info"                       },
+        { "author",     std::string(plugin->get_author())   },
+        { "license",    std::string(plugin->get_license())  },
+        { "summary",    std::string(plugin->get_summary())  },
+        { "version",    std::string(plugin->get_version())  }
     });
 }
 
--- a/libirccd/irccd/daemon/command/plugin_list_command.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/command/plugin_list_command.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -16,6 +16,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <algorithm>
+
 #include <irccd/daemon/irccd.hpp>
 #include <irccd/daemon/transport_client.hpp>
 
@@ -34,8 +36,10 @@
 {
     auto list = nlohmann::json::array();
 
-    for (const auto& plugin : irccd.plugins().list())
-        list += plugin->get_name();
+    for (const auto& [key, _] : irccd.plugins().all())
+        list += key;
+
+    std::sort(list.begin(), list.end());
 
     client.write({
         { "command",    "plugin-list"   },
--- a/libirccd/irccd/daemon/dynlib_plugin.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/dynlib_plugin.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -38,9 +38,9 @@
 
 namespace {
 
-auto symbol(const std::string& path) -> std::pair<std::string, std::string>
+auto symbol(std::string_view path) -> std::pair<std::string, std::string>
 {
-    auto id = boost::filesystem::path(path).stem().string();
+    auto id = boost::filesystem::path(std::string(path)).stem().string();
 
     // Remove forbidden characters.
     id.erase(std::remove_if(id.begin(), id.end(), [] (auto c) {
@@ -65,27 +65,29 @@
 {
 }
 
-auto dynlib_plugin_loader::open(const std::string& id,
-                                const std::string& path) -> std::shared_ptr<plugin>
+auto dynlib_plugin_loader::open(std::string_view id, std::string_view path) -> std::shared_ptr<plugin>
 {
-    const auto [ abisym, initsym ] = symbol(path);
+    const std::string idstr(id);
+    const std::string pathstr(path);
+
+    const auto [ abisym, initsym ] = symbol(pathstr);
 
     using abisym_func_type = version ();
     using initsym_func_type = std::unique_ptr<plugin> ();
 
-    const auto abi = boost::dll::import_alias<abisym_func_type>(path, abisym);
-    const auto init = boost::dll::import_alias<initsym_func_type>(path, initsym);
+    const auto abi = boost::dll::import_alias<abisym_func_type>(pathstr, abisym);
+    const auto init = boost::dll::import_alias<initsym_func_type>(pathstr, initsym);
 
     // The abi version is reset after new major version, check for both.
     const version current;
 
     if (current.major != abi().major || current.abi != abi().abi)
-        throw plugin_error(plugin_error::exec_error, id, "incompatible version");
+        throw plugin_error(plugin_error::exec_error, idstr, "incompatible version");
 
     auto plg = init();
 
     if (!plg)
-        throw plugin_error(plugin_error::exec_error, id, "invalid plugin");
+        throw plugin_error(plugin_error::exec_error, idstr, "invalid plugin");
 
     /*
      * We need to keep a reference to `init' variable for the whole plugin
--- a/libirccd/irccd/daemon/dynlib_plugin.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/dynlib_plugin.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -43,8 +43,7 @@
     /**
      * \copydoc plugin_loader::open
      */
-    auto open(const std::string& id,
-              const std::string& file) -> std::shared_ptr<plugin> override;
+    auto open(std::string_view id, std::string_view file) -> std::shared_ptr<plugin> override;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/plugin.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/plugin.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -24,20 +24,26 @@
 
 #include "plugin.hpp"
 
-namespace fs = boost::filesystem;
-
 namespace irccd {
 
-std::shared_ptr<plugin> plugin_loader::find(const std::string& name)
+plugin_loader::plugin_loader(std::vector<std::string> directories,
+              std::vector<std::string> extensions) noexcept
+    : directories_(std::move(directories))
+    , extensions_(std::move(extensions))
+{
+    assert(!extensions_.empty());
+}
+
+auto plugin_loader::find(std::string_view name) -> std::shared_ptr<plugin>
 {
     std::vector<std::string> filenames;
 
     if (directories_.empty())
-        filenames = sys::plugin_filenames(name, extensions_);
+        filenames = sys::plugin_filenames(std::string(name), extensions_);
     else {
         for (const auto& dir : directories_)
             for (const auto& ext : extensions_)
-                filenames.push_back(dir + "/" + name + ext);
+                filenames.push_back(dir + std::string("/") + std::string(name) + ext);
     }
 
     for (const auto& candidate : filenames) {
@@ -55,7 +61,7 @@
     return nullptr;
 }
 
-plugin_error::plugin_error(error errc, std::string name, std::string message) noexcept
+plugin_error::plugin_error(error errc, std::string_view name, std::string_view message)
     : system_error(make_error_code(errc))
     , name_(std::move(name))
     , message_(std::move(message))
@@ -73,16 +79,31 @@
     what_ = oss.str();
 }
 
-const std::error_category& plugin_category()
+auto plugin_error::get_name() const noexcept -> const std::string&
+{
+    return name_;
+}
+
+auto plugin_error::get_message() const noexcept -> const std::string&
+{
+    return message_;
+}
+
+auto plugin_error::what() const noexcept -> const char*
+{
+    return what_.c_str();
+}
+
+auto plugin_category() -> const std::error_category&
 {
     static const class category : public std::error_category {
     public:
-        const char* name() const noexcept override
+        auto name() const noexcept -> const char* override
         {
             return "plugin";
         }
 
-        std::string message(int e) const override
+        auto message(int e) const -> std::string override
         {
             switch (static_cast<plugin_error::error>(e)) {
             case plugin_error::not_found:
@@ -102,7 +123,7 @@
     return category;
 }
 
-std::error_code make_error_code(plugin_error::error e)
+auto make_error_code(plugin_error::error e) -> std::error_code
 {
     return {static_cast<int>(e), plugin_category()};
 }
--- a/libirccd/irccd/daemon/plugin.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/plugin.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -31,9 +31,9 @@
 
 #include <irccd/sysconfig.hpp>
 
-#include <cassert>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <system_error>
 #include <unordered_map>
 #include <vector>
@@ -58,51 +58,19 @@
 class whois_event;
 
 /**
- * \brief Configuration map extract from config file.
- */
-using plugin_config = std::unordered_map<std::string, std::string>;
-
-/**
- * \brief Formats for plugins.
- */
-using plugin_formats = std::unordered_map<std::string, std::string>;
-
-/**
- * \brief Paths for plugins.
- */
-using plugin_paths = std::unordered_map<std::string, std::string>;
-
-/**
  * \ingroup plugins
  * \brief Abstract plugin.
  *
  * A plugin is identified by name and can be loaded and unloaded at runtime.
  */
 class plugin : public std::enable_shared_from_this<plugin> {
-private:
-    // Plugin information
-    std::string name_;
-    std::string path_;
-
-    // Metadata
-    std::string author_{"unknown"};
-    std::string license_{"unknown"};
-    std::string summary_{"unknown"};
-    std::string version_{"unknown"};
-
 public:
     /**
-     * Constructor.
+     * Map for key/value pairs.
      *
-     * \param name the plugin id
-     * \param path the fully resolved path to the plugin
-     * \throws std::runtime_error on errors
+     * Used in options, formats and paths.
      */
-    inline plugin(std::string name, std::string path) noexcept
-        : name_(std::move(name))
-        , path_(std::move(path))
-    {
-    }
+    using map = std::unordered_map<std::string, std::string>;
 
     /**
      * Temporary, close all timers.
@@ -114,40 +82,16 @@
      *
      * \return the plugin name
      */
-    inline const std::string& get_name() const noexcept
-    {
-        return name_;
-    }
-
-    /**
-     * Get the plugin path.
-     *
-     * \return the plugin path
-     * \note some plugins may not exist on the disk
-     */
-    inline const std::string& get_path() const noexcept
-    {
-        return path_;
-    }
+    virtual auto get_name() const noexcept -> std::string_view = 0;
 
     /**
      * Get the author.
      *
      * \return the author
      */
-    inline const std::string& get_author() const noexcept
+    virtual auto get_author() const noexcept -> std::string_view
     {
-        return author_;
-    }
-
-    /**
-     * Set the author.
-     *
-     * \param author the author
-     */
-    inline void set_author(std::string author) noexcept
-    {
-        author_ = std::move(author);
+        return "unknown";
     }
 
     /**
@@ -155,19 +99,9 @@
      *
      * \return the license
      */
-    inline const std::string& get_license() const noexcept
+    virtual auto get_license() const noexcept -> std::string_view
     {
-        return license_;
-    }
-
-    /**
-     * Set the license.
-     *
-     * \param license the license
-     */
-    inline void set_license(std::string license) noexcept
-    {
-        license_ = std::move(license);
+        return "unknown";
     }
 
     /**
@@ -175,19 +109,9 @@
      *
      * \return the summary
      */
-    inline const std::string& get_summary() const noexcept
+    virtual auto get_summary() const noexcept -> std::string_view
     {
-        return summary_;
-    }
-
-    /**
-     * Set the summary.
-     *
-     * \param summary the summary
-     */
-    inline void set_summary(std::string summary) noexcept
-    {
-        summary_ = std::move(summary);
+        return "unknown";
     }
 
     /**
@@ -195,79 +119,69 @@
      *
      * \return the version
      */
-    inline const std::string& get_version() const noexcept
+    virtual auto get_version() const noexcept -> std::string_view
     {
-        return version_;
+        return "unknown";
     }
 
     /**
-     * Set the version.
+     * Get all options.
      *
-     * \param version the version
+     * \return options
      */
-    inline void set_version(std::string version) noexcept
-    {
-        version_ = std::move(version);
-    }
-
-    /**
-     * Access the plugin configuration.
-     *
-     * \return the config
-     */
-    virtual plugin_config get_config()
+    virtual auto get_options() const -> map
     {
         return {};
     }
 
     /**
-     * Set the configuration.
+     * Set all options.
      *
-     * \param config the configuration
+     * \param map the options
      */
-    virtual void set_config(plugin_config config)
+    virtual void set_options(const map& map)
     {
-        (void)config;
+        (void)map;
     }
 
     /**
-     * Access the plugin formats.
+     * Get all formats.
      *
-     * \return the format
+     * \return formats
      */
-    virtual plugin_formats get_formats()
+    virtual auto get_formats() const -> map
     {
         return {};
     }
 
     /**
-     * Set the formats.
+     * Set all formats.
      *
-     * \param formats the formats
+     * \param map the formats
      */
-    virtual void set_formats(plugin_formats formats)
+    virtual void set_formats(const map& map)
     {
-        (void)formats;
+        (void)map;
     }
 
     /**
-     * Access the plugin paths.
+     * Get all paths.
      *
-     * \return the paths
+     * \return paths
      */
-    virtual plugin_paths get_paths()
+    virtual auto get_paths() const -> map
     {
         return {};
     }
 
     /**
-     * Set the paths.
+     * Set all paths.
      *
-     * \param paths the paths
+     * \param map the paths
      */
-    virtual void set_paths(plugin_paths paths)
+    virtual void set_paths(const map& map)
     {
-        (void)paths;
+        (void)map;
     }
 
     /**
@@ -511,41 +425,8 @@
      * \param directories optional list of directories to search
      * \param extensions the non empty list of extensions supported
      */
-    inline plugin_loader(std::vector<std::string> directories,
-                  std::vector<std::string> extensions) noexcept
-        : directories_(std::move(directories))
-        , extensions_(std::move(extensions))
-    {
-        assert(!extensions_.empty());
-    }
-
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~plugin_loader() = default;
-
-    /**
-     * Set directories where to search plugins.
-     *
-     * \param directories the directories
-     */
-    inline void set_directories(std::vector<std::string> directories)
-    {
-        directories_ = std::move(directories);
-    }
-
-    /**
-     * Set supported extensions for this loader.
-     *
-     * \pre !extensions.empty()
-     * \param extensions the extensions (with the dot)
-     */
-    inline void set_extensions(std::vector<std::string> extensions)
-    {
-        assert(!extensions.empty());
-
-        extensions_ = std::move(extensions);
-    }
+    plugin_loader(std::vector<std::string> directories,
+                  std::vector<std::string> extensions) noexcept;
 
     /**
      * Try to open the plugin specified by path.
@@ -555,17 +436,18 @@
      *
      * \param id the plugin identifier
      * \param file the file path
+     * \throw plugin_error on errors
      */
-    virtual std::shared_ptr<plugin> open(const std::string& id,
-                                         const std::string& file) = 0;
+    virtual auto open(std::string_view id, std::string_view file) -> std::shared_ptr<plugin> = 0;
 
     /**
      * Search for a plugin named by this id.
      *
      * \param id the plugin id
      * \return the plugin
+     * \throw plugin_error on errors
      */
-    virtual std::shared_ptr<plugin> find(const std::string& id);
+    virtual auto find(std::string_view id) -> std::shared_ptr<plugin>;
 };
 
 /**
@@ -606,35 +488,26 @@
      * \param name the plugin name
      * \param message the optional message (e.g. error from plugin)
      */
-    plugin_error(error code, std::string name = "", std::string message = "") noexcept;
+    plugin_error(error code, std::string_view name = "", std::string_view message = "");
 
     /**
      * Get the plugin name.
      *
      * \return the name
      */
-    inline const std::string& name() const noexcept
-    {
-        return name_;
-    }
+    auto get_name() const noexcept -> const std::string&;
 
     /**
      * Get the additional message.
      *
      * \return the message
      */
-    inline const std::string& message() const noexcept
-    {
-        return message_;
-    }
+    auto get_message() const noexcept -> const std::string&;
 
     /**
      * Get message appropriate for use with logger.
      */
-    const char* what() const noexcept override
-    {
-        return what_.c_str();
-    }
+    auto what() const noexcept -> const char* override;
 };
 
 /**
@@ -642,14 +515,14 @@
  *
  * \return the singleton
  */
-const std::error_category& plugin_category();
+auto plugin_category() -> const std::error_category&;
 
 /**
  * Create a boost::system::error_code from plugin_error::error enum.
  *
  * \param e the error code
  */
-std::error_code make_error_code(plugin_error::error e);
+auto make_error_code(plugin_error::error e) -> std::error_code ;
 
 } // !irccd
 
--- a/libirccd/irccd/daemon/rule.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/rule.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -38,22 +38,23 @@
 {
 }
 
-bool rule::match(const std::string& server,
-                 const std::string& channel,
-                 const std::string& nick,
-                 const std::string& plugin,
-                 const std::string& event) const noexcept
+bool rule::match(std::string_view server,
+                 std::string_view channel,
+                 std::string_view nick,
+                 std::string_view plugin,
+                 std::string_view event) const noexcept
 {
-    const auto tolower = [] (auto str) noexcept {
-        std::transform(str.begin(), str.end(), str.begin(), ::tolower);
-        return str;
+    const auto tolower = [] (auto str) noexcept -> std::string {
+        std::string ret(str);
+        std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
+        return ret;
     };
 
     return match_set(servers_, tolower(server)) &&
            match_set(channels_, tolower(channel)) &&
            match_set(origins_, tolower(nick)) &&
            match_set(plugins_, tolower(plugin)) &&
-           match_set(events_, event);
+           match_set(events_, std::string(event));
 }
 
 const std::error_category& rule_category()
--- a/libirccd/irccd/daemon/rule.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/rule.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -94,11 +94,11 @@
      * \param event the event
      * \return true if match
      */
-    bool match(const std::string& server,
-               const std::string& channel,
-               const std::string& nick,
-               const std::string& plugin,
-               const std::string& event) const noexcept;
+    bool match(std::string_view server,
+               std::string_view channel,
+               std::string_view nick,
+               std::string_view plugin,
+               std::string_view event) const noexcept;
 
     /**
      * Get the action.
--- a/libirccd/irccd/daemon/server_util.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/server_util.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -238,9 +238,11 @@
     return sv;
 }
 
-message_pack parse_message(std::string message, const std::string& cc, const std::string& name)
+message_pack parse_message(std::string_view message, std::string_view cchar, std::string_view plugin)
 {
-    auto result = message;
+    auto result = std::string(message);
+    auto cc = std::string(cchar);
+    auto name = std::string(plugin);
     auto iscommand = false;
 
     // handle special commands "!<plugin> command"
--- a/libirccd/irccd/daemon/server_util.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/server_util.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -107,9 +107,7 @@
  * \param plugin the plugin name
  * \return the pair
  */
-message_pack parse_message(std::string message,
-                           const std::string& cchar,
-                           const std::string& plugin);
+message_pack parse_message(std::string_view message, std::string_view cchar, std::string_view plugin);
 
 } // !server_util
 
--- a/libirccd/irccd/daemon/service/plugin_service.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/service/plugin_service.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -29,10 +29,9 @@
 
 namespace {
 
-template <typename Map>
-Map to_map(const config& conf, const std::string& section)
+auto to_map(const config& conf, const std::string& section) -> plugin::map
 {
-    Map ret;
+    plugin::map ret;
 
     for (const auto& opt : conf.get(section))
         ret.emplace(opt.key(), opt.value());
@@ -49,7 +48,7 @@
 
 plugin_service::~plugin_service()
 {
-    for (const auto& plugin : plugins_) {
+    for (const auto& [_, plugin] : plugins_) {
         try {
             plugin->handle_unload(irccd_);
         } catch (const std::exception& ex) {
@@ -58,26 +57,25 @@
     }
 }
 
-bool plugin_service::has(const std::string& name) const noexcept
+auto plugin_service::all() const noexcept -> plugins
 {
-    return std::count_if(plugins_.cbegin(), plugins_.cend(), [&] (const auto& plugin) {
-        return plugin->get_name() == name;
-    }) > 0;
+    return plugins_;
+}
+
+auto plugin_service::has(const std::string& name) const noexcept -> bool
+{
+    return plugins_.find(name) != plugins_.end();
 }
 
-std::shared_ptr<plugin> plugin_service::get(const std::string& name) const noexcept
+auto plugin_service::get(const std::string& name) const noexcept -> std::shared_ptr<plugin>
 {
-    auto it = std::find_if(plugins_.begin(), plugins_.end(), [&] (const auto& plugin) {
-        return plugin->get_name() == name;
-    });
+    if (auto it = plugins_.find(name); it != plugins_.end())
+        return it->second;
 
-    if (it == plugins_.end())
-        return nullptr;
-
-    return *it;
+    return nullptr;
 }
 
-std::shared_ptr<plugin> plugin_service::require(const std::string& name) const
+auto plugin_service::require(const std::string& name) const -> std::shared_ptr<plugin>
 {
     auto plugin = get(name);
 
@@ -87,38 +85,40 @@
     return plugin;
 }
 
-void plugin_service::add(std::shared_ptr<plugin> plugin)
+void plugin_service::add(std::string id, std::shared_ptr<plugin> plugin)
 {
-    plugins_.push_back(std::move(plugin));
+    plugins_.emplace(std::move(id), std::move(plugin));
 }
 
 void plugin_service::add_loader(std::unique_ptr<plugin_loader> loader)
 {
+    assert(loader);
+
     loaders_.push_back(std::move(loader));
 }
 
-plugin_config plugin_service::config(const std::string& id)
+auto plugin_service::get_options(const std::string& id) -> plugin::map
 {
-    return to_map<plugin_config>(irccd_.get_config(), string_util::sprintf("plugin.%s", id));
+    return to_map(irccd_.get_config(), string_util::sprintf("plugin.%s", id));
 }
 
-plugin_formats plugin_service::formats(const std::string& id)
+auto plugin_service::get_formats(const std::string& id) -> plugin::map
 {
-    return to_map<plugin_formats>(irccd_.get_config(), string_util::sprintf("format.%s", id));
+    return to_map(irccd_.get_config(), string_util::sprintf("format.%s", id));
 }
 
-plugin_paths plugin_service::paths(const std::string& id)
+auto plugin_service::get_paths(const std::string& id) -> plugin::map
 {
-    auto defaults = to_map<plugin_paths>(irccd_.get_config(), "paths");
-    auto paths = to_map<plugin_paths>(irccd_.get_config(), string_util::sprintf("paths.%s", id));
+    auto defaults = to_map(irccd_.get_config(), "paths");
+    auto paths = to_map(irccd_.get_config(), string_util::sprintf("paths.%s", id));
 
     // Fill defaults paths.
     if (!defaults.count("cache"))
-        defaults.emplace("cache", (sys::cachedir() / "plugin" / id).string());
+        defaults.emplace("cache", (sys::cachedir() / "plugin" / std::string(id)).string());
     if (!defaults.count("data"))
-        paths.emplace("data", (sys::datadir() / "plugin" / id).string());
+        paths.emplace("data", (sys::datadir() / "plugin" / std::string(id)).string());
     if (!defaults.count("config"))
-        paths.emplace("config", (sys::sysconfdir() / "plugin" / id).string());
+        paths.emplace("config", (sys::sysconfdir() / "plugin" / std::string(id)).string());
 
     // Now fill missing fields.
     if (!paths.count("cache"))
@@ -131,8 +131,8 @@
     return paths;
 }
 
-std::shared_ptr<plugin> plugin_service::open(const std::string& id,
-                                             const std::string& path)
+auto plugin_service::open(const std::string& id,
+                          const std::string& path) -> std::shared_ptr<plugin>
 {
     for (const auto& loader : loaders_) {
         auto plugin = loader->open(id, path);
@@ -144,7 +144,7 @@
     return nullptr;
 }
 
-std::shared_ptr<plugin> plugin_service::find(const std::string& id)
+auto plugin_service::find(const std::string& id) -> std::shared_ptr<plugin>
 {
     for (const auto& loader : loaders_) {
         try {
@@ -160,27 +160,27 @@
     return nullptr;
 }
 
-void plugin_service::load(std::string name, std::string path)
+void plugin_service::load(const std::string& id, const std::string& path)
 {
-    if (has(name))
-        throw plugin_error(plugin_error::already_exists, name);
+    if (has(id))
+        throw plugin_error(plugin_error::already_exists, id);
 
     std::shared_ptr<plugin> plugin;
 
     if (path.empty())
-        plugin = find(name);
+        plugin = find(id);
     else
-        plugin = open(name, std::move(path));
+        plugin = open(id, std::move(path));
 
     if (!plugin)
-        throw plugin_error(plugin_error::not_found, name);
+        throw plugin_error(plugin_error::not_found, id);
 
-    plugin->set_config(config(name));
-    plugin->set_formats(formats(name));
-    plugin->set_paths(paths(name));
+    plugin->set_options(get_options(id));
+    plugin->set_formats(get_formats(id));
+    plugin->set_paths(get_paths(id));
 
     exec(plugin, &plugin::handle_load, irccd_);
-    add(std::move(plugin));
+    add(std::move(id), std::move(plugin));
 }
 
 void plugin_service::reload(const std::string& name)
@@ -193,23 +193,21 @@
     exec(plugin, &plugin::handle_reload, irccd_);
 }
 
-void plugin_service::unload(const std::string& name)
+void plugin_service::unload(const std::string& id)
 {
-    const auto it = std::find_if(plugins_.begin(), plugins_.end(), [&] (const auto& plugin) {
-        return plugin->get_name() == name;
-    });
+    const auto it = plugins_.find(id);
 
     if (it == plugins_.end())
-        throw plugin_error(plugin_error::not_found, name);
+        throw plugin_error(plugin_error::not_found, id);
 
     // Erase first, in case of throwing.
-    const auto save = *it;
+    const auto save = it->second;
 
     plugins_.erase(it);
-    exec(save, &plugin::handle_unload, irccd_);
+    exec(it->second, &plugin::handle_unload, irccd_);
 }
 
-void plugin_service::load(const class config& cfg) noexcept
+void plugin_service::load(const config& cfg) noexcept
 {
     for (const auto& option : cfg.get("plugins")) {
         if (!string_util::is_identifier(option.key()))
@@ -220,9 +218,9 @@
 
         // Reload the plugin if already loaded.
         if (p) {
-            p->set_config(config(name));
-            p->set_formats(formats(name));
-            p->set_paths(paths(name));
+            p->set_options(get_options(name));
+            p->set_formats(get_formats(name));
+            p->set_paths(get_paths(name));
         } else {
             try {
                 load(name, option.value());
--- a/libirccd/irccd/daemon/service/plugin_service.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/service/plugin_service.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -27,6 +27,7 @@
 #include <cassert>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include <irccd/daemon/plugin.hpp>
@@ -41,10 +42,21 @@
  * \ingroup services
  */
 class plugin_service {
+public:
+    /**
+     * \brief Map of plugins.
+     */
+    using plugins = std::unordered_map<std::string, std::shared_ptr<plugin>>;
+
+    /**
+     * \brief List of loaders.
+     */
+    using plugin_loaders = std::vector<std::unique_ptr<plugin_loader>>;
+
 private:
     irccd& irccd_;
-    std::vector<std::shared_ptr<plugin>> plugins_;
-    std::vector<std::unique_ptr<plugin_loader>> loaders_;
+    plugins plugins_;
+    plugin_loaders loaders_;
 
 public:
     /**
@@ -57,17 +69,14 @@
     /**
      * Destroy plugins.
      */
-    ~plugin_service();
+    virtual ~plugin_service();
 
     /**
      * Get the list of plugins.
      *
      * \return the list of plugins
      */
-    inline const std::vector<std::shared_ptr<plugin>>& list() const noexcept
-    {
-        return plugins_;
-    }
+    auto all() const noexcept -> plugins;
 
     /**
      * Check if a plugin is loaded.
@@ -75,7 +84,7 @@
      * \param name the plugin id
      * \return true if has plugin
      */
-    bool has(const std::string& name) const noexcept;
+    auto has(const std::string& name) const noexcept -> bool;
 
     /**
      * Get a loaded plugin or null if not found.
@@ -83,29 +92,32 @@
      * \param name the plugin id
      * \return the plugin or empty one if not found
      */
-    std::shared_ptr<plugin> get(const std::string& name) const noexcept;
+    auto get(const std::string& name) const noexcept -> std::shared_ptr<plugin>;
 
     /**
      * Find a loaded plugin.
      *
      * \param name the plugin id
      * \return the plugin
-     * \throws std::out_of_range if not found
+     * \throw plugin_error on errors
      */
-    std::shared_ptr<plugin> require(const std::string& name) const;
+    auto require(const std::string& name) const -> std::shared_ptr<plugin>;
 
     /**
      * Add the specified plugin to the registry.
      *
+     * \pre id is valid
      * \pre plugin != nullptr
+     * \param id the unique plugin identifier
      * \param plugin the plugin
      * \note the plugin is only added to the list, no action is performed on it
      */
-    void add(std::shared_ptr<plugin> plugin);
+    void add(std::string id, std::shared_ptr<plugin> plugin);
 
     /**
      * Add a loader.
      *
+     * \pre loader != nullptr
      * \param loader the loader
      */
     void add_loader(std::unique_ptr<plugin_loader> loader);
@@ -115,14 +127,14 @@
      *
      * \return the configuration
      */
-    plugin_config config(const std::string& id);
+    auto get_options(const std::string& id) -> plugin::map;
 
     /**
      * Get the formats for the specified plugin.
      *
      * \return the formats
      */
-    plugin_formats formats(const std::string& id);
+    auto get_formats(const std::string& id) -> plugin::map;
 
     /**
      * Get the paths for the specified plugin.
@@ -131,7 +143,7 @@
      *
      * \return the paths
      */
-    plugin_paths paths(const std::string& id);
+    auto get_paths(const std::string& id) -> plugin::map;
 
     /**
      * Generic function for opening the plugin at the given path.
@@ -143,8 +155,8 @@
      * \param path the path to the file
      * \return the plugin or nullptr on failures
      */
-    std::shared_ptr<plugin> open(const std::string& id,
-                                 const std::string& path);
+    auto open(const std::string& id,
+              const std::string& path) -> std::shared_ptr<plugin>;
 
     /**
      * Generic function for finding a plugin.
@@ -152,18 +164,18 @@
      * \param id the plugin id
      * \return the plugin or nullptr on failures
      */
-    std::shared_ptr<plugin> find(const std::string& id);
+    auto find(const std::string& id) -> std::shared_ptr<plugin>;
 
     /**
-     * Convenient wrapper that loads a plugin, call onLoad and add it to the
-     * registry.
+     * Convenient wrapper that loads a plugin, call handle_load and add it to
+     * the registry.
      *
      * Any errors are printed using logger.
      *
      * \param name the name
      * \param path the optional path (searched if empty)
      */
-    void load(std::string name, std::string path = "");
+    void load(const std::string& name, const std::string& path = "");
 
     /**
      * Unload a plugin and remove it.
@@ -229,7 +241,7 @@
      *
      * \param cfg the config
      */
-    void load(const class config& cfg) noexcept;
+    void load(const config& cfg) noexcept;
 };
 
 } // !irccd
--- a/libirccd/irccd/daemon/service/rule_service.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/service/rule_service.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -69,11 +69,11 @@
     return rules_[position];
 }
 
-bool rule_service::solve(const std::string& server,
-                         const std::string& channel,
-                         const std::string& origin,
-                         const std::string& plugin,
-                         const std::string& event) noexcept
+bool rule_service::solve(std::string_view server,
+                         std::string_view channel,
+                         std::string_view origin,
+                         std::string_view plugin,
+                         std::string_view event) noexcept
 {
     bool result = true;
 
--- a/libirccd/irccd/daemon/service/rule_service.hpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/service/rule_service.hpp	Mon Jul 16 21:19:47 2018 +0200
@@ -119,11 +119,11 @@
      * \param event the event name (e.g onKick)
      * \return true if the plugin must be called
      */
-    bool solve(const std::string& server,
-               const std::string& channel,
-               const std::string& origin,
-               const std::string& plugin,
-               const std::string& event) noexcept;
+    bool solve(std::string_view server,
+               std::string_view channel,
+               std::string_view origin,
+               std::string_view plugin,
+               std::string_view event) noexcept;
 
     /**
      * Load rules from the configuration.
--- a/libirccd/irccd/daemon/service/server_service.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/libirccd/irccd/daemon/service/server_service.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -34,13 +34,13 @@
 
 template <typename EventNameFunc, typename ExecFunc>
 void dispatch(irccd& daemon,
-              const std::string& server,
-              const std::string& origin,
-              const std::string& target,
+              std::string_view server,
+              std::string_view origin,
+              std::string_view target,
               EventNameFunc&& name_func,
               ExecFunc exec_func)
 {
-    for (auto& plugin : daemon.plugins().list()) {
+    for (auto& [_, plugin] : daemon.plugins().all()) {
         const auto eventname = name_func(*plugin);
         const auto allowed = daemon.rules().solve(server, target, origin, plugin->get_name(), eventname);
 
--- a/plugins/ask/ask.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/ask/ask.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "ask",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "Crazy module for asking a medium",
--- a/plugins/auth/auth.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/auth/auth.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    auth: "auth",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "Generic plugin to authenticate to services",
--- a/plugins/hangman/hangman.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/hangman/hangman.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "hangman",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "A hangman game for IRC",
--- a/plugins/history/history.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/history/history.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "history",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "track nickname's history",
--- a/plugins/joke/joke.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/joke/joke.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "joke",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "display some jokes",
--- a/plugins/links/links.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/links/links.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -392,11 +392,19 @@
 
 class links_plugin : public plugin {
 public:
-    using plugin::plugin;
+    auto get_name() const noexcept -> std::string_view override;
+
+    auto get_author() const noexcept -> std::string_view override;
+
+    auto get_license() const noexcept -> std::string_view override;
 
-    void set_config(plugin_config) override;
+    auto get_summary() const noexcept -> std::string_view override;
+
+    auto get_version() const noexcept -> std::string_view override;
 
-    void set_formats(plugin_formats) override;
+    void set_options(const map&) override;
+
+    void set_formats(const map&) override;
 
     void handle_message(irccd&, const message_event&) override;
 
@@ -405,13 +413,39 @@
     static auto init() -> std::unique_ptr<plugin>;
 };
 
-void links_plugin::set_config(plugin_config conf)
+auto links_plugin::get_name() const noexcept -> std::string_view
+{
+    return "links";
+}
+
+auto links_plugin::get_author() const noexcept -> std::string_view
 {
-    if (const auto v = string_util::to_uint(conf["timeout"]); v)
-        config::timeout = *v;
+    return "David Demelier <markand@malikania.fr>";
+}
+
+auto links_plugin::get_license() const noexcept -> std::string_view
+{
+    return "ISC";
 }
 
-void links_plugin::set_formats(plugin_formats formats)
+auto links_plugin::get_summary() const noexcept -> std::string_view
+{
+    return "show webpage title";
+}
+
+auto links_plugin::get_version() const noexcept -> std::string_view
+{
+    return IRCCD_VERSION;
+}
+
+void links_plugin::set_options(const map& conf)
+{
+    if (const auto it = conf.find("timeout"); it != conf.end())
+        if (const auto v = string_util::to_uint(it->second); v)
+            config::timeout = *v;
+}
+
+void links_plugin::set_formats(const map& formats)
 {
     if (const auto it = formats.find("info"); it != formats.end())
         formats::info = it->second;
@@ -429,7 +463,7 @@
 
 auto links_plugin::init() -> std::unique_ptr<plugin>
 {
-    return std::make_unique<links_plugin>("links", "");
+    return std::make_unique<links_plugin>();
 }
 
 BOOST_DLL_ALIAS(links_plugin::abi, irccd_abi_links)
--- a/plugins/logger/logger.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/logger/logger.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "logger",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "A plugin to log everything",
--- a/plugins/plugin/plugin.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/plugin/plugin.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "plugin",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "A plugin to inspect plugins",
--- a/plugins/roulette/roulette.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/roulette/roulette.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "roulette",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "A russian roulette for IRC",
--- a/plugins/tictactoe/tictactoe.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/plugins/tictactoe/tictactoe.js	Mon Jul 16 21:19:47 2018 +0200
@@ -18,6 +18,7 @@
 
 // Plugin information.
 info = {
+    name: "tictactoe",
     author: "David Demelier <markand@malikania.fr>",
     license: "ISC",
     summary: "A tictactoe game for IRC",
--- a/tests/src/irccdctl/cli-plugin-config/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/irccdctl/cli-plugin-config/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -25,44 +25,45 @@
 
 namespace {
 
-class custom_plugin : public plugin {
+class configurable_plugin : public plugin {
 private:
-    plugin_config config_;
+    map config_;
 
 public:
-    using plugin::plugin;
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "config";
+    }
 
-    plugin_config get_config() override
+    auto get_options() const -> map override
     {
         return config_;
     }
 
-    void set_config(plugin_config config) override
+    void set_options(const map& config) override
     {
         config_ = std::move(config);
     }
 };
 
-class custom_plugin_cli_test : public plugin_cli_test {
+class configurable_plugin_cli_test : public plugin_cli_test {
 public:
-    custom_plugin_cli_test()
+    configurable_plugin_cli_test()
     {
-        auto conf1 = std::make_unique<custom_plugin>("conf1", "local");
-        auto conf2 = std::make_unique<custom_plugin>("conf2", "local");
+        auto conf1 = std::make_unique<configurable_plugin>();
+        auto conf2 = std::make_unique<configurable_plugin>();
 
-        conf1->set_config({
+        conf1->set_options({
             { "v1", "123" },
             { "v2", "456" }
         });
 
-        irccd_.plugins().add(std::move(conf1));
-        irccd_.plugins().add(std::move(conf2));
+        irccd_.plugins().add("conf1", std::move(conf1));
+        irccd_.plugins().add("conf2", std::move(conf2));
     }
 };
 
-} // !namespace
-
-BOOST_FIXTURE_TEST_SUITE(plugin_config_suite, custom_plugin_cli_test)
+BOOST_FIXTURE_TEST_SUITE(plugin_config_suite, configurable_plugin_cli_test)
 
 BOOST_AUTO_TEST_CASE(set_and_get)
 {
@@ -101,4 +102,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/irccdctl/cli-plugin-info/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/irccdctl/cli-plugin-info/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -23,18 +23,43 @@
 
 namespace irccd {
 
+namespace {
+
+class sample : public plugin {
+public:
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "sample";
+    }
+
+    auto get_author() const noexcept -> std::string_view override
+    {
+        return "David Demelier <markand@malikania.fr>";
+    }
+
+    auto get_license() const noexcept -> std::string_view override
+    {
+        return "ISC";
+    }
+
+    auto get_summary() const noexcept -> std::string_view override
+    {
+        return "foo";
+    }
+
+    auto get_version() const noexcept -> std::string_view override
+    {
+        return "0.0";
+    }
+};
+
 BOOST_FIXTURE_TEST_SUITE(plugin_info_suite, plugin_cli_test)
 
 BOOST_AUTO_TEST_CASE(simple)
 {
-    auto p = std::make_unique<plugin>("p", "local");
+    auto p = std::make_unique<sample>();
 
-    p->set_author("David Demelier <markand@malikania.fr>");
-    p->set_license("ISC");
-    p->set_summary("foo");
-    p->set_version("0.0");
-
-    irccd_.plugins().add(std::move(p));
+    irccd_.plugins().add("p", std::move(p));
     start();
 
     const auto result = exec({ "plugin-info", "p" });
@@ -49,4 +74,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/irccdctl/cli-plugin-list/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/irccdctl/cli-plugin-list/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -23,12 +23,22 @@
 
 namespace irccd {
 
+namespace {
+
+class sample : public plugin {
+public:
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "sample";
+    }
+};
+
 BOOST_FIXTURE_TEST_SUITE(plugin_list_suite, plugin_cli_test)
 
 BOOST_AUTO_TEST_CASE(output)
 {
-    irccd_.plugins().add(std::make_unique<plugin>("p1", "local"));
-    irccd_.plugins().add(std::make_unique<plugin>("p2", "local"));
+    irccd_.plugins().add("p1", std::make_unique<sample>());
+    irccd_.plugins().add("p2", std::make_unique<sample>());
     start();
 
     const auto result = exec({ "plugin-list" });
@@ -41,4 +51,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/irccdctl/cli-plugin-load/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/irccdctl/cli-plugin-load/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -25,6 +25,14 @@
 
 namespace {
 
+class sample : public plugin {
+public:
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "sample";
+    }
+};
+
 class custom_plugin_loader : public plugin_loader {
 public:
     custom_plugin_loader()
@@ -32,14 +40,14 @@
     {
     }
 
-    std::shared_ptr<plugin> find(const std::string& id) override
+    auto find(std::string_view) -> std::shared_ptr<plugin> override
     {
-        return std::make_unique<plugin>(id, "local");
+        return std::make_unique<sample>();
     }
 
-    std::shared_ptr<plugin> open(const std::string& id, const std::string& path) override
+    auto open(std::string_view, std::string_view) -> std::shared_ptr<plugin> override
     {
-        return std::make_unique<plugin>(id, path);
+        return std::make_unique<sample>();
     }
 };
 
@@ -49,8 +57,8 @@
 
 BOOST_AUTO_TEST_CASE(simple)
 {
-    irccd_.plugins().add(std::make_unique<plugin>("p1", "local"));
-    irccd_.plugins().add(std::make_unique<plugin>("p2", "local"));
+    irccd_.plugins().add("p1", std::make_unique<sample>());
+    irccd_.plugins().add("p2", std::make_unique<sample>());
     irccd_.plugins().add_loader(std::make_unique<custom_plugin_loader>());
     start();
 
--- a/tests/src/irccdctl/cli-plugin-reload/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/irccdctl/cli-plugin-reload/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -25,39 +25,39 @@
 
 namespace {
 
-class custom_plugin : public plugin {
+class reloadable_plugin : public plugin {
 public:
-    bool reloaded_{false};
+    bool reloaded{false};
 
-    custom_plugin()
-        : plugin("p", "local")
+    auto get_name() const noexcept -> std::string_view override
     {
+        return "reload";
     }
 
     void handle_reload(irccd&) override
     {
-        reloaded_ = true;
+        reloaded = true;
     }
 };
 
-} // !namespace
-
 BOOST_FIXTURE_TEST_SUITE(plugin_reload_suite, plugin_cli_test)
 
 BOOST_AUTO_TEST_CASE(simple)
 {
-    const auto plugin = std::make_shared<custom_plugin>();
+    const auto plugin = std::make_shared<reloadable_plugin>();
 
-    irccd_.plugins().add(plugin);
+    irccd_.plugins().add("p", plugin);
     start();
 
     const auto result = exec({ "plugin-reload", "p" });
 
     BOOST_TEST(result.first.size() == 0U);
     BOOST_TEST(result.second.size() == 0U);
-    BOOST_TEST(plugin->reloaded_);
+    BOOST_TEST(plugin->reloaded);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/irccdctl/cli-plugin-unload/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/irccdctl/cli-plugin-unload/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -25,30 +25,28 @@
 
 namespace {
 
-class custom_plugin : public plugin {
+class unloadable_plugin : public plugin {
 public:
-    bool unloaded_{false};
+    bool unloaded{false};
 
-    custom_plugin()
-        : plugin("p", "local")
+    auto get_name() const noexcept -> std::string_view override
     {
+        return "unload";
     }
 
     void handle_unload(irccd&) override
     {
-        unloaded_ = true;
+        unloaded = true;
     }
 };
 
-} // !namespace
-
 BOOST_FIXTURE_TEST_SUITE(plugin_unload_suite, plugin_cli_test)
 
 BOOST_AUTO_TEST_CASE(simple)
 {
-    const auto plugin = std::make_shared<custom_plugin>();
+    const auto plugin = std::make_shared<unloadable_plugin>();
 
-    irccd_.plugins().add(plugin);
+    irccd_.plugins().add("p", plugin);
     start();
 
     const auto result = exec({ "plugin-unload", "p" });
@@ -60,4 +58,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/libirccd-js/js-plugin/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/js-plugin/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -35,9 +35,9 @@
     irccd irccd_{service_};
     std::shared_ptr<js_plugin> plugin_;
 
-    void load(std::string name, std::string path)
+    void load(std::string path)
     {
-        plugin_ = std::make_unique<js_plugin>(std::move(name), std::move(path));
+        plugin_ = std::make_unique<js_plugin>(std::move(path));
 
         irccd_jsapi().load(irccd_, plugin_);
         plugin_jsapi().load(irccd_, plugin_);
@@ -50,47 +50,47 @@
 
 BOOST_AUTO_TEST_CASE(assign)
 {
-    load("test", CMAKE_CURRENT_SOURCE_DIR "/config-assign.js");
+    load(CMAKE_CURRENT_SOURCE_DIR "/config-assign.js");
 
-    plugin_->set_config({
+    plugin_->set_options({
         { "path",       "none"  },
         { "verbose",    "false" }
     });
     plugin_->handle_load(irccd_);
 
-    BOOST_TEST(plugin_->get_config().at("path") == "none");
-    BOOST_TEST(plugin_->get_config().at("verbose") == "false");
-    BOOST_TEST(plugin_->get_config().at("hard") == "true");
+    BOOST_TEST(plugin_->get_options().at("path") == "none");
+    BOOST_TEST(plugin_->get_options().at("verbose") == "false");
+    BOOST_TEST(plugin_->get_options().at("hard") == "true");
 }
 
 BOOST_AUTO_TEST_CASE(fill)
 {
-    load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js");
+    load(CMAKE_CURRENT_SOURCE_DIR "/config-fill.js");
 
-    plugin_->set_config({
+    plugin_->set_options({
         { "path",       "none"  },
         { "verbose",    "false" }
     });
     plugin_->handle_load(irccd_);
 
-    BOOST_TEST(plugin_->get_config().at("path") == "none");
-    BOOST_TEST(plugin_->get_config().at("verbose") == "false");
-    BOOST_TEST(plugin_->get_config().at("hard") == "true");
+    BOOST_TEST(plugin_->get_options().at("path") == "none");
+    BOOST_TEST(plugin_->get_options().at("verbose") == "false");
+    BOOST_TEST(plugin_->get_options().at("hard") == "true");
 }
 
 BOOST_AUTO_TEST_CASE(merge_after)
 {
-    load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js");
+    load(CMAKE_CURRENT_SOURCE_DIR "/config-fill.js");
 
     plugin_->handle_load(irccd_);
-    plugin_->set_config({
+    plugin_->set_options({
         { "path",       "none"  },
         { "verbose",    "false" }
     });
 
-    BOOST_TEST(plugin_->get_config().at("path") == "none");
-    BOOST_TEST(plugin_->get_config().at("verbose") == "false");
-    BOOST_TEST(plugin_->get_config().at("hard") == "true");
+    BOOST_TEST(plugin_->get_options().at("path") == "none");
+    BOOST_TEST(plugin_->get_options().at("verbose") == "false");
+    BOOST_TEST(plugin_->get_options().at("hard") == "true");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
@@ -126,27 +126,27 @@
 {
     load("test", CMAKE_CURRENT_SOURCE_DIR "/config-assign.js");
 
-    BOOST_TEST(plugin_->get_config().at("path") == "none");
-    BOOST_TEST(plugin_->get_config().at("verbose") == "false");
-    BOOST_TEST(plugin_->get_config().at("hard") == "true");
+    BOOST_TEST(plugin_->get_options().at("path") == "none");
+    BOOST_TEST(plugin_->get_options().at("verbose") == "false");
+    BOOST_TEST(plugin_->get_options().at("hard") == "true");
 }
 
 BOOST_AUTO_TEST_CASE(fill)
 {
     load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js");
 
-    BOOST_TEST(plugin_->get_config().at("path") == "none");
-    BOOST_TEST(plugin_->get_config().at("verbose") == "false");
-    BOOST_TEST(plugin_->get_config().at("hard") == "true");
+    BOOST_TEST(plugin_->get_options().at("path") == "none");
+    BOOST_TEST(plugin_->get_options().at("verbose") == "false");
+    BOOST_TEST(plugin_->get_options().at("hard") == "true");
 }
 
 BOOST_AUTO_TEST_CASE(merge_after)
 {
     load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js");
 
-    BOOST_TEST(plugin_->get_config().at("path") == "none");
-    BOOST_TEST(plugin_->get_config().at("verbose") == "false");
-    BOOST_TEST(plugin_->get_config().at("hard") == "true");
+    BOOST_TEST(plugin_->get_options().at("path") == "none");
+    BOOST_TEST(plugin_->get_options().at("verbose") == "false");
+    BOOST_TEST(plugin_->get_options().at("hard") == "true");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd-js/jsapi-directory/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-directory/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -35,11 +35,11 @@
         "l = d.entries.length;"
     );
 
-    if (duk_peval_string(plugin_->context(), script.c_str()) != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), script.c_str()) != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    duk_get_global_string(plugin_->context(), "l");
-    BOOST_TEST(duk_get_int(plugin_->context(), -1) == 3);
+    duk_get_global_string(plugin_->get_context(), "l");
+    BOOST_TEST(duk_get_int(plugin_->get_context(), -1) == 3);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd-js/jsapi-elapsedtimer/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-elapsedtimer/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -33,17 +33,17 @@
 
 BOOST_AUTO_TEST_CASE(standard)
 {
-    if (duk_peval_string(plugin_->context(), "timer = new Irccd.ElapsedTimer();") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "timer = new Irccd.ElapsedTimer();") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
     std::this_thread::sleep_for(300ms);
 
-    if (duk_peval_string(plugin_->context(), "result = timer.elapsed();") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "result = timer.elapsed();") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_REQUIRE(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_REQUIRE_GE(duk_get_int(plugin_->context(), -1), 250);
-    BOOST_REQUIRE_LE(duk_get_int(plugin_->context(), -1), 350);
+    BOOST_REQUIRE(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_REQUIRE_GE(duk_get_int(plugin_->get_context(), -1), 250);
+    BOOST_REQUIRE_LE(duk_get_int(plugin_->get_context(), -1), 350);
 }
 
 } // !irccd
--- a/tests/src/libirccd-js/jsapi-file/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-file/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -32,38 +32,38 @@
 
 BOOST_AUTO_TEST_CASE(function_basename)
 {
-    if (duk_peval_string(plugin_->context(), "result = Irccd.File.basename('/usr/local/etc/irccd.conf');"))
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.basename('/usr/local/etc/irccd.conf');"))
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("irccd.conf" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST("irccd.conf" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(function_dirname)
 {
-    if (duk_peval_string(plugin_->context(), "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');"))
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');"))
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("/usr/local/etc" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST("/usr/local/etc" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(function_exists)
 {
-    if (duk_peval_string(plugin_->context(), "result = Irccd.File.exists(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt')"))
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.exists(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt')"))
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(function_exists2)
 {
-    if (duk_peval_string(plugin_->context(), "result = Irccd.File.exists('file_which_does_not_exist.txt')"))
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.exists('file_which_does_not_exist.txt')"))
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(!duk_get_boolean(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(function_remove)
@@ -71,8 +71,8 @@
     // First create a dummy file
     std::ofstream("test-js-fs.remove");
 
-    if (duk_peval_string(plugin_->context(), "Irccd.File.remove('test-js-fs.remove');") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "Irccd.File.remove('test-js-fs.remove');") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
     std::ifstream in("test-js-fs.remove");
 
@@ -81,95 +81,95 @@
 
 BOOST_AUTO_TEST_CASE(method_basename)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "result = f.basename();"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("file-1.txt" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST("file-1.txt" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_basename_closed)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.close();"
         "result = f.basename();"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("file-1.txt" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST("file-1.txt" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_dirname)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "result = f.dirname();"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_dirname_closed)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.close();"
         "result = f.dirname();"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_lines)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "result = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r').lines();"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
     std::vector<std::string> expected{"a", "b", "c"};
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek1)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.seek(Irccd.File.SeekSet, 6);"
         "result = f.read(1);"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(".", dukx_get<std::string>(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(".", dukx_get<std::string>(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek1_closed)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.close();"
         "f.seek(Irccd.File.SeekSet, 4);"
@@ -178,15 +178,15 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek2)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.seek(Irccd.File.SeekSet, 2);"
         "f.seek(Irccd.File.SeekCur, 4);"
@@ -194,15 +194,15 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("." == dukx_get<std::string>(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST("." == dukx_get<std::string>(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek2c_losed)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.close();"
         "f.seek(Irccd.File.SeekSet, 2);"
@@ -212,30 +212,30 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek3)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.seek(Irccd.File.SeekEnd, -2);"
         "result = f.read(1);"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("t" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST("t" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek3_closed)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "f.close();"
         "f.seek(Irccd.File.SeekEnd, -2);"
@@ -244,29 +244,29 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_read1)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');"
         "result = f.read();"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("file-1.txt\n" == duk_get_string(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST("file-1.txt\n" == duk_get_string(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_readline)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "result = [];"
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r');"
         "for (var s; s = f.readline(); ) {"
@@ -275,17 +275,17 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
     std::vector<std::string> expected{"a", "b", "c"};
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_readline_closed)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "result = [];"
         "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r');"
         "f.close();"
@@ -295,12 +295,12 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
     std::vector<std::string> expected;
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd-js/jsapi-irccd/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-irccd/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -27,26 +27,26 @@
 
 BOOST_AUTO_TEST_CASE(version)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "major = Irccd.version.major;"
         "minor = Irccd.version.minor;"
         "patch = Irccd.version.patch;"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "major"));
-    BOOST_TEST(IRCCD_VERSION_MAJOR == duk_get_int(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "minor"));
-    BOOST_TEST(IRCCD_VERSION_MINOR == duk_get_int(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "patch"));
-    BOOST_TEST(IRCCD_VERSION_PATCH == duk_get_int(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "major"));
+    BOOST_TEST(IRCCD_VERSION_MAJOR == duk_get_int(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "minor"));
+    BOOST_TEST(IRCCD_VERSION_MINOR == duk_get_int(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "patch"));
+    BOOST_TEST(IRCCD_VERSION_PATCH == duk_get_int(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(from_javascript)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "try {"
         "  throw new Irccd.SystemError(1, 'test');"
         "} catch (e) {"
@@ -59,31 +59,31 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "errno"));
-    BOOST_TEST(1 == duk_get_int(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
-    BOOST_TEST("SystemError" == duk_get_string(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "message"));
-    BOOST_TEST("test" == duk_get_string(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "v1"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "v2"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "errno"));
+    BOOST_TEST(1 == duk_get_int(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name"));
+    BOOST_TEST("SystemError" == duk_get_string(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message"));
+    BOOST_TEST("test" == duk_get_string(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v1"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v2"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(from_native)
 {
-    duk_push_c_function(plugin_->context(), [] (duk_context *ctx) -> duk_ret_t {
+    duk_push_c_function(plugin_->get_context(), [] (duk_context *ctx) -> duk_ret_t {
         dukx_throw(ctx, std::system_error(make_error_code(std::errc::invalid_argument)));
 
         return 0;
     }, 0);
 
-    duk_put_global_string(plugin_->context(), "f");
+    duk_put_global_string(plugin_->get_context(), "f");
 
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "try {"
         "  f();"
         "} catch (e) {"
@@ -95,16 +95,16 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "errno"));
-    BOOST_TEST(EINVAL == duk_get_int(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
-    BOOST_TEST("SystemError" == duk_get_string(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "v1"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "v2"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "errno"));
+    BOOST_TEST(EINVAL == duk_get_int(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name"));
+    BOOST_TEST("SystemError" == duk_get_string(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v1"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v2"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd-js/jsapi-logger/empty.js	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-logger/empty.js	Mon Jul 16 21:19:47 2018 +0200
@@ -0,0 +1,3 @@
+info = {
+    name: "test"
+};
--- a/tests/src/libirccd-js/jsapi-logger/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-logger/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -61,6 +61,7 @@
     };
 
     logger_test()
+        : js_test(CMAKE_CURRENT_SOURCE_DIR "/empty.js")
     {
         irccd_.set_log(std::make_unique<my_logger>(*this));
         irccd_.get_log().set_verbose(true);
@@ -71,16 +72,16 @@
 
 BOOST_AUTO_TEST_CASE(info)
 {
-    if (duk_peval_string(plugin_->context(), "Irccd.Logger.info(\"hello!\");") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.info(\"hello!\");") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
     BOOST_TEST("plugin test: hello!" == line_info);
 }
 
 BOOST_AUTO_TEST_CASE(warning)
 {
-    if (duk_peval_string(plugin_->context(), "Irccd.Logger.warning(\"FAIL!\");") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.warning(\"FAIL!\");") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
     BOOST_TEST("plugin test: FAIL!" == line_warning);
 }
@@ -89,8 +90,8 @@
 
 BOOST_AUTO_TEST_CASE(debug)
 {
-    if (duk_peval_string(plugin_->context(), "Irccd.Logger.debug(\"starting\");") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.debug(\"starting\");") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
     BOOST_TEST("plugin test: starting" == line_debug);
 }
--- a/tests/src/libirccd-js/jsapi-system/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-system/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -36,26 +36,26 @@
 
 BOOST_AUTO_TEST_CASE(home)
 {
-    duk_peval_string_noresult(plugin_->context(), "result = Irccd.System.home();");
+    duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.System.home();");
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(),"result"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == sys::home());
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(),"result"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == sys::home());
 }
 
 #if defined(HAVE_POPEN)
 
 BOOST_AUTO_TEST_CASE(popen)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "f = Irccd.System.popen(\"" IRCCD_EXECUTABLE " --version\", \"r\");"
         "r = f.readline();"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "r"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == IRCCD_VERSION);
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "r"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == IRCCD_VERSION);
 }
 
 #endif
--- a/tests/src/libirccd-js/jsapi-timer/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-timer/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -40,11 +40,11 @@
 
     void set_type(const std::string& name)
     {
-        duk_get_global_string(plugin_->context(), "Irccd");
-        duk_get_prop_string(plugin_->context(), -1, "Timer");
-        duk_get_prop_string(plugin_->context(), -1, name.c_str());
-        duk_put_global_string(plugin_->context(), "type");
-        duk_pop_n(plugin_->context(), 2);
+        duk_get_global_string(plugin_->get_context(), "Irccd");
+        duk_get_prop_string(plugin_->get_context(), -1, "Timer");
+        duk_get_prop_string(plugin_->get_context(), -1, name.c_str());
+        duk_put_global_string(plugin_->get_context(), "type");
+        duk_pop_n(plugin_->get_context(), 2);
 
         plugin_->open();
         plugin_->handle_load(irccd_);
@@ -66,8 +66,8 @@
         service_.poll();
     }
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "count"));
-    BOOST_TEST(duk_get_int(plugin_->context(), -1) == 1);
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "count"));
+    BOOST_TEST(duk_get_int(plugin_->get_context(), -1) == 1);
 }
 
 BOOST_AUTO_TEST_CASE(repeat)
@@ -81,8 +81,8 @@
         service_.poll();
     }
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "count"));
-    BOOST_TEST(duk_get_int(plugin_->context(), -1) >= 5);
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "count"));
+    BOOST_TEST(duk_get_int(plugin_->get_context(), -1) >= 5);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd-js/jsapi-unicode/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-unicode/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -33,35 +33,35 @@
 
 BOOST_AUTO_TEST_CASE(is_letter)
 {
-    duk_peval_string_noresult(plugin_->context(), "result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 
-    duk_peval_string_noresult(plugin_->context(), "result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(!duk_get_boolean(plugin_->context(), -1));
+    duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(is_lower)
 {
-    duk_peval_string_noresult(plugin_->context(), "result = Irccd.Unicode.isLower(String('é').charCodeAt(0));");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLower(String('é').charCodeAt(0));");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 
-    duk_peval_string_noresult(plugin_->context(), "result = Irccd.Unicode.isLower(String('É').charCodeAt(0));");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(!duk_get_boolean(plugin_->context(), -1));
+    duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLower(String('É').charCodeAt(0));");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(is_upper)
 {
-    duk_peval_string_noresult(plugin_->context(), "result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
+    duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1));
 
-    duk_peval_string_noresult(plugin_->context(), "result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(!duk_get_boolean(plugin_->context(), -1));
+    duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd-js/jsapi-util/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd-js/jsapi-util/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -34,33 +34,33 @@
 
 BOOST_AUTO_TEST_CASE(format_simple)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "result = Irccd.Util.format(\"#{target}\", { target: \"markand\" })"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "markand");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "markand");
 }
 
 BOOST_AUTO_TEST_CASE(splituser)
 {
-    if (duk_peval_string(plugin_->context(), "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "user");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "user");
 }
 
 BOOST_AUTO_TEST_CASE(splithost)
 {
-    if (duk_peval_string(plugin_->context(), "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0)
-        throw dukx_stack(plugin_->context(), -1);
+    if (duk_peval_string(plugin_->get_context(), "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0)
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "~user@hyper/super/host");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "~user@hyper/super/host");
 }
 
 /*
@@ -70,68 +70,68 @@
 
 BOOST_AUTO_TEST_CASE(cut_string_simple)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "lines = Irccd.Util.cut('hello world');\n"
         "line0 = lines[0];\n"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello world");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello world");
 }
 
 BOOST_AUTO_TEST_CASE(cut_string_double)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "lines = Irccd.Util.cut('hello world', 5);\n"
         "line0 = lines[0];\n"
         "line1 = lines[1];\n"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line1"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "world");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world");
 }
 
 BOOST_AUTO_TEST_CASE(cut_string_dirty)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "lines = Irccd.Util.cut('     hello    world     ', 5);\n"
         "line0 = lines[0];\n"
         "line1 = lines[1];\n"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line1"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "world");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world");
 }
 
 BOOST_AUTO_TEST_CASE(cut_string_too_much_lines)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "lines = Irccd.Util.cut('abc def ghi jkl', 3, 3);\n"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "lines"));
-    BOOST_TEST(duk_is_undefined(plugin_->context(), -1));
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "lines"));
+    BOOST_TEST(duk_is_undefined(plugin_->get_context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(cut_string_token_too_big)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "try {\n"
         "  lines = Irccd.Util.cut('hello world', 3);\n"
         "} catch (e) {\n"
@@ -141,17 +141,17 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "RangeError");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "message"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "word 'hello' could not fit in maxc limit (3)");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "word 'hello' could not fit in maxc limit (3)");
 }
 
 BOOST_AUTO_TEST_CASE(cut_string_negative_maxc)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "try {\n"
         "  lines = Irccd.Util.cut('hello world', -3);\n"
         "} catch (e) {\n"
@@ -161,17 +161,17 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "RangeError");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "message"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "argument 1 (maxc) must be positive");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "argument 1 (maxc) must be positive");
 }
 
 BOOST_AUTO_TEST_CASE(cut_string_negative_maxl)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "try {\n"
         "  lines = Irccd.Util.cut('hello world', undefined, -1);\n"
         "} catch (e) {\n"
@@ -181,65 +181,65 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "RangeError");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "message"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "argument 2 (maxl) must be positive");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "argument 2 (maxl) must be positive");
 }
 
 BOOST_AUTO_TEST_CASE(cut_array_simple)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "lines = Irccd.Util.cut([ 'hello', 'world' ]);\n"
         "line0 = lines[0];\n"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello world");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello world");
 }
 
 BOOST_AUTO_TEST_CASE(cut_array_double)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "lines = Irccd.Util.cut([ 'hello', 'world' ], 5);\n"
         "line0 = lines[0];\n"
         "line1 = lines[1];\n"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line1"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "world");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world");
 }
 
 BOOST_AUTO_TEST_CASE(cut_array_dirty)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "lines = Irccd.Util.cut([ '   ', ' hello  ', '  world ', '    '], 5);\n"
         "line0 = lines[0];\n"
         "line1 = lines[1];\n"
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "line1"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "world");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world");
 }
 
 BOOST_AUTO_TEST_CASE(cut_invalid_data)
 {
-    auto ret = duk_peval_string(plugin_->context(),
+    auto ret = duk_peval_string(plugin_->get_context(),
         "try {\n"
         "  lines = Irccd.Util.cut(123);\n"
         "} catch (e) {\n"
@@ -249,10 +249,10 @@
     );
 
     if (ret != 0)
-        throw dukx_stack(plugin_->context(), -1);
+        throw dukx_stack(plugin_->get_context(), -1);
 
-    BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
-    BOOST_TEST(duk_get_string(plugin_->context(), -1) == "TypeError");
+    BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name"));
+    BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "TypeError");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-plugin-config/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/command-plugin-config/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -30,21 +30,21 @@
 
 class custom_plugin : public plugin {
 public:
-    plugin_config config_;
+    map config_;
 
-    custom_plugin(std::string name = "test")
-        : plugin(std::move(name), "")
+    auto get_name() const noexcept -> std::string_view override
     {
+        return "test";
     }
 
-    plugin_config get_config() override
+    auto get_options() const -> map override
     {
         return config_;
     }
 
-    void set_config(plugin_config config) override
+    void set_options(const map& options) override
     {
-        config_ = std::move(config);
+        config_ = std::move(options);
     }
 };
 
@@ -54,7 +54,7 @@
 
 BOOST_AUTO_TEST_CASE(set)
 {
-    daemon_->plugins().add(std::make_unique<custom_plugin>("test"));
+    daemon_->plugins().add("test", std::make_unique<custom_plugin>());
     ctl_->write({
         { "command",    "plugin-config" },
         { "plugin",     "test"          },
@@ -63,10 +63,10 @@
     });
 
     wait_for([&] {
-        return !daemon_->plugins().require("test")->get_config().empty();
+        return !daemon_->plugins().require("test")->get_options().empty();
     });
 
-    auto config = daemon_->plugins().require("test")->get_config();
+    auto config = daemon_->plugins().require("test")->get_options();
 
     BOOST_TEST(!config.empty());
     BOOST_TEST(config["verbosy"] == "falsy");
@@ -74,14 +74,14 @@
 
 BOOST_AUTO_TEST_CASE(get)
 {
-    auto plugin = std::make_unique<custom_plugin>("test");
+    auto plugin = std::make_unique<custom_plugin>();
     auto json = nlohmann::json();
 
-    plugin->set_config({
+    plugin->set_options({
         { "x1", "10" },
         { "x2", "20" }
     });
-    daemon_->plugins().add(std::move(plugin));
+    daemon_->plugins().add("test", std::move(plugin));
 
     auto result = request({
         { "command",    "plugin-config" },
@@ -95,14 +95,14 @@
 
 BOOST_AUTO_TEST_CASE(getall)
 {
-    auto plugin = std::make_unique<custom_plugin>("test");
+    auto plugin = std::make_unique<custom_plugin>();
     auto json = nlohmann::json();
 
-    plugin->set_config({
+    plugin->set_options({
         { "x1", "10" },
         { "x2", "20" }
     });
-    daemon_->plugins().add(std::move(plugin));
+    daemon_->plugins().add("test", std::move(plugin));
 
     auto result = request({
         { "command", "plugin-config" },
--- a/tests/src/libirccd/command-plugin-info/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/command-plugin-info/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -26,18 +26,44 @@
 
 namespace irccd {
 
+namespace {
+
+class sample_plugin : public plugin {
+public:
+    auto get_name() const noexcept -> std::string_view
+    {
+        return "test";
+    }
+
+    auto get_author() const noexcept -> std::string_view override
+    {
+        return "Francis Beaugrand";
+    }
+
+    auto get_license() const noexcept -> std::string_view override
+    {
+        return "GPL";
+    }
+
+    auto get_summary() const noexcept -> std::string_view override
+    {
+        return "Completely useless plugin";
+    }
+
+    auto get_version() const noexcept -> std::string_view override
+    {
+        return "0.0.0.0.0.0.0.0.1-beta5";
+    }
+};
+
 BOOST_FIXTURE_TEST_SUITE(plugin_info_test_suite, command_test<plugin_info_command>)
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    auto plg = std::make_unique<plugin>("test", "");
+    auto plg = std::make_unique<sample_plugin>();
     auto response = nlohmann::json();
 
-    plg->set_author("Francis Beaugrand");
-    plg->set_license("GPL");
-    plg->set_summary("Completely useless plugin");
-    plg->set_version("0.0.0.0.0.0.0.0.1-beta5");
-    daemon_->plugins().add(std::move(plg));
+    daemon_->plugins().add("test", std::move(plg));
 
     const auto result = request({
         { "command",    "plugin-info"       },
@@ -79,4 +105,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/libirccd/command-plugin-list/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/command-plugin-list/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -26,12 +26,22 @@
 
 namespace irccd {
 
+namespace {
+
+class sample_plugin : public plugin {
+public:
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "sample";
+    }
+};
+
 class plugin_list_test : public command_test<plugin_list_command> {
 public:
     plugin_list_test()
     {
-        daemon_->plugins().add(std::make_unique<plugin>("t1", ""));
-        daemon_->plugins().add(std::make_unique<plugin>("t2", ""));
+        daemon_->plugins().add("t1", std::make_unique<sample_plugin>());
+        daemon_->plugins().add("t2", std::make_unique<sample_plugin>());
     }
 };
 
@@ -39,15 +49,18 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [ result, code ] = request({
         { "command", "plugin-list" }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(result.first["list"][0].template get<std::string>() == "t1");
-    BOOST_TEST(result.first["list"][1].template get<std::string>() == "t2");
+    BOOST_TEST(!code);
+    BOOST_TEST(result.is_object());
+    BOOST_TEST(result["list"][0].template get<std::string>() == "t1");
+    BOOST_TEST(result["list"][1].template get<std::string>() == "t2");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/libirccd/command-plugin-load/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/command-plugin-load/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -28,39 +28,64 @@
 
 namespace {
 
-class custom_loader : public plugin_loader {
+class broken : public plugin {
 public:
-    custom_loader()
-        : plugin_loader({}, {".none"})
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "broken";
+    }
+
+    void handle_load(irccd&) override
+    {
+        throw std::runtime_error("broken");
+    }
+};
+
+class broken_loader : public plugin_loader {
+public:
+    broken_loader()
+        : plugin_loader({}, { ".none" })
     {
     }
 
-    std::shared_ptr<plugin> open(const std::string&,
-                                 const std::string&) noexcept override
+    auto open(std::string_view, std::string_view) -> std::shared_ptr<plugin> override
     {
         return nullptr;
     }
 
-    std::shared_ptr<plugin> find(const std::string& id) noexcept override
+    auto find(std::string_view id) noexcept -> std::shared_ptr<plugin> override
     {
-        class broken : public plugin {
-        public:
-            using plugin::plugin;
+        if (id == "broken")
+            return std::make_unique<broken>();
+
+        return nullptr;
+    }
+};
+
+class sample : public plugin {
+public:
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "test";
+    }
+};
 
-            void handle_load(irccd&) override
-            {
-                throw std::runtime_error("broken");
-            }
-        };
+class sample_loader : public plugin_loader {
+public:
+    sample_loader()
+        : plugin_loader({}, {".none"})
+    {
+    }
 
-        /*
-         * The 'magic' plugin will be created for the unit tests, all other
-         * plugins will return null.
-         */
-        if (id == "magic")
-            return std::make_unique<plugin>(id, "");
-        if (id == "broken")
-            return std::make_unique<broken>(id, "");
+    auto open(std::string_view, std::string_view) -> std::shared_ptr<plugin> override
+    {
+        return nullptr;
+    }
+
+    auto find(std::string_view id) noexcept -> std::shared_ptr<plugin> override
+    {
+        if (id == "test")
+            return std::make_unique<sample>();
 
         return nullptr;
     }
@@ -70,8 +95,9 @@
 public:
     plugin_load_test()
     {
-        daemon_->plugins().add_loader(std::make_unique<custom_loader>());
-        daemon_->plugins().add(std::make_unique<plugin>("already", ""));
+        daemon_->plugins().add_loader(std::make_unique<sample_loader>());
+        daemon_->plugins().add_loader(std::make_unique<broken_loader>());
+        daemon_->plugins().add("already", std::make_unique<sample>());
     }
 };
 
@@ -83,15 +109,15 @@
 {
     ctl_->write({
         { "command",    "plugin-load"   },
-        { "plugin",     "magic"         }
+        { "plugin",     "test"          }
     });
 
     wait_for([&] () {
-        return daemon_->plugins().has("magic");
+        return daemon_->plugins().has("test");
     });
 
-    BOOST_TEST(!daemon_->plugins().list().empty());
-    BOOST_TEST(daemon_->plugins().has("magic"));
+    BOOST_TEST(!daemon_->plugins().all().empty());
+    BOOST_TEST(daemon_->plugins().has("test"));
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-plugin-reload/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/command-plugin-reload/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -28,13 +28,13 @@
 
 namespace {
 
-class custom_plugin : public plugin {
+class reloadable_plugin : public plugin {
 public:
     bool reloaded{false};
 
-    custom_plugin()
-        : plugin("test", "")
+    auto get_name() const noexcept -> std::string_view override
     {
+        return "reload";
     }
 
     void handle_reload(irccd&) override
@@ -45,7 +45,10 @@
 
 class broken_plugin : public plugin {
 public:
-    using plugin::plugin;
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "broken";
+    }
 
     void handle_reload(irccd&) override
     {
@@ -55,13 +58,13 @@
 
 class plugin_reload_test : public command_test<plugin_reload_command> {
 protected:
-    std::shared_ptr<custom_plugin> plugin_;
+    std::shared_ptr<reloadable_plugin> plugin_;
 
     plugin_reload_test()
-        : plugin_(std::make_shared<custom_plugin>())
+        : plugin_(std::make_shared<reloadable_plugin>())
     {
-        daemon_->plugins().add(plugin_);
-        daemon_->plugins().add(std::make_unique<broken_plugin>("broken", ""));
+        daemon_->plugins().add("test", plugin_);
+        daemon_->plugins().add("broken", std::make_unique<broken_plugin>());
     }
 };
 
--- a/tests/src/libirccd/command-plugin-unload/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/command-plugin-unload/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -28,13 +28,13 @@
 
 namespace {
 
-class custom_plugin : public plugin {
+class unloadable_plugin : public plugin {
 public:
     bool unloaded{false};
 
-    custom_plugin()
-        : plugin("test", "")
+    auto get_name() const noexcept -> std::string_view override
     {
+        return "unload";
     }
 
     void handle_unload(irccd &) override
@@ -45,7 +45,10 @@
 
 class broken_plugin : public plugin {
 public:
-    using plugin::plugin;
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "broken";
+    }
 
     void handle_unload(irccd&) override
     {
@@ -55,13 +58,13 @@
 
 class plugin_unload_test : public command_test<plugin_unload_command> {
 protected:
-    std::shared_ptr<custom_plugin> plugin_;
+    std::shared_ptr<unloadable_plugin> plugin_;
 
     plugin_unload_test()
-        : plugin_(std::make_shared<custom_plugin>())
+        : plugin_(std::make_shared<unloadable_plugin>())
     {
-        daemon_->plugins().add(plugin_);
-        daemon_->plugins().add(std::make_unique<broken_plugin>("broken", ""));
+        daemon_->plugins().add("test", plugin_);
+        daemon_->plugins().add("broken", std::make_unique<broken_plugin>());
     }
 };
 
--- a/tests/src/libirccd/dynlib-plugin/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/dynlib-plugin/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -21,7 +21,7 @@
 
 /*
  * For this test, we update internal plugin configuration each time a function
- * is called and check if it has been called correctly using get_config.
+ * is called and check if it has been called correctly using get_options.
  */
 
 #include <irccd/daemon/dynlib_plugin.hpp>
@@ -51,136 +51,136 @@
 {
     plugin_->handle_command(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["command"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["command"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_connect)
 {
     plugin_->handle_connect(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["connect"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["connect"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_invite)
 {
     plugin_->handle_invite(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["invite"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["invite"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_join)
 {
     plugin_->handle_join(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["join"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["join"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_kick)
 {
     plugin_->handle_kick(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["kick"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["kick"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_load)
 {
     plugin_->handle_load(irccd_);
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["load"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["load"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_message)
 {
     plugin_->handle_message(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["message"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["message"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_me)
 {
     plugin_->handle_me(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["me"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["me"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_mode)
 {
     plugin_->handle_mode(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["mode"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["mode"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_names)
 {
     plugin_->handle_names(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["names"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["names"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_nick)
 {
     plugin_->handle_nick(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["nick"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["nick"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_notice)
 {
     plugin_->handle_notice(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["notice"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["notice"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_part)
 {
     plugin_->handle_part(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["part"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["part"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_reload)
 {
     plugin_->handle_reload(irccd_);
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["reload"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["reload"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_topic)
 {
     plugin_->handle_topic(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["topic"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["topic"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_unload)
 {
     plugin_->handle_unload(irccd_);
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["unload"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["unload"] == "true");
 }
 
 BOOST_AUTO_TEST_CASE(handle_whois)
 {
     plugin_->handle_whois(irccd_, {});
 
-    BOOST_TEST(plugin_->get_config().size() == 1U);
-    BOOST_TEST(plugin_->get_config()["whois"] == "true");
+    BOOST_TEST(plugin_->get_options().size() == 1U);
+    BOOST_TEST(plugin_->get_options()["whois"] == "true");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/dynlib-plugin/test_plugin.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/libirccd/dynlib-plugin/test_plugin.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -24,16 +24,19 @@
 
 class test_plugin : public plugin {
 private:
-    plugin_config config_;
+    map config_;
 
 public:
-    using plugin::plugin;
-
-    plugin_config get_config() override
+    auto get_options() const -> map override
     {
         return config_;
     }
 
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "test";
+    }
+
     void handle_command(irccd&, const message_event&) override
     {
         config_["command"] = "true";
@@ -126,7 +129,7 @@
 
     static auto init() -> std::unique_ptr<plugin>
     {
-        return std::make_unique<test_plugin>("testplugin", "");
+        return std::make_unique<test_plugin>();
     }
 };
 
--- a/tests/src/plugins/ask/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/ask/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -22,7 +22,5 @@
         ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/answers.conf
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="ask"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/ask/ask.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/ask/ask.js"
 )
--- a/tests/src/plugins/ask/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/ask/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -26,12 +26,14 @@
 
 namespace irccd {
 
+namespace {
+
 class ask_test : public plugin_test {
 public:
-    inline ask_test()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+    ask_test()
+        : plugin_test(PLUGIN_PATH)
     {
-        plugin_->set_config({
+        plugin_->set_options({
             { "file", CMAKE_CURRENT_SOURCE_DIR "/answers.conf" }
         });
         plugin_->handle_load(irccd_);
@@ -73,4 +75,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/plugins/auth/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/auth/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -20,7 +20,5 @@
     NAME plugin-auth
     SOURCES main.cpp
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="auth"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/auth/auth.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/auth/auth.js"
 )
--- a/tests/src/plugins/auth/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/auth/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -34,12 +34,12 @@
 
 public:
     auth_test()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+        : plugin_test(PLUGIN_PATH)
         , nickserv1_(std::make_shared<journal_server>(service_, "nickserv1"))
         , nickserv2_(std::make_shared<journal_server>(service_, "nickserv2"))
         , quakenet_(std::make_shared<journal_server>(service_, "quakenet"))
     {
-        plugin_->set_config({
+        plugin_->set_options({
             { "nickserv1.type", "nickserv" },
             { "nickserv1.password", "plopation" },
             { "nickserv2.type", "nickserv" },
--- a/tests/src/plugins/hangman/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/hangman/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -23,7 +23,5 @@
         words.conf
         wordlist_fix_644.conf
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="hangman"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/hangman/hangman.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/hangman/hangman.js"
 )
--- a/tests/src/plugins/hangman/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/hangman/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -32,7 +32,7 @@
 class hangman_test : public plugin_test {
 public:
     hangman_test()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+        : plugin_test(PLUGIN_PATH)
     {
         plugin_->set_formats({
             { "asked", "asked=#{plugin}:#{command}:#{server}:#{channel}:#{origin}:#{nickname}:#{letter}" },
@@ -47,13 +47,13 @@
         });
     }
 
-    void load(plugin_config config = {})
+    void load(plugin::map config = {})
     {
         // Add file if not there.
         if (config.count("file") == 0)
             config.emplace("file", CMAKE_CURRENT_SOURCE_DIR "/words.conf");
 
-        plugin_->set_config(config);
+        plugin_->set_options(config);
         plugin_->handle_load(irccd_);
     }
 };
--- a/tests/src/plugins/history/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/history/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -20,7 +20,5 @@
     NAME plugin-history
     SOURCES main.cpp
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="history"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/history/history.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/history/history.js"
 )
--- a/tests/src/plugins/history/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/history/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -31,7 +31,7 @@
 class history_test : public plugin_test {
 public:
     history_test()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+        : plugin_test(PLUGIN_PATH)
     {
         plugin_->set_formats({
             { "error", "error=#{plugin}:#{command}:#{server}:#{channel}:#{origin}:#{nickname}" },
@@ -41,13 +41,13 @@
         });
     }
 
-    void load(plugin_config config = {})
+    void load(plugin::map config = {})
     {
         // Add file if not there.
         if (config.count("file") == 0)
             config.emplace("file", CMAKE_CURRENT_SOURCE_DIR "/words.conf");
 
-        plugin_->set_config(config);
+        plugin_->set_options(config);
         plugin_->handle_load(irccd_);
     }
 };
--- a/tests/src/plugins/joke/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/joke/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -26,7 +26,5 @@
         jokes-not-array.json
         jokes-toobig.json
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="joke"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/joke/joke.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/joke/joke.js"
 )
--- a/tests/src/plugins/joke/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/joke/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -26,20 +26,20 @@
 class joke_test : public plugin_test {
 public:
     joke_test()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+        : plugin_test(PLUGIN_PATH)
     {
         plugin_->set_formats({
             { "error", "error=#{server}:#{channel}:#{origin}:#{nickname}" }
         });
     }
 
-    void load(plugin_config config = {})
+    void load(plugin::map config = {})
     {
         // Add file if not there.
         if (config.count("file") == 0)
             config.emplace("file", CMAKE_CURRENT_SOURCE_DIR "/jokes.json");
 
-        plugin_->set_config(config);
+        plugin_->set_options(config);
         plugin_->handle_load(irccd_);
     }
 };
--- a/tests/src/plugins/logger/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/logger/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -20,7 +20,5 @@
     NAME plugin-logger
     SOURCES main.cpp
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="logger"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/logger/logger.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/logger/logger.js"
 )
--- a/tests/src/plugins/logger/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/logger/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -40,7 +40,7 @@
 
 public:
     logger_test()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+        : plugin_test(PLUGIN_PATH)
     {
         remove(CMAKE_CURRENT_BINARY_DIR "/log.txt");
 
@@ -57,12 +57,12 @@
         });
     }
 
-    void load(plugin_config config = plugin_config())
+    void load(plugin::map config = {})
     {
         if (config.count("path") == 0)
             config.emplace("path", CMAKE_CURRENT_BINARY_DIR "/log.txt");
 
-        plugin_->set_config(config);
+        plugin_->set_options(config);
         plugin_->handle_load(irccd_);
     }
 };
--- a/tests/src/plugins/plugin/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/plugin/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -20,7 +20,5 @@
     NAME plugin-plugin
     SOURCES main.cpp
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="plugin"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/plugin/plugin.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/plugin/plugin.js"
 )
--- a/tests/src/plugins/plugin/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/plugin/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -29,24 +29,42 @@
 
 namespace irccd {
 
+namespace {
+
 class fake_plugin : public plugin {
 public:
-    fake_plugin()
-        : plugin("fake", "")
+    auto get_name() const noexcept -> std::string_view override
+    {
+        return "fake";
+    }
+
+    auto get_author() const noexcept -> std::string_view override
+    {
+        return "jean";
+    }
+
+    auto get_version() const noexcept -> std::string_view override
     {
-        set_author("jean");
-        set_version("0.0.0.0.0.1");
-        set_license("BEER");
-        set_summary("Fake White Beer 2000");
+        return "0.0.0.0.0.1";
+    }
+
+    auto get_license() const noexcept -> std::string_view override
+    {
+        return "BEER";
+    }
+
+    auto get_summary() const noexcept -> std::string_view override
+    {
+        return "Fake White Beer 2000";
     }
 };
 
 class test_fixture : public plugin_test {
 public:
     test_fixture()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+        : plugin_test(PLUGIN_PATH)
     {
-        irccd_.plugins().add(std::make_shared<fake_plugin>());
+        irccd_.plugins().add("fake", std::make_shared<fake_plugin>());
 
         plugin_->set_formats({
             { "usage", "usage=#{plugin}:#{command}:#{server}:#{channel}:#{origin}:#{nickname}" },
@@ -111,7 +129,7 @@
 BOOST_AUTO_TEST_CASE(format_too_long)
 {
     for (int i = 0; i < 100; ++i)
-        irccd_.plugins().add(std::make_shared<plugin>(string_util::sprintf("plugin-n-%d", i), ""));
+        irccd_.plugins().add(string_util::sprintf("plugin-n-%d", i), std::make_shared<fake_plugin>());
 
     plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#staff", "list"});
 
@@ -124,4 +142,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // !namespace
+
 } // !irccd
--- a/tests/src/plugins/tictactoe/CMakeLists.txt	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/tictactoe/CMakeLists.txt	Mon Jul 16 21:19:47 2018 +0200
@@ -20,8 +20,6 @@
     NAME plugin-tictactoe
     SOURCES main.cpp
     LIBRARIES libirccd
-    FLAGS
-        PLUGIN_NAME="tictactoe"
-        PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/tictactoe/tictactoe.js"
+    FLAGS PLUGIN_PATH="${CMAKE_SOURCE_DIR}/plugins/tictactoe/tictactoe.js"
 )
 
--- a/tests/src/plugins/tictactoe/main.cpp	Mon Jul 16 13:29:48 2018 +0200
+++ b/tests/src/plugins/tictactoe/main.cpp	Mon Jul 16 21:19:47 2018 +0200
@@ -32,7 +32,7 @@
 class test_fixture : public plugin_test {
 public:
     test_fixture()
-        : plugin_test(PLUGIN_NAME, PLUGIN_PATH)
+        : plugin_test(PLUGIN_PATH)
     {
         plugin_->set_formats({
             { "draw",       "draw=#{channel}:#{command}:#{nickname}:#{plugin}:#{server}"                },