Skip to content

Commit

Permalink
docs(plugin): add singleton support async create function (#2392)
Browse files Browse the repository at this point in the history
  • Loading branch information
dead-horse authored and popomore committed Apr 15, 2018
1 parent 05d925f commit 3d499a9
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 16 deletions.
41 changes: 29 additions & 12 deletions docs/source/en/advanced/plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Plugin is actually a 'mini application', directory of plugin is as below
└── mw.test.js
```

It is almost the same as the application directory, what's the difference?
It is almost the same as the application directory, what's the difference?

1. Plugin have no independant router or controller. This is because:

Expand Down Expand Up @@ -135,7 +135,7 @@ We've discussed what plugin is. Now what can it do?

### Built-in Objects API Extension

Extend the built-in objects of the framework, just like the application
Extend the built-in objects of the framework, just like the application

- `app/extend/request.js` - extends Koa#Request object
- `app/extend/response.js` - extends Koa#Response object
Expand Down Expand Up @@ -173,7 +173,7 @@ Extend the built-in objects of the framework, just like the application
const assert = require('assert');
module.exports = app => {
// insert static middleware before bodyParser
// insert static middleware before bodyParser
const index = app.config.coreMiddleware.indexOf('bodyParser');
assert(index >= 0, 'bodyParser highly needed');
Expand Down Expand Up @@ -273,7 +273,7 @@ If each plugin makes their own implementation, all sorts of configs and initia

#### Writing Plugin

We simplify the [egg-mysql] plugin and to see how to write such a plugin:
We simplify the [egg-mysql] plugin and to see how to write such a plugin:

```js
// egg-mysql/app.js
Expand All @@ -284,7 +284,7 @@ module.exports = app => {
}

/**
* @param {Object} config The config which already processed by the framework. If the application configured multiple MySQL instances, each config would be passed in and call multiple createMysql
* @param {Object} config The config which already processed by the framework. If the application configured multiple MySQL instances, each config would be passed in and call multiple createMysql
* @param {Application} app the current application
* @return {Object} return the created MySQL instance
*/
Expand All @@ -294,16 +294,33 @@ function createMysql(config, app) {
const client = new Mysql(config);

// check before start the application
app.beforeStart(async function startMysql() {
app.beforeStart(async () => {
const rows = await client.query('select now() as currentTime;');
const index = count++;
app.coreLogger.info(`[egg-mysql] instance[${index}] status OK, rds currentTime: ${rows[0].currentTime}`);
app.coreLogger.info(`[egg-mysql] init instance success, rds currentTime: ${rows[0].currentTime}`);
});

return client;
}
```

The initialization function also support `Async function`, convenient for some special plugins that need to be asynchronous to get some configuration files.

```js
async function createMysql(config, app) {
// get mysql configurations asynchronous
const mysqlConfig = await app.configManager.getMysqlConfig(config.mysql);
assert(mysqlConfig.host && mysqlConfig.port && mysqlConfig.user && mysqlConfig.database);
// create instance
const client = new Mysql(mysqlConfig);

// check before start the application
const rows = await client.query('select now() as currentTime;');
app.coreLogger.info(`[egg-mysql] init instance success, rds currentTime: ${rows[0].currentTime}`);

return client;
}
```

As you can see, all we need to do for this plugin is passing in the field that need to be mounted and the corresponding initialization function. Framework will be in charge of managing all the configs and the ways to access the instances.

#### Application Layer Usage Case
Expand Down Expand Up @@ -366,7 +383,7 @@ exports.mysql = {
};
```

2. Access the corresponding instance by `app.mysql.get('db1')`
2. Access the corresponding instance by `app.mysql.get('db1')`

```js
// app/controller/post.js
Expand All @@ -388,12 +405,12 @@ module.exports = app => {
// get MySQL config from configuration center { host, post, password, ... }
const mysqlConfig = await app.configCenter.fetch('mysql');
// create MySQL instance dynamically
app.database = app.mysql.createInstance(mysqlConfig);
app.database = app.mysql.createInstanceAsync(mysqlConfig);
});
};
```

Access the instance through `app.database`
Access the instance through `app.database`

```js
// app/controller/post.js
Expand Down Expand Up @@ -455,7 +472,7 @@ We are very welcome your contribution to the new plugins, but also hope you foll

Egg defines the plugin name through the `eggPlugin.name`, it is only unique in application or framework, that means **many npm packages might get the same plugin name**, why design in this way?

First, Egg plugin do not only support npm package, it also supports search plugins in local directory. In Chapter [progressive](../tutorials/progressive.md) we mentioned how to make progress by using these two configurations. Directory is more friendly to unit test. So, Egg can not ensure uniqueness through npm package name.
First, Egg plugin do not only support npm package, it also supports search plugins in local directory. In Chapter [progressive](../tutorials/progressive.md) we mentioned how to make progress by using these two configurations. Directory is more friendly to unit test. So, Egg can not ensure uniqueness through npm package name.

What's more, Egg can use this feature to make Adapter. For example, the plugin defined in[Template Develop Spec](./view-plugin.md#PluginNameSpecification) was named as view, but there are plugins named `egg-view-nunjucks` and `egg-view-react`, the users only need to change the plugin and modify the templates, no need to modify the Controller, because all these plugins have implemented the same API.

Expand Down
25 changes: 21 additions & 4 deletions docs/source/zh-cn/advanced/plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,16 +294,33 @@ function createMysql(config, app) {
const client = new Mysql(config);
// 做启动应用前的检查
app.beforeStart(async function startMysql() {
app.beforeStart(async () => {
const rows = await client.query('select now() as currentTime;');
const index = count++;
app.coreLogger.info(`[egg-mysql] instance[${index}] status OK, rds currentTime: ${rows[0].currentTime}`);
app.coreLogger.info(`[egg-mysql] init instance success, rds currentTime: ${rows[0].currentTime}`);
});
return client;
}
```

初始化方法也支持 `Async function`,便于有些特殊的插件需要异步化获取一些配置文件:

```js
async function createMysql(config, app) {
// 异步获取 mysql 配置
const mysqlConfig = await app.configManager.getMysqlConfig(config.mysql);
assert(mysqlConfig.host && mysqlConfig.port && mysqlConfig.user && mysqlConfig.database);
// 创建实例
const client = new Mysql(mysqlConfig);
// 做启动应用前的检查
const rows = await client.query('select now() as currentTime;');
app.coreLogger.info(`[egg-mysql] init instance success, rds currentTime: ${rows[0].currentTime}`);
return client;
}
```

可以看到,插件中我们只需要提供要挂载的字段以及对应服务的初始化方法,所有的配置管理、实例获取方式都由框架封装并统一提供了。

#### 应用层使用方案
Expand Down Expand Up @@ -388,7 +405,7 @@ module.exports = app => {
// 从配置中心获取 MySQL 的配置 { host, post, password, ... }
const mysqlConfig = await app.configCenter.fetch('mysql');
// 动态创建 MySQL 实例
app.database = app.mysql.createInstance(mysqlConfig);
app.database = await app.mysql.createInstanceAsync(mysqlConfig);
});
};
```
Expand Down

0 comments on commit 3d499a9

Please sign in to comment.