From ccdfaf61f2bf03ac3618098b684b73bde895376a Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Tue, 6 Dec 2016 21:33:31 -0500 Subject: [PATCH] implement plugin support: implement support for client-side web3 providers and contracts plugins --- lib/abi.js | 62 +++++++++++++++++++++++++++++++++----------------- lib/deploy.js | 2 +- lib/index.js | 17 +++++++++++--- lib/plugin.js | 42 ++++++++++++++++++++++++++++++++++ lib/plugins.js | 31 +++++++++++++++++++++++++ lib/test.js | 2 +- test/abi.js | 6 ++--- 7 files changed, 133 insertions(+), 29 deletions(-) create mode 100644 lib/plugin.js create mode 100644 lib/plugins.js diff --git a/lib/abi.js b/lib/abi.js index 5abc6c57..344f866b 100644 --- a/lib/abi.js +++ b/lib/abi.js @@ -1,39 +1,59 @@ +var Plugins = require('./plugins.js'); -var ABIGenerator = function(blockchainConfig, contractsManager) { - this.blockchainConfig = blockchainConfig; - this.contractsManager = contractsManager; - this.rpcHost = blockchainConfig.rpcHost; - this.rpcPort = blockchainConfig.rpcPort; +var ABIGenerator = function(options) { + this.blockchainConfig = options.blockchainConfig || {}; + this.contractsManager = options.contractsManager; + this.rpcHost = options.blockchainConfig.rpcHost; + this.rpcPort = options.blockchainConfig.rpcPort; + this.plugins = options.plugins || new Plugins({}); }; ABIGenerator.prototype.generateProvider = function() { + var self = this; var result = ""; - result += "\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {"; - result += '\n\tweb3 = new Web3(web3.currentProvider);'; - result += "\n} else if (typeof Web3 !== 'undefined') {"; - result += '\n\tweb3 = new Web3(new Web3.providers.HttpProvider("http://' + this.rpcHost + ':' + this.rpcPort + '"));'; - result += '\n}'; - result += "\nweb3.eth.defaultAccount = web3.eth.accounts[0];"; + var providerPlugins = this.plugins.getPluginsFor('clientWeb3Provider'); + + if (providerPlugins.length > 0) { + providerPlugins.forEach(function(plugin) { + result += plugin.generateProvider(self); + }); + } else { + result += "\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {"; + result += '\n\tweb3 = new Web3(web3.currentProvider);'; + result += "\n} else if (typeof Web3 !== 'undefined') {"; + result += '\n\tweb3 = new Web3(new Web3.providers.HttpProvider("http://' + this.rpcHost + ':' + this.rpcPort + '"));'; + result += '\n}'; + result += "\nweb3.eth.defaultAccount = web3.eth.accounts[0];"; + } return result; }; ABIGenerator.prototype.generateContracts = function(useEmbarkJS) { + var self = this; var result = "\n"; - for(var className in this.contractsManager.contracts) { - var contract = this.contractsManager.contracts[className]; + var contractsPlugins = this.plugins.getPluginsFor('contractGeneration'); - var abi = JSON.stringify(contract.abiDefinition); - var gasEstimates = JSON.stringify(contract.gasEstimates); + if (contractsPlugins.length > 0) { + contractsPlugins.forEach(function(plugin) { + result += plugin.generateContracts({contracts: self.contractsManager.contracts}); + }); + } else { + for(var className in this.contractsManager.contracts) { + var contract = this.contractsManager.contracts[className]; - if (useEmbarkJS) { - result += "\n" + className + " = new EmbarkJS.Contract({abi: " + abi + ", address: '" + contract.deployedAddress + "', code: '" + contract.code + "', gasEstimates: " + gasEstimates + "});"; - } else { - result += "\n" + className + "Abi = " + abi + ";"; - result += "\n" + className + "Contract = web3.eth.contract(" + className + "Abi);"; - result += "\n" + className + " = " + className + "Contract.at('" + contract.deployedAddress + "');"; + var abi = JSON.stringify(contract.abiDefinition); + var gasEstimates = JSON.stringify(contract.gasEstimates); + + if (useEmbarkJS) { + result += "\n" + className + " = new EmbarkJS.Contract({abi: " + abi + ", address: '" + contract.deployedAddress + "', code: '" + contract.code + "', gasEstimates: " + gasEstimates + "});"; + } else { + result += "\n" + className + "Abi = " + abi + ";"; + result += "\n" + className + "Contract = web3.eth.contract(" + className + "Abi);"; + result += "\n" + className + " = " + className + "Contract.at('" + contract.deployedAddress + "');"; + } } } diff --git a/lib/deploy.js b/lib/deploy.js index fb600f12..43be7281 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -81,7 +81,7 @@ Deploy.prototype.checkAndDeployContract = function(contract, params, callback) { // a callback if (contract.onDeploy !== undefined) { self.logger.info('executing onDeploy commands'); - var abiGenerator = new ABIGenerator({}, self.contractsManager); + var abiGenerator = new ABIGenerator({contractsManager: self.contractsManager}); web3 = self.web3; var abi = abiGenerator.generateContracts(false); eval(abi); // jshint ignore:line diff --git a/lib/index.js b/lib/index.js index c30f8f7b..010f269b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -6,6 +6,7 @@ var grunt = require('grunt'); var mkdirp = require('mkdirp'); var colors = require('colors'); var chokidar = require('chokidar'); +var path = require('path'); var Cmd = require('./cmd.js'); var Deploy = require('./deploy.js'); @@ -24,6 +25,7 @@ var ServicesMonitor = require('./services.js'); var Console = require('./console.js'); var IPFS = require('./ipfs.js'); var Swarm = require('./swarm.js'); +var Plugins = require('./plugins.js'); var Embark = { @@ -43,6 +45,8 @@ var Embark = { this.config = new Config({env: env}); this.config.loadConfigFiles(options); this.logger = new Logger({logLevel: 'debug'}); + this.plugins = new Plugins({plugins: this.config.embarkConfig.plugins}); + this.plugins.loadPlugins(); }, redeploy: function(env) { @@ -132,6 +136,13 @@ var Embark = { Embark.redeploy(); }); callback(); + }, + function displayLoadedPlugins(callback) { + var pluginList = self.plugins.pluginList; + if (pluginList.length > 0) { + self.logger.info("loaded plugins: " + pluginList.join(", ")); + } + callback(); } ], function(err, result) { if (err) { @@ -239,7 +250,7 @@ var Embark = { }); }, function generateABI(contractsManager, callback) { - var abiGenerator = new ABIGenerator(self.config.blockchainConfig, contractsManager); + var abiGenerator = new ABIGenerator({blockchainConfig: self.config.blockchainConfig, contractsManager: contractsManager, plugins: self.plugins}); callback(null, abiGenerator.generateABI({useEmbarkJS: true})); } ], function(err, result) { @@ -265,13 +276,13 @@ var Embark = { }); }, function generateConsoleABI(contractsManager, callback) { - var abiGenerator = new ABIGenerator(self.config.blockchainConfig, contractsManager); + var abiGenerator = new ABIGenerator({blockchainConfig: self.config.blockchainConfig, contractsManager: contractsManager}); var consoleABI = abiGenerator.generateABI({useEmbarkJS: false}); Embark.console.runCode(consoleABI); callback(null, contractsManager); }, function generateABI(contractsManager, callback) { - var abiGenerator = new ABIGenerator(self.config.blockchainConfig, contractsManager); + var abiGenerator = new ABIGenerator({blockchainConfig: self.config.blockchainConfig, contractsManager: contractsManager, plugins: self.plugins}); callback(null, abiGenerator.generateABI({useEmbarkJS: true})); }, function buildPipeline(abi, callback) { diff --git a/lib/plugin.js b/lib/plugin.js new file mode 100644 index 00000000..7d7a16ef --- /dev/null +++ b/lib/plugin.js @@ -0,0 +1,42 @@ + +// TODO: pass other params like blockchainConfig, contract files, etc.. +var Plugin = function(options) { + this.name = options.name; + this.pluginModule = options.pluginModule; + this.clientWeb3Providers = []; + this.contractsGenerators = []; + this.pluginTypes = []; +}; + +Plugin.prototype.loadPlugin = function() { + (this.pluginModule.call(this, this)); +}; + +// TODO: add deploy provider +Plugin.prototype.registerClientWeb3Provider = function(cb) { + this.clientWeb3Providers.push(cb); + this.pluginTypes.push('clientWeb3Provider'); +}; + +Plugin.prototype.registerContractsGeneration = function(cb) { + this.contractsGenerators.push(cb); + this.pluginTypes.push('contractGeneration'); +}; + +Plugin.prototype.has = function(pluginType) { + return this.pluginTypes.indexOf(pluginType) >= 0; +}; + +Plugin.prototype.generateProvider = function(args) { + return this.clientWeb3Providers.map(function(cb) { + return cb.call(this, args); + }).join("\n"); +}; + +Plugin.prototype.generateContracts = function(args) { + return this.contractsGenerators.map(function(cb) { + return cb.call(this, args); + }).join("\n"); +}; + +module.exports = Plugin; diff --git a/lib/plugins.js b/lib/plugins.js new file mode 100644 index 00000000..132a19b2 --- /dev/null +++ b/lib/plugins.js @@ -0,0 +1,31 @@ +var Plugin = require('./plugin.js'); +var path = require('path'); + +var Plugins = function(options) { + this.pluginList = options.plugins || []; + this.plugins = []; +}; + +Plugins.prototype.loadPlugins = function() { + var i, plugin; + for (i = 0; i < this.pluginList.length; i++) { + plugin = this.pluginList[i]; + this.loadPlugin(plugin); + } +}; + +Plugins.prototype.loadPlugin = function(pluginName) { + var plugin = require(path.join(process.env.PWD, 'node_modules', pluginName)); + + var pluginWrapper = new Plugin({name: pluginName, pluginModule: plugin}); + pluginWrapper.loadPlugin(); + this.plugins.push(pluginWrapper); +}; + +Plugins.prototype.getPluginsFor = function(pluginType) { + return this.plugins.filter(function(plugin) { + return plugin.has(pluginType); + }); +}; + +module.exports = Plugins; diff --git a/lib/test.js b/lib/test.js index 7f5661af..8835651b 100644 --- a/lib/test.js +++ b/lib/test.js @@ -54,7 +54,7 @@ Test.prototype.deployAll = function(contractsConfig, cb) { }); }, function generateABI(contractsManager, callback) { - var abiGenerator = new ABIGenerator({}, contractsManager); + var abiGenerator = new ABIGenerator({contractsManager: contractsManager}); var ABI = abiGenerator.generateContracts(false); callback(null, ABI); } diff --git a/test/abi.js b/test/abi.js index 96351e1b..133402ce 100644 --- a/test/abi.js +++ b/test/abi.js @@ -7,7 +7,7 @@ var assert = require('assert'); describe('embark.ABIGenerator', function() { describe('#generateProvider', function() { - var generator = new ABIGenerator({rpcHost: 'somehost', rpcPort: '1234'}, {}); + var generator = new ABIGenerator({blockchainConfig: {rpcHost: 'somehost', rpcPort: '1234'}, contractsManager: {}}); it('should generate code to connect to a provider', function() { var providerCode = "\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {\n\tweb3 = new Web3(web3.currentProvider);\n} else if (typeof Web3 !== 'undefined') {\n\tweb3 = new Web3(new Web3.providers.HttpProvider(\"http://somehost:1234\"));\n}\nweb3.eth.defaultAccount = web3.eth.accounts[0];"; @@ -17,7 +17,7 @@ describe('embark.ABIGenerator', function() { }); describe('#generateContracts', function() { - var generator = new ABIGenerator({}, { + var generator = new ABIGenerator({blockchainConfig: {}, contractsManager: { contracts: { SimpleStorage: { abiDefinition: [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initialValue","type":"uint256"}],"type":"constructor"}], @@ -32,7 +32,7 @@ describe('embark.ABIGenerator', function() { code: '123456' } } - }); + }}); describe('with EmbarkJS', function() { var withEmbarkJS = true;