DCL

An elegant OOP with mixins + AOP for JavaScript.

advise()

Version 2.x

This is a function that weaves AOP advices dynamically. Unlike dcl.advise() it works on objects, rather than when defining “classes”. All advices can be “unadvised” at any moment.

Description

advise() is a function, which takes three parameters:

  • obj - the object to advise. Any object would do, including objects produced without dcl-made constructors.
  • name - the method name. If the method had AOP advices defined by dcl(), and/or previous calls to advise(), they will be properly combined.
  • advice - the object with properties before, around, and/or after. See dcl.advise() for more details.

It returns the object, which defines the method unadvise(). When called without parameters, it removes the corresponding advice from the object, no matter when it was defined. For convenience, this method is aliased as remove(), and destroy().

How to use advices is described in details in dcl.advise().

advise()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var a = {
  method: function (msg) { cosole.log("MSG: " + msg); }
};

var methodAdv = advise(a, "method", {
  before: function (msg) {
    console.log("Method was called with msg = " + msg);
  },
  after: function (args, result) {
    console.log("Method has finished.");
  },
  around: function (sup) {
    return function (msg) {
      // let's ignore our parameter
      sub.call(this, "Canned response no matter what.");
    };
  }
});

a.method("Hey!");
// Method was called with msg = Hey!
// MSG: Canned response no matter what.
// Method has finished.

methodAdv.unadvise();
// Now all previous advices are removed from the object.

Like dcl.advise(), advise() can be used to advise getters and setters. Unlike dcl.advise(), it cannot dynamically convert values to getters, and getters to values. So if you want to advise a getter (or a setter), they should be already defined.

advise() with getters/setters
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var a = {
  get x ()  { return this.v || 0; },
  set x (v) { this.v = v; }
};

var propAdv = advise(a, "x", {
  get: {
    before: function () {
      console.log("getting x");
    },
    after: function () {
      console.log("Getter has finished.");
    },
    around: function(sup){
      return function(){
        return 2 * sup.call(this);
      };
    }
  },
  set: {
    before: function (v) {
      console.log("setting x to " + v);
    },
    after: function () {
      console.log("Setter has finished.");
    }
  }
});

Examples

advise() changes return values
1
2
3
4
5
6
7
8
9
10
11
var a = ...;

advise(a, 'm', {
  after: function (args, result, makeReturn, makeThrow) {
    if (result % 2) {
      makeReturn(1);
      return;
    }
    makeThrow(new Error("evil even number!"));
  }
});

Notes

Shortcuts

If you want to weave just one advice, you may want to use a shortcut:

advise.before()
1
2
3
4
5
6
7
8
9
advise.before(a, "method", function (msg) {
  console.log("Method was called with msg = " + msg);
});
// is equivalent to
advise(a, "method", {
  before: function (msg) {
    console.log("Method was called with msg = " + msg);
  }
});
advise.after()
1
2
3
4
5
6
7
8
9
advise.after(a, "method", function () {
  console.log("Method has finished.");
});
// is equivalent to
advise(a, "method", {
  after: function () {
    console.log("Method has finished.");
  }
});
advise.around()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
advise.around(a, "method", function (sup) {
  return function (msg) {
    // let's ignore our parameter
    sub.call(this, "Canned response no matter what.");
  };
});
// is equivalent to
advise(a, "method", {
  around: function (sup) {
    return function (msg) {
      // let's ignore our parameter
      sub.call(this, "Canned response no matter what.");
    };
  }
});

You can find those methods documented respectively in advise.before(), advise.after(), and advise.around().