feat(@embark/plugins): introduce API to register a contract factory

This commit introduces two new plugin APIs `registerTestContractFactory()` and
`registerCustomContractGenerator()`, which can be used to register a factory function
for the creation of web3 contract instances within tests, and custom code generation
  for `embark console` respectively.

Example:

```
// some.plugin.js

module.exports = function (embark) {
  embark.registerTestContractFactory(function (contractRecipe, web3) {
    // do something with web3 and contractRecipe and return contract instance here
  });
};
```

**Notice that**:

- This factory function is used for contract instance creation within tests.
  A `contractRecipe` and a `web3` instance is accessible within the factory.

Example:

```
// some.plugin.js

module.exports = function (embark) {
  embark.registerCustomContractGenerator(function (contractRecipe) {
    // returns code string that will be eval'ed
  });
};
```

**Notice that**:

- Once registered, this generator will be used for **all** contract instances
  that will be created for `embark console`, including built-in once like
  ENSRegistry.

- While this does affect contract creation in client-side code, it doesn't
  actually affect the instances created for deployment hooks **if** deployment
  hooks are written as functions.

Closes #1066

Always use custom generator and fallback to vanilla
This commit is contained in:
Pascal Precht 2018-11-26 12:13:01 +01:00 committed by Pascal Precht
parent 639b8d8ebd
commit 90aac8343e
4 changed files with 39 additions and 7 deletions

View File

@ -13,6 +13,8 @@ var Plugin = function(options) {
this.clientWeb3Providers = []; this.clientWeb3Providers = [];
this.beforeDeploy = []; this.beforeDeploy = [];
this.contractsGenerators = []; this.contractsGenerators = [];
this.generateCustomContractCode = null;
this.testContractFactory = null;
this.pipeline = []; this.pipeline = [];
this.pipelineFiles = []; this.pipelineFiles = [];
this.console = []; this.console = [];
@ -120,6 +122,16 @@ Plugin.prototype.registerContractsGeneration = function(cb) {
this.addPluginType('contractGeneration'); this.addPluginType('contractGeneration');
}; };
Plugin.prototype.registerCustomContractGenerator = function (cb) {
this.generateCustomContractCode = cb;
this.addPluginType('customContractGeneration');
};
Plugin.prototype.registerTestContractFactory = function(cb) {
this.testContractFactory = cb;
this.addPluginType('testContractFactory');
};
Plugin.prototype.registerPipeline = function(matcthingFiles, cb) { Plugin.prototype.registerPipeline = function(matcthingFiles, cb) {
// TODO: generate error for more than one pipeline per plugin // TODO: generate error for more than one pipeline per plugin
this.pipeline.push({matcthingFiles: matcthingFiles, cb: cb}); this.pipeline.push({matcthingFiles: matcthingFiles, cb: cb});

View File

@ -69,6 +69,18 @@ class CodeGenerator {
cb(self.generateContractCode(contract, gasLimit)); cb(self.generateContractCode(contract, gasLimit));
}); });
self.events.setCommandHandler('code-generator:contract:custom', (contract, cb) => {
const customCode = self.generateCustomContractCode(contract);
if (!customCode) {
// Fallback to generate code from vanilla contract generator.
//
// TODO: can be moved into a afterDeploy event
// just need to figure out the gasLimit coupling issue
return cb(self.generateContractCode(contract, contract._gasLimit || false));
}
cb(customCode);
});
self.events.setCommandHandler('code-generator:embarkjs:provider-code', (cb) => { self.events.setCommandHandler('code-generator:embarkjs:provider-code', (cb) => {
cb(self.getEmbarkJsProviderCode()); cb(self.getEmbarkJsProviderCode());
}); });
@ -171,6 +183,14 @@ class CodeGenerator {
return block; return block;
} }
generateCustomContractCode(contract) {
const customContractGeneratorPlugin = this.plugins.getPluginsFor('customContractGeneration').splice(-1)[0];
if (!customContractGeneratorPlugin) {
return null;
}
return customContractGeneratorPlugin.generateCustomContractCode(contract);
}
generateNamesInitialization(useEmbarkJS) { generateNamesInitialization(useEmbarkJS) {
if (!useEmbarkJS || this.namesystemConfig === {}) return ""; if (!useEmbarkJS || this.namesystemConfig === {}) return "";

View File

@ -193,9 +193,7 @@ class ContractDeployer {
contract.deployedAddress = trackedContract.address; contract.deployedAddress = trackedContract.address;
self.events.emit("deploy:contract:deployed", contract); self.events.emit("deploy:contract:deployed", contract);
// TODO: can be moved into a afterDeploy event self.events.request('code-generator:contract:custom', contract, (contractCode) => {
// just need to figure out the gasLimit coupling issue
self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit || false, (contractCode) => {
self.events.request('runcode:eval', contractCode, () => {}, true); self.events.request('runcode:eval', contractCode, () => {}, true);
return callback(); return callback();
}); });
@ -306,9 +304,8 @@ class ContractDeployer {
self.events.emit("deploy:contract:receipt", receipt); self.events.emit("deploy:contract:receipt", receipt);
self.events.emit("deploy:contract:deployed", contract); self.events.emit("deploy:contract:deployed", contract);
// TODO: can be moved into a afterDeploy event
// just need to figure out the gasLimit coupling issue self.events.request('code-generator:contract:custom', contract, (contractCode) => {
self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit || false, (contractCode) => {
self.events.request('runcode:eval', contractCode, () => {}, true); self.events.request('runcode:eval', contractCode, () => {}, true);
self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, () => { self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, () => {
return next(null, receipt); return next(null, receipt);

View File

@ -11,6 +11,7 @@ class Test {
this.options = options || {}; this.options = options || {};
this.simOptions = {}; this.simOptions = {};
this.events = options.events; this.events = options.events;
this.plugins = options.config.plugins;
this.logger = options.logger; this.logger = options.logger;
this.ipc = options.ipc; this.ipc = options.ipc;
this.configObj = options.config; this.configObj = options.config;
@ -268,7 +269,9 @@ class Test {
self.contracts[contract.className] = {}; self.contracts[contract.className] = {};
} }
const newContract = Test.getWeb3Contract(contract, web3); const testContractFactoryPlugin = self.plugins.getPluginsFor('testContractFactory').slice(-1)[0];
const newContract = testContractFactoryPlugin ? testContractFactoryPlugin.testContractFactory(contract, web3) : Test.getWeb3Contract(contract, web3);
Object.setPrototypeOf(self.contracts[contract.className], newContract); Object.setPrototypeOf(self.contracts[contract.className], newContract);
eachCb(); eachCb();