我即将在Rust中重写一个高度模块化的CMS,所以我的问题是,如果甚至可以让"核心"应用程序设置扩展点(动作/钩子),其他插件/板条箱能够"标签"到.
这样的东西就足够了,但你怎么能在Rust做到这一点?上面的体系结构使用插件注册表,并通过遍历每个插件的主要方法来迭代每个插件的主要方法.但是在Rust中,因为你不能在例如plugin_registry lib crate中拥有全局"模块"变量,我想这不是Rust中的正确思路.
是否有更好,更灵活的方式使"插件"与核心应用程序无缝集成?例如,像WordPress这样的事件调度程序会使用什么?
正如Shepmaster所说,这是一个非常普遍的问题; 因此,有很多方法可以做你想要的.如前所述,也是iron模块化框架的一个很好的例子.
不过,我会尽量给一个怎样一个有用的例子可以实现这样的插件系统.对于我将假设的示例,有某种主板可以加载插件并"配置"CMS.这意味着插件不会动态加载!
首先,假设我们有四个板条箱:
rustpress:具有所有WordPress功能的大型主箱rustpress-plugin:需要由插件作者使用(是一个自己的箱子,以避免像rustpress每个插件一样使用巨大的箱子)rustpress-signature:在这里我们创建我们的插件,它将为每个帖子添加一个签名my-blog:这将是配置我们博客的主要可执行文件,稍后将作为Web服务器运行在Rust的方式是traits.您可以将它们与其他语言的接口进行比较.我们现在将设计适用于trait以下插件的插件rustpress-plugin:
pub trait Plugin {
/// Returns the name of the plugin
fn name(&self) -> &str;
/// Hook to change the title of a post
fn filter_title(&self, title: &mut String) {}
/// Hook to change the body of a post
fn filter_body(&self, body: &mut String) {}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这些filter_*方法已经有一个不执行任何操作的默认实现({}).这意味着如果插件只想使用一个挂钩,则不必覆盖所有方法.
正如我所说,我们想要编写一个插件,将我们的签名添加到每个帖子正文中.要做到这一点,我们将为impl我们自己的类型(in rustpress-signature)的特征:
extern crate rustpress_plugin;
use rustpress_plugin::Plugin;
pub struct Signature {
pub text: String,
}
impl Plugin for Signature {
fn name(&self) -> &str {
"Signature Plugin v0.1 by ferris"
}
fn filter_body(&self, body: &mut String) {
body.push_str("\n-------\n"); // add visual seperator
body.push_str(&self.text);
}
}
Run Code Online (Sandbox Code Playgroud)
我们创建了一个Signature我们实现特征的简单类型Plugin.我们必须实现该name()方法,我们也覆盖该filter_body()方法.在我们的实现中,我们只是向帖子正文添加文本.我们没有覆盖,filter_title()因为我们不需要.
CMS必须管理所有插件.我假设CMS有一个主要类型RustPress,可以处理所有事情.它看起来像这样(in rustpress):
extern crate rustpress_plugin;
use rustpress_plugin::Plugin;
pub struct RustPress {
// ...
plugins: Vec<Box<Plugin>>,
}
impl RustPress {
pub fn new() -> RustPress {
RustPress {
// ...
plugins: Vec::new(),
}
}
/// Adds a plugin to the stack
pub fn add_plugin<P: Plugin + 'static>(&mut self, plugin: P) {
self.plugins.push(Box::new(plugin));
}
/// Internal function that prepares a post
fn serve_post(&self) {
let mut title = "dummy".to_string();
let mut body = "dummy body".to_string();
for p in &self.plugins {
p.filter_title(&mut title);
p.filter_body(&mut body);
}
// use the finalized title and body now ...
}
/// Starts the CMS ...
pub fn start(&self) {}
}
Run Code Online (Sandbox Code Playgroud)
我们在这里做的是存储一个Vec完整的盒装插件(我们需要打包它们,因为我们想要所有权,但是特征是未经确定的).当CMS然后准备博客帖子时,它会遍历所有插件并调用所有挂钩.
最后一步是添加插件并启动CMS(将它们放在一起).我们将在my-blog箱子里这样做:
extern crate rustpress;
extern crate rustpress_plugin;
extern crate rustpress_signature;
use rustpress::RustPress;
use rustpress_plugin::Plugin;
use rustpress_signature::Signature;
fn main() {
let mut rustpress = RustPress::new();
// add plugin
let sig = Signature { text: "Ferris loves you <3".into() };
rustpress.add_plugin(sig);
rustpress.start();
}
Run Code Online (Sandbox Code Playgroud)
您还需要将依赖项添加到Cargo.toml文件中.我省略了,因为它应该相当容易.
再次注意,这是创建这样一个系统的众多可能性之一.我希望这个例子很有帮助.你也可以在游乐场试试.
| 归档时间: |
|
| 查看次数: |
289 次 |
| 最近记录: |