1.0.8 • Published 4 years ago

composable-factory v1.0.8

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago

Composable factory

Define and use composable factory functions with fun!

Being heavily inspired by stampit, this library promotes Composable Factory Functions (CFF) as an alternative pattern to use over Class Hierarchies and let you combine multiple reusable objects together.

Why should I use this library?

  • ComposableFactory focuses on simplicity and provides a clean API without thousands of options and possibilities which create only confusion. (Keep it simple, stupid).

    You wanted a composable factory function - you got it! Nothing more!

  • This library has a small footprint of about 48 SLOC.

  • Advanced TypeScript support. Types definitions not only cover ComposabeFactory built-in methods but also the properties and methods of the created object instances. Demo:

import { ComposableFactory } from 'composable-factory';

interface TodoInterface { 
    name: string,
    status: number,
    setDone?: () => void,
    someMethod?: () => string
}

ComposableFactory<TodoInterface>({ 
    properties: {   
        // Only name, status are valid properties. 
        // TypeScript will complain if you put something else
    },
    init({ name }) {
        // this.name, this.status, this.setDone() and this.someMethod() are all accessible 
        // 'this' scope is TodoInterface 
    },
    methods: {
        
        // Only methods from TodoInterface are acceptable. 
        // Methods signatures are checked while you are implementing them
        
        someMethod() {
            // this.name, this.status, this.setDone() and this.someMethod() are all accessible 
            // 'this' scope is TodoInterface
        }
    },
});

// typescript will complain about missing options
const TodoFactory = ComposableFactory<TodoInterface>(); 

// typescript will complain about missing options.properties given that name and status are required
const TodoFactory = ComposableFactory<TodoInterface>({});

// typescript will complain again about missing status property
const TodoFactory = ComposableFactory<TodoInterface>({
  properties: {
    name: '',
  } 
}); 

// OK. Typescript will ignore missing options.methods because both setDone() and someMethod() are optional.
const TodoFactory = ComposableFactory<TodoInterface>({
  properties: {
    name: '',
    status: 0,
  } 
});
  • Finally if you ever want to understand how this library works, the source code is friendly readable and clean.

Table of content

  1. Installation
  2. Usage
  3. API
  4. Contributing
  5. License

Installation

npm install composable-factory --save

Usage

The API for both JavaScript and TypeScript is almost the same. JavaScript users should ignore types parameters and remove angle brackets (<>). For this reason the examples in this section and also in the API docs will be demonstrated using TypeScript.

TypeScript

import { ComposableFactory } from 'composable-factory';

JavaScript

const { ComposableFactory } = require('composable-factory');

Usage example

interface EngineInterface {
    speed: number,
    accelerate: (incr: number) => number,
    decelerate: (decr: number) => number,
}

interface BreaksInterface {
    speed: number,
    stop(): number
}

interface BodyInterface {
    colour: string,
    design: string,
}

const EngineFactory = ComposableFactory<EngineInterface>({
    // default properties
    properties: {
        speed: 0,
        maxSpeed: 180,
    },
    init({ maxSpeed }) {
        if (maxSpeed) this.maxSpeed = maxSpeed;
    }, 
    methods: {
        accelerate(incr) {
            this.speed = this.speed + incr;
            return this.speed;
        },
        decelerate(decr) {
            this.speed = this.speed - decr;
            return this.speed;
        }
    }
});

const BreaksFactory = ComposableFactory<BreaksInterface>({
    methods: {
        stop() {
            while (this.speed > 0) {
                this.speed = this.speed - 1;
            }
            return 0;
        }
    }
});

const BodyFactory = ComposableFactory<BodyInterface>({
    // default properties
    properties: {
        colour: 'yellow',
        design: 'suv'
    },
    init({ colour, design }) {  
        if (colour) this.colour = colour;
        if (design) this.design = design; 
    }
});

const Car = EngineFactory.compose(BreaksFactory).compose(BodyFactory);
const car = Car({design: 'sport car', maxSpeed: 300});

expect(car.speed).toEqual(0)
expect(car.maxSpeed).toEqual(300)
expect(car.color).toEqual('yellow')
expect(car.design).toEqual('sport car')

car.accelerate(100);
expect(car.speed).toEqual(100)

car.stop();
expect(car.speed).toEqual(0)

API

A picture is worth a thousand words. The big picture looks like:

type ComposableFactoryParams = {
  properties: {[key:string]: any},
  init(initOptions: {[key:string]:any}) => void,
  methods: { (...args: any[]) : any }, 
  statics: { [key: string]: any },
}

ComposableFactory(ComposableFactoryParams) => ComposableInterface

type initOptions = {
    [key: string]: any
};

ComposableInterface(initOptions) => Object instance

ComposableInterface.compose( ComposableFactoryParams | ComposableInterface  ) => ComposableInterface

ComposableFactory.compose(ComposableInterface[]) => ComposableInterface

See API Reference for more details.

Contributing

So you are interested in contributing to this project? Please see CONTRIBUTING.md.

License

MIT

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago