declassified

    1.0.1 • Public • Published

    declassified

    Motivation

    A lot of libraries come in the form

    class MyThing {
      constructor(config) {
        // much around with this
        this.thing = config[thing]
      }
    
      method() {
        // subtly depend on values set during the configuration phase
        something(this.config)
      }
    }

    This has a few problems:

    • The this.X properties are mutable throughout the code.
    • The constructor is synchronous meaning that often times the class isn't usable until some methods have been called.

    What is preferable

    What if, instead of a class, the authors had exported a library. It might look something like:

    async function myThing (config) => {
      await doInitialization()
      return {
        method() {
          something(config)
        }
      }
    }

    This allows asynchronous set-up and protects the internals of the library (the configs, any other arguments) from imperative modification.

    The solution

    const declassified = require('declassified')
    const MyClass = require('something')
    
    const myLibrary = declassified(MyClassBasedLibrary, async (instance) => {
      // do imperative things to the instance here that you'd want to do as part of the setup
    })
    
    const myThing = await myLibrary(configs)

    API

    The declassify function takes only two arguments

    • The class-based library to be wrapped
    • (optional) An init function for your class which will be called with a "newed" class instance. The return value is used if supplied, otherwise it defaults to the class instance. Defaults to the identity function.

    It returns an async function "config" that takes the same arguments as the original constructor.

    When called the "config" function will return a frozen copy of the object returned by init

    Examples

    pg

    Say you want to connect to a database like Postgres. The node pg client requires you to

    const { Client } = require('pg')
    
    const contructorArgs = { host: 'my-db' } //...
    const client = new Client(contructorArgs)
    await client.connect()
    
    const res = await client.query('SELECT $1::text as message', ['Hello world!'])
    console.log(res.rows[0].message) // Hello world!

    How might we declassify that?

    const declassified = require('declassified')
    const { Client } = require('pg')
    
    const contructorArgs = { host: 'my-db' } //...
    const init = async (instance) => {
      await instance.connect()
      return instance
    }
    
    const client = await declassified(Client, init)(constructorArgs)
    
    const res = await client.query('SELECT $1::text as message', ['Hello world!'])
    console.log(res.rows[0].message) // Hello world!

    So you might ask: this looks like it's just extra lines to do the same thing, but it's not: Say someone set client.connection = something. Instead of being meaningless code, that has broken your client permanently. It might not have even occurred during the handling of the request you're looking at due to the asynchronous nature of node. In large code bases (particularly large test suites where people are mocking and meta-programming frequently) these are real bugs that can be avoided.

    Install

    npm i declassified

    DownloadsWeekly Downloads

    5

    Version

    1.0.1

    License

    MIT

    Unpacked Size

    6.79 kB

    Total Files

    5

    Last publish

    Collaborators

    • metalsheep