npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

exedore

v1.0.0

Published

Library for aspect-oriented programming

Downloads

6

Readme

Exedore

Exedore is a JavaScript library for aspect-oriented programming (AoP) in ES6 and newer versions of JavaScript.

It is based on Aop.js by Fredrik Appelberg and Dave Clayton, and on the unit tests in Chapter 2 of Reliable JavaScript: How to Code Safely in the World's Most Dangerous Language by Seth Richards and Lawrence Spencer (ISBN 9781119028727). From this foundation, Exedore was extended to interoperate with ES6 classes, as easily as Aop.js works with functions defined on plain objects.

Understanding Aspect-Oriented Programming (AOP)

Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.) -- Spring Framework documentation

The first paragraph of the Wikipedia entry on Aspect-Oriented Programming also has a good definition, which I won't bother to repeat here.

We don't deal with a lot of transaction management in the JavaScript world, so think about logging instead. You don't want to write a bunch of logging code in all of your functions. AOP allows you to write generic logging functions and then insert them before or after the specific functions that you want to log. You can see this in example 1.

In the jargon of AOP, an aspect is a modularization of a cross-cutting concern. An advice is the action that the aspect takes at any given point. In other words, an advice is the function that wraps around a target function, and an aspect is a library or class that contains one or more advices.

Basic Usage


IMPORTANT

When defining an advice function, do NOT use arrow functions (aka "fat arrows"). Use the old school function declaration:

const advice = function( target, args ) { /* do stuff */ }

This is because arrow functions lexically bind the value of this to the context where they are defined. In AOP, we want to be able to control the context of the advice. We move advice functions to many different contexts, so we don't want them to be bound to a particular context. Don't use arrow functions.


Start, of course, by importing Exedore into your file:

import Exedore from 'exedore';

Executing Advice Before the Target

Suppose that you want to monitor the usage of certain functions. This is a perfect use-case for AOP! First, we want to know when a function is called and what arguments it is called with. Here are our functions, as properties on a plain object. We are also creating an empty array to store the log messages.

const log = [ ];
const math = {
    add: function( a, b ) { return a + b; },
    multiply: function( a, b ) { return a * b; }
};

Create the Advice Function

An advice function recieves two arguments: the original or target function, and the arguments to the target function. Our logger function will push information about the function call onto the log array

const loggerBefore = function( target, args )  {
    const beforeMessage = `Function '${target.name}' called with ${args.toString()}`;
    log.push( beforeMessage );
}

Using the Advice

To use the advice function, we wrap the function(s) that we want to log. The before function takes as its arguments (1) the object that contains the target function, (2) the name of the target function as a string, and (3) the advice function.

Exedore.before( math, 'add', loggerBefore );
Exedore.before( math, 'multiply', loggerBefore );

Then we use the functions and view the log.

math.multiply( 3, 9 );
math.add( 1, 1 );
math.multiply( 4, 4 );
math.add( 2, 2 );

console.log( `The log has ${log.length} entries` );
log.forEach( entry => console.log( entry ) );

All of this code comes from example 1, so you can see it all together in that file. To run the examples, clone this repository, install the dependencies with npm install, and execute npm run example:01 (or the number of the example you want to see).

↓ TODO: Start here ↓

Executing Advice After the Target

Adding Multiple Advice Functions

Advanced Usage

Wrap

To execute the original function, in the body of the advice, call Exedore.next. Pass it the current context this, the target function, and the args to the target function.

The next function will return the result of calling the target function with the provided arguments. This means that Exedore will let you modify the arguments before passing them to the target!

const result = Exedore.next( this, target, args );

You can also modify the result of the target function before returning it.

Putting it all together, a "logger" advice function might look like this:

const logger = function( target, args ) {
    const beforeMessage = `Function '${target.name}' called with ${args.toString()}`;
    log.push( beforeMessage );

    const result = Exedore.next( this, target, args );

    const afterMessage = `Function '${target.name}' returned ${result}`;
    log.push( afterMessage );

    return result;
};

Use the Advice

Before we can use the logger advice function, we need some things to use it with. In the example, these all come before the advice function is definied.

const log = [ ];
const math = {
    add: function( a, b ) { return a + b; },
    multiply: function( a, b ) { return a * b; }
};

Next, we wrap the function(s) that we want to log. The wrap functions takes as its arguments (1) the object that contains the target function, (2) the name of the target function as a string, and (3) the advice function.

Exedore.wrap( math, 'add', logger );
Exedore.wrap( math, 'multiply', logger );

Then we use the functions and view the log.

math.multiply( 3, 9 );
math.add( 1, 1 );
math.multiply( 4, 4 );
math.add( 2, 2 );

console.log( `The log has ${log.length} entries` );
log.forEach( entry => console.log( entry ) );

All of this code comes from the example, so you can see it all together in that file. To run the examples, clone this repository, install the dependencies with npm install, and execute npm run example:01 (or the number of the example you want to see).

Exedore and Classes

TODO

Recommendations and Precautions

  • recommendations and precautions for using with module loaders and npm modules
    • only modify prototypes that you own
    • only wrap instances of modules objects
    • use custom factory or class if you need to wrap every instance (show example)