2016-04-04 20 views
0

我正試圖在npm包中重載/替換函數。該軟件包被創建用來與星號AMI(一個套接字接口)進行對話。在公共npm包中修改某些功能

我需要與具有幾乎完全相同接口的服務通話,但它在登錄時顯示不同的歡迎字符串,並且它在登錄時需要額外的字段。其餘的都是一樣的。我不想簡單複製600 LOC,而是修改兩行或三行,我想覆蓋檢測問候字符串和登錄功能的函數,並繼續使用包。

裏面ami-io包有一個文件index.js包含以下功能:

Client.prototype.auth = function (data) { 
    this.logger.debug('First message:', data); 
    if (data.match(/Asterisk Call Manager/)) { 
     this._setVersion(data); 
     this.socket.on('data', function (data) { 
      this.splitMessages(data); 
     }.bind(this)); 
     this.send(new Action.Login(this.config.login, this.config.password), function (error, response) { 
      if (response && response.response === 'Success') this.emit('connected'); 
      else this.emit('incorrectLogin'); 
     }.bind(this)); 
    } else { 
     this.emit('incorrectServer', data); 
    } 
}; 

現在我想不匹配Asterisk Call Manager,但MyService,我要定義和使用Action.LoginExt(this.config.login, this.config.password)與另一個帶有一個額外的參數。

這可能嗎?我在我自己的模塊嘗試這樣做:

var AmiIo = require('ami-io'); 
var amiio = AmiIo.createClient({port:5038, host:'x.x.x.x', login:'system', password:'admin'}); 


amiio.prototype.auth = function (data) { 
    this.logger.debug('First message:', data); 
    if (data.match(/MyService Version/)) { 
    this._setVersion(data); 
    this.socket.on('data', function (data) { 
     this.splitMessages(data); 
    }.bind(this)); 
    this.send(new Action.LoginExt(this.config.login, this.config.password, this.config.extra), function (error, response) { 
     if (response && response.response === 'Success') this.emit('connected'); 
     else this.emit('incorrectLogin'); 
    }.bind(this)); 
    } else { 
    this.emit('incorrectServer', data); 
    } 
}; 

...但它造成了TypeError: Cannot set property 'auth' of undefined,現在我無言以對。 另外,我可以在自己的模塊中定義一個新的Action.LoginExt對象嗎?怎麼樣?

的action.js模塊定義的Action對象如下:

function Action(name) { 
    Action.super_.bind(this)(); 
    this.id = this.getId(); 
    this.set('ActionID', this.id); 
    this.set('Action', name); 
} 

(function(){ 
    var Message = require('./message.js'); 
    var util = require('util'); 
    util.inherits(Action, Message); 
})(); 

Action.prototype.getId = (function() { 
    var id = 0; 
    return function() { 
     return ++id; 
    } 
})(); 

function Login(username, secret) { 
    Login.super_.bind(this, 'Login')(); 
    this.set('Username', username); 
    this.set('Secret', secret); 
} 

... more functions ... 

(function() { 
    var actions = [ 
     Login, 
     ... more functions ... 
    ]; 
    var util = require('util'); 
    for (var i = 0; i < actions.length; i++) { 
     util.inherits(actions[i], Action); 
     exports[actions[i].name] = actions[i]; 
    } 
    exports.Action = Action; 
})(); 

我想我明白的是,行動是從信息的子類。 Login函數依次從Action繼承,並導出(在最後一個代碼塊中)。 所以我覺得在我的代碼,我可以嘗試類似的東西:

// extend ami-io with LoginExt function 
function LoginExt(username, secret, company) { 
    Login.super_.bind(this, 'LoginExt')(); 
    this.set('Username', username); 
    this.set('Secret', secret); 
    this.set('Company', company); 
} 

var util = require('util'); 
util.inherits(LoginExt, amiio.Action); 

但util.inherits失敗,不確定的。我也在ami-io上開了一個問題。

回答

1

您可以使用:

var AmiIo = require('ami-io'); 
AmiIo.Action.Login = function NewConstructor(){}; //to override Login action 
//new constructor shold extend AmiIo.Action.Action(actionName) 
//and also, you can use 
AmiIo.Action.SomeNewAction = function SomeNewAction(){};//to create new actuion 
//it also should extend AmiIo.Action.Action(actionName); 

AmiIo.Action就是一個對象。所有構造函數都是它的字段。

要創建新事件,您不需要執行任何操作,因爲它只是一個對象。如果服務器發送給您

Event: Armageddon 
SomeField: 123 

ami-io將創建名稱爲'Armageddon'的事件。

要覆蓋客戶端#AUTH()方法,你就應該做

var AmiIo = require('ami-io'); 
AmiIo.Client.prototype.auth = function(){};//new function 
0

amiio實例Clientprototype屬性僅對構造函數有意義,例如Client。它對構造函數的結果沒有意義(除非在罕見的情況下,實例碰巧也是一個函數本身 - 但即使在這種情況下,更改實例的prototype也不會影響其父構造函數)。

相反,你需要Object.getPrototypeOf來獲得實例的原型:

Object.getPrototypeOf(amiio).auth = function() { ... } 

如果您不需要更改此爲每個客戶端,但只有一個單一的客戶端,你不需要改變原型。更改實例的auth足夠:

amiio.auth = function() { ... } 

注意,如果Action.LoginExt是本地模塊的範圍,你的代碼將無法正常工作。如果模塊將其導出,則可以改爲使用AmiIo.Action.LoginExt。如果它不導出LoginExt,則需要複製實現該代碼的代碼,並將其重新實現到導入作用域中。修改模塊本身可能會更簡單。

+0

謝謝。這很簡單。我現在只剩下添加/擴展Action的角色了。修改模塊本身可能確實比較簡單,但是我必須將其引入我自己的代碼庫中,這會產生技術債務。或者......分叉它,重命名它,並將其放入npm回購。只有幾行代碼... – raarts

0

這裏是我申請的解決方案工作:

// Override the AmiIo auth procedure, because the other login is slightly different 
// Write our own Login function (which adds a company) 
function Login(username, secret, company) { 
    Login.super_.bind(this, 'Login')(); 
    this.set('Username', username); 
    this.set('Secret', secret); 
    this.set('Company', company); 
} 

// This function should inherit from Action 
var util = require('util'); 
util.inherits(Login, AmiIo.Action.Action); 
AmiIo.Action.Login = Login; 

// replace the auth with our own, to add the company. Also 
// this sends a slightly different greeting: "Service Version 1.0" 
AmiIo.Client.prototype.auth = function (data) { 
    if (data.match(/Service Version/)) { 
    this._setVersion(data); 
    this.socket.on('data', function (data) { 
     this.splitMessages(data); 
    }.bind(this)); 
    this.send(new AmiIo.Action.Login(this.config.login, this.config.password, this.config.company), function (error, response) { 
     if (response && response.response === 'Success') this.emit('connected'); 
     else this.emit('incorrectLogin'); 
    }.bind(this)); 
    } else { 
    this.emit('incorrectServer', data); 
    } 
}; 

// our own function to grab the version number from the new greeting 
AmiIo.Client.prototype._setVersion = function(version){ 
    var v = version.match(/Service Version ([\d\.]*[\-\w\d\.]*)/i); 
    if (v){ 
    this.version = v[1]; 
    } 
}; 

因此,原來這是爲可行的,因爲我希望這將是。 @NumminorihSF和@apsillers的答案都幫助我,但我可以將其中的一個標記爲最佳答案。