修改公共npm包中的某些功能

时间:2016-04-04 19:33:23

标签: javascript node.js

我正在尝试重载/替换ami-io npm包中的函数。创建该包是为了与套接字接口asterisk AMI进行通信。

我需要与具有几乎完全相同的界面的服务进行通信,但是在登录时它会显示不同的问候语字符串,并且在登录时需要额外的字段。其余的都是一样的。我想覆盖检测问候语字符串和登录功能的函数,而不是仅仅复制600 LOC ami-io包并修改两行或三行,并继续使用ami-io包。

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;
})();

我认为我理解的是Action是来自Message的子类。 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上开了一个问题。

3 个答案:

答案 0 :(得分: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'的活动。

要覆盖Client#auth()方法,您应该执行

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

答案 1 :(得分:0)

amiioClient实例prototype属性仅对构造函数有意义,例如Client。它对构造函数的结果没有意义(除了在不常见的情况下实例恰好也是函数本身 - 但即使在这种情况下,更改实例的prototype也不会影响其父构造函数)

相反,您需要使用Object.getPrototypeOf获取实例的原型:

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

如果您不需要为每个客户端更改此设置,但只需要更改一个客户端,则根本不需要更改原型。更改实例的auth就足够了:

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

请注意,如果Action.LoginExt是模块范围的本地代码,则代码将无效。如果模块导出它,您可以改为AmiIo.Action.LoginExt。如果它不导出LoginExt,您将需要复制实现它的代码,并在导入范围中重新实现它。修改模块本身可能更简单。

答案 2 :(得分: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的答案都帮助了我,但我只能将其中一个作为最佳答案。