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

dynamic_variables.js

v0.0.5

Published

Dynamic variables i.e. global variables with dynamic scope

Downloads

75

Readme

dynamic_variables.js

Global variables...with dynamic context.

From the original write-up:

Let's go back the definition that I gave earlier: global variables, with dynamic scope. I know that the first rule of global variables is: "thou shalt not use global variables", but it's just that sometimes they appear to be right tool for the job, especially in the context of Web applications; think about the following use cases:

  • getting a hold of the currently logged in user
  • automatically adding the content of the X-Request-Id header to each log trace
  • querying the right database, based on the logged in user's tenant

How would you implement these? Either you shove all this information into a context object, and pass it around, everywhere; or maybe you forget about all the bad things you read about global variables, and consciously and carefully agree to use them where it really matters, where it really makes a difference.

Status

It works but relies on async_hooks, a Node.js API still marked as experimental; so feel free to use it, but carefully test as many asynchronous code paths as possible and confirm the asynchronous context is not in any way lost.

API

Dynamic environments

Creating a dynamic environment

The library automatically creates a dynamic environment for you, and makes it available to the running Node.js instance through the env module export:

var { env } = require('dynamic_variables.js');

Alternatively, if you don't feel like using a shared dynamic environment, you can create a private environment as follows:

var { DynamicEnvironment } = require('dynamic_variables.js');

var env1 = new DynamicEnvironment();

var env2 = new DynamicEnvironment('a', 1);

Getting a binding

To get a binding (i.e. the value currently bound to a specific variable name) you can use the DynamicEnvironment.prototype.get method:

var env = new DynamicEnvironment('a', 1, 'b', 2)

> env.get('a')
1

> env.get('b')
2

> env.get('c')
Uncaught Error: Dynamic variable, unbound: 'c'
    at Bindings.get (repl:4:11)
    at DynamicEnvironment.get (repl:2:30)

> env.get()
Uncaught AssertionError [ERR_ASSERTION]: Dynamic variable name, invalid: undefined
    at Bindings.get (repl:2:3)
    at DynamicEnvironment.get (repl:2:30)
    at repl:1:5
    at Script.runInThisContext (vm.js:131:18)
    at REPLServer.defaultEval (repl.js:472:29)
    at bound (domain.js:430:14)
    at REPLServer.runBound [as eval] (domain.js:443:12)
    at REPLServer.onLine (repl.js:794:10)
    at REPLServer.emit (events.js:326:22)
    at REPLServer.EventEmitter.emit (domain.js:486:12) {
  generatedMessage: false,
  code: 'ERR_ASSERTION',
  actual: undefined,
  expected: true,
  operator: '=='
}

Setting a new binding

Lastly, DynamicEnvironment.prototype.set can be used to set a new binding that's going to persist across subsequent asynchronous operations as well:

  var env = new DynamicEnvironment("x", 5);

  var foo = function () {
    return env.get("x");
  };

  var bar = function () {
    return env.set("x", 42, () => {
      return new Promise((resolve) => {
        setTimeout(() => resolve(foo()), 2000);
      });
    });
  };

  assert.equal(env.get("x"), 5);
  assert.equal(foo(), 5);
  assert.equal(await bar(), 42);
  assert.equal(env.get("x"), 5);

Note: you can omit the last argument (i.e. the body function) when calling DynamicEnvironment.prototype.set, and in which case the change will affect the current execution context, and not just subsequent ones:

  var env = new DynamicEnvironment("x", 5);

  var foo = function () {
    return env.get("x");
  };

  var bar = function () {
    return env.set("x", 42);
  };

  assert.equal(env.get("x"), 5);
  assert.equal(foo(), 5);
  await bar();
  assert.equal(env.get("x"), 42);

Dynamic variables

Creating a dynamic variables

Sometimes creating an environment is a bit too much especially if you plan to store a single binding in it. You can create an instance of DynamicVariable instead:

var { DynamicVariable } = require('dynamic_variables.js');

var x = new DynamicVariable();

var y = new DynamicVariable(42);

Getting the current value

To get the value currently bound to the dynamic variable you can use the DynamicVariable.prototype.get method:

var x = new DynamicVariable(42)

> x.get()
42

Setting a new value

Lastly, DynamicVariable.prototype.set can be used to override the dynamic variable's value, and make sure the new value persists across subsequent asynchronous operations:

  var x = new DynamicVariable(5);

  var foo = function () {
    return x.get();
  };

  var bar = function () {
    return x.set(42, () => {
      return new Promise((resolve) => {
        setTimeout(() => resolve(foo()), 2000);
      });
    });
  };

  assert.equal(x.get(), 5);
  assert.equal(foo(), 5);
  assert.equal(await bar(), 42);
  assert.equal(x.get(), 5);

Note: you can omit the last argument (i.e. the body function) when calling DynamicVariable.prototype.set, and in which case the change will affect the current execution context, and not just subsequent ones:

  var x = new DynamicVariable(5);

  var foo = function () {
    return x.get();
  };

  var bar = function () {
    return x.set(42);
  };

  assert.equal(x.get(), 5);
  assert.equal(foo(), 5);
  await bar();
  assert.equal(x.get(), 42);

Changelog

0.0.5:

  • New "stack-of-frames"-based implementation enabling clients to interact with the dynamic environment, from the outside (you can read more about this, here)
  • DynamicVariable is now implemented on top of DynamicEnvironment (i.e. it's a dynamic environment with a single binding, whose name is private)

0.0.4:

  • Rolls back few changes accidentally published to the npm registry

0.0.3:

  • Fixes a problem where the env export was not a proper instance of DynamicEnvironment (we were calling the constructor function without new before)

0.0.2:

  • Exports DynamicVariable for creating a private dynamic variable

0.0.1:

  • Exports env, a dynamic environment shared with all the modules loaded in the current Node.js instance
  • Exports DynamicEnvironment for creating a private dynamic environment