Mercurial > irccd
changeset 524:ba81aeb514b8
Irccd: change loading order in js_plugin, #678
To open a Javascript plugin, several steps are required in this order:
1. create Duktape heap,
2. load Javascript modules,
3. open the file
4. apply user configuration
This order is important as it let the plugin setting defaults values
that can be overriden by the user.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 14 Nov 2017 19:54:32 +0100 |
parents | 542c54f76488 |
children | d070d33bf4a0 |
files | libirccd-js/irccd/js_plugin.cpp libirccd-js/irccd/js_plugin.hpp tests/js-plugin/config-assign.js tests/js-plugin/config-fill.js tests/js-plugin/irccd.conf tests/js-plugin/main.cpp |
diffstat | 6 files changed, 142 insertions(+), 87 deletions(-) [+] |
line wrap: on
line diff
--- a/libirccd-js/irccd/js_plugin.cpp Mon Nov 13 15:49:47 2017 +0100 +++ b/libirccd-js/irccd/js_plugin.cpp Tue Nov 14 19:54:32 2017 +0100 @@ -16,15 +16,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <boost/filesystem.hpp> - -#include "sysconfig.hpp" - -#if defined(HAVE_STAT) -# include <sys/stat.h> -# include <cerrno> -# include <cstring> -#endif +#include <cstring> +#include <cerrno> +#include <fstream> +#include <iterator> +#include <stdexcept> #include "irccd.hpp" #include "logger.hpp" @@ -86,21 +82,11 @@ } } -void js_plugin::put_vars() +js_plugin::js_plugin(std::string name, std::string path) + : plugin(name, path) { StackAssert sa(context_); - duk_push_pointer(context_, this); - duk_put_global_string(context_, "\xff""\xff""plugin"); - dukx_push_std_string(context_, name()); - duk_put_global_string(context_, "\xff""\xff""name"); - dukx_push_std_string(context_, path()); - duk_put_global_string(context_, "\xff""\xff""path"); -} - -js_plugin::js_plugin(std::string name, std::string path) - : plugin(name, path) -{ /* * Create two special tables for configuration and formats, they are * referenced later as @@ -117,6 +103,46 @@ duk_put_global_string(context_, format_property.c_str()); duk_push_object(context_); duk_put_global_string(context_, paths_property.c_str()); + + duk_push_pointer(context_, this); + duk_put_global_string(context_, "\xff""\xff""plugin"); + dukx_push_std_string(context_, name); + duk_put_global_string(context_, "\xff""\xff""name"); + dukx_push_std_string(context_, path); + duk_put_global_string(context_, "\xff""\xff""path"); +} + +void js_plugin::open() +{ + std::ifstream input(path()); + + if (!input) + throw std::runtime_error(std::strerror(errno)); + + std::string data( + std::istreambuf_iterator<char>(input.rdbuf()), + std::istreambuf_iterator<char>() + ); + + if (duk_peval_string(context_, data.c_str())) + throw dukx_exception(context_, -1); + + // 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) : author()); + duk_get_prop_string(context_, -2, "license"); + set_license(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : license()); + duk_get_prop_string(context_, -3, "summary"); + set_summary(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : summary()); + duk_get_prop_string(context_, -4, "version"); + set_version(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : version()); + duk_pop_n(context_, 4); + } + + duk_pop(context_); } void js_plugin::on_channel_mode(irccd& , const channel_mode_event &event) @@ -197,55 +223,6 @@ { StackAssert sa(context_); - /* - * Duktape currently emit useless warnings when a file do - * not exists so we do a homemade access. - */ -#if defined(HAVE_STAT) - struct stat st; - - if (::stat(path().c_str(), &st) < 0) - throw std::runtime_error(std::strerror(errno)); -#endif - - // Try to load the file (does not call onLoad yet). - dukx_peval_file(context_, path()); - duk_pop(context_); - - - /* - * We put configuration and formats after loading the file and before - * calling onLoad to allow the plugin adding configuration to - * irccd.Plugin.(config|format) before the user. - */ - put_vars(); - - // Read metadata . - duk_get_global_string(context_, "info"); - - if (duk_get_type(context_, -1) == DUK_TYPE_OBJECT) { - // 'author' - duk_get_prop_string(context_, -1, "author"); - set_author(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : author()); - duk_pop(context_); - - // 'license' - duk_get_prop_string(context_, -1, "license"); - set_license(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : license()); - duk_pop(context_); - - // 'summary' - duk_get_prop_string(context_, -1, "summary"); - set_summary(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : summary()); - duk_pop(context_); - - // 'version' - duk_get_prop_string(context_, -1, "version"); - set_version(duk_is_string(context_, -1) ? duk_get_string(context_, -1) : version()); - duk_pop(context_); - } - - duk_pop(context_); call("onLoad", 0); } @@ -413,6 +390,8 @@ for (const auto& mod : modules_) mod->load(irccd_, plugin); + plugin->open(); + return plugin; } catch (const std::exception &ex) { log::warning() << "plugin " << id << ": " << ex.what() << std::endl;
--- a/libirccd-js/irccd/js_plugin.hpp Mon Nov 13 15:49:47 2017 +0100 +++ b/libirccd-js/irccd/js_plugin.hpp Tue Nov 14 19:54:32 2017 +0100 @@ -40,6 +40,11 @@ class js_plugin : public plugin { public: /** + * List of modules to enable. + */ + using modules_t = std::vector<std::unique_ptr<module>>; + + /** * Global property where to read/write plugin configuration (object). */ static const std::string config_property; @@ -62,7 +67,6 @@ 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>&); void call(const std::string&, unsigned = 0); - void put_vars(); public: /** @@ -84,6 +88,11 @@ } /** + * Open the script file associated. + */ + void open(); + + /** * \copydoc Plugin::config */ plugin_config config() override
--- a/tests/js-plugin/config-assign.js Mon Nov 13 15:49:47 2017 +0100 +++ b/tests/js-plugin/config-assign.js Tue Nov 14 19:54:32 2017 +0100 @@ -1,3 +1,3 @@ Irccd.Plugin.config = { - "key1": "value1" + "hard": "true" };
--- a/tests/js-plugin/config-fill.js Mon Nov 13 15:49:47 2017 +0100 +++ b/tests/js-plugin/config-fill.js Tue Nov 14 19:54:32 2017 +0100 @@ -1,1 +1,1 @@ -Irccd.Plugin.config["key1"] = "value1"; +Irccd.Plugin.config["hard"] = "true";
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/js-plugin/irccd.conf Tue Nov 14 19:54:32 2017 +0100 @@ -0,0 +1,3 @@ +[plugin.test] +path = "none" +verbose = "false"
--- a/tests/js-plugin/main.cpp Mon Nov 13 15:49:47 2017 +0100 +++ b/tests/js-plugin/main.cpp Tue Nov 14 19:54:32 2017 +0100 @@ -20,13 +20,14 @@ #include <boost/test/unit_test.hpp> #include <irccd/irccd.hpp> +#include <irccd/service.hpp> #include <irccd/js_plugin.hpp> #include <irccd/js_irccd_module.hpp> #include <irccd/js_plugin_module.hpp> namespace irccd { -class test { +class js_plugin_test { protected: irccd irccd_; std::shared_ptr<js_plugin> plugin_; @@ -37,48 +38,111 @@ js_irccd_module().load(irccd_, plugin_); js_plugin_module().load(irccd_, plugin_); + + plugin_->open(); } }; -BOOST_FIXTURE_TEST_SUITE(test_suite, test) +BOOST_FIXTURE_TEST_SUITE(js_plugin_test_suite, js_plugin_test) BOOST_AUTO_TEST_CASE(assign) { - load("assign", CMAKE_CURRENT_SOURCE_DIR "/config-assign.js"); + load("test", CMAKE_CURRENT_SOURCE_DIR "/config-assign.js"); plugin_->set_config({ - { "key2", "value2" } + { "path", "none" }, + { "verbose", "false" } }); plugin_->on_load(irccd_); - BOOST_TEST(plugin_->config().at("key1") == "value1"); - BOOST_TEST(plugin_->config().at("key2") == "value2"); + BOOST_TEST(plugin_->config().at("path") == "none"); + BOOST_TEST(plugin_->config().at("verbose") == "false"); + BOOST_TEST(plugin_->config().at("hard") == "true"); } BOOST_AUTO_TEST_CASE(fill) { - load("assign", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js"); + load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js"); plugin_->set_config({ - { "key2", "value2" } + { "path", "none" }, + { "verbose", "false" } }); plugin_->on_load(irccd_); - BOOST_TEST(plugin_->config().at("key1") == "value1"); - BOOST_TEST(plugin_->config().at("key2") == "value2"); + BOOST_TEST(plugin_->config().at("path") == "none"); + BOOST_TEST(plugin_->config().at("verbose") == "false"); + BOOST_TEST(plugin_->config().at("hard") == "true"); } BOOST_AUTO_TEST_CASE(merge_after) { - load("assign", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js"); + load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js"); plugin_->on_load(irccd_); plugin_->set_config({ - { "key2", "value2" } + { "path", "none" }, + { "verbose", "false" } }); - BOOST_TEST(plugin_->config().at("key1") == "value1"); - BOOST_TEST(plugin_->config().at("key2") == "value2"); + BOOST_TEST(plugin_->config().at("path") == "none"); + BOOST_TEST(plugin_->config().at("verbose") == "false"); + BOOST_TEST(plugin_->config().at("hard") == "true"); +} + +BOOST_AUTO_TEST_SUITE_END() + +class js_plugin_loader_test { +protected: + irccd irccd_; + std::shared_ptr<plugin> plugin_; + + js_plugin_loader_test() + { + irccd_.set_config(CMAKE_CURRENT_SOURCE_DIR "/irccd.conf"); + + auto loader = std::make_unique<js_plugin_loader>(irccd_); + + loader->add_module(std::make_unique<js_irccd_module>()); + loader->add_module(std::make_unique<js_plugin_module>()); + + irccd_.plugins().add_loader(std::move(loader)); + } + + void load(std::string name, std::string path) + { + irccd_.plugins().load(name, path); + plugin_ = irccd_.plugins().require(name); + } +}; + +BOOST_FIXTURE_TEST_SUITE(js_plugin_loader_test_suite, js_plugin_loader_test) + +BOOST_AUTO_TEST_CASE(assign) +{ + load("test", CMAKE_CURRENT_SOURCE_DIR "/config-assign.js"); + + BOOST_TEST(plugin_->config().at("path") == "none"); + BOOST_TEST(plugin_->config().at("verbose") == "false"); + BOOST_TEST(plugin_->config().at("hard") == "true"); +} + +BOOST_AUTO_TEST_CASE(fill) +{ + load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js"); + + BOOST_TEST(plugin_->config().at("path") == "none"); + BOOST_TEST(plugin_->config().at("verbose") == "false"); + BOOST_TEST(plugin_->config().at("hard") == "true"); +} + +BOOST_AUTO_TEST_CASE(merge_after) +{ + load("test", CMAKE_CURRENT_SOURCE_DIR "/config-fill.js"); + + BOOST_TEST(plugin_->config().at("path") == "none"); + BOOST_TEST(plugin_->config().at("verbose") == "false"); + BOOST_TEST(plugin_->config().at("hard") == "true"); } BOOST_AUTO_TEST_SUITE_END()