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

@choux/compose

v1.0.4

Published

Compose Classes! There are many like it, but this one is for _you_.

Downloads

8,153

Readme

Compose Classes! There are many like it, but this one is for you.

Example with simplified Mongo collections

Consider a few basic interfaces into persistent storage:

class Users {

  static get collection() { return db.collection('users') }

  static get schema() {
    return Joi.Object({
      name: Joi.string().required()
    })
  }

  static async findByName(name) { /* ... */ }

}


class Devices {

  static get collection() { return db.collection('devices') }

  static get schema() {
    return Joi.Object({
      started: Joi.bool().default(false)
    })
  }

  static async startAll() { /* ... */ }

}

Ok. Nice and lean. We can keep them that way by encapsulating some common behavior:

class CommonOperations {

  static assertSchema(data) {
    return Joi.attempt(data, this.schema)
  }

  static async create(data) {
    const newThing = CommonOperations.assertSchema(data)

    return this.collection.insertOne(newThing)
  }

  static async findById(id) {
    return this.collection.findOne({ id })
  }

  static async armageddon() {
    console.warn(`Removing all documents from ${this.collection}!`)
    return this.collection.remove({})
  }

}

Time to beef these up!:

// Compose `CommonOperations` into our interfaces...
const BeefyUsers = compose([ CommonOperations ])(Users)
const BeefyDevices = compose([ CommonOperations ])(Devices)

// ...and we can make use of the common operations
const favoriteUser = await Users.findById(1)
const favoriteDevice = await Device.findById(12)

// For a more useful example:
const maru = { name: 'maru' }
const stemCellReactor = { started: false }

// CommonOperations delegates to the correct Class schema
await Users.create(maru)
await Devices.create(stemCellReactor)

Less creative examples:

class Target {

  static get targetGetter() { return '0' }

  static originalMethod() { return 0 }

}

class FirstSource {

  static get firstSourceGetter() { return '1' }

  static get overwritableGetter() { return 'first compose\'s getter' }

  static firstSourceMethod() { return 1234 }

  static overwritableMethod() { return 'first compose\'s method' }

}

class SecondSource {

  static get secondSourceGetter() { return '2' }

  static get overwritableGetter() { return 'second compose\'s getter' }

  static overwritableMethod() { return 'second compose\'s method' }

  static secondSourceMethod() { return 5678 }

}

// compose a class from multiple sources
const ComposedClass = compose([ FirstSource, SecondSource ])(Target)

// applies the static methods and getters of multiple source classes

Target.targetGetter
// -> '0'

Target.firstSourceGetter
// -> '1'

Target.secondSourceGetter
// -> '2'

Target.originalMethod()
// -> 0

Target.firstSourceMethod()
// -> 1234

Target.secondSourceMethod()
// -> 5678

// composed methods/getters with naming collisions supersede each other according to their position in the array
// the last composed class supersedes the first. à la reduce right:
Target.overwritableMethod()
// -> 'second compose\'s method'

// 'Multiple composed classes do not overwrite each other\'s methods or getters', () => {
FirstSource.overwritableGetter
// -> 'first compose\'s getter'

FirstSource.overwritableMethod()
// -> 'first compose\'s method'

SecondSource.overwritableGetter
// -> 'second compose\'s getter'

SecondSource.overwritableMethod()
// -> 'second compose\'s method'