# o-toolbox

> Utilities for Node.js.

Latest version **7.7.0** (published 2021-08-22) · ISC license · 0 weekly downloads

## Install

```sh
npm install o-toolbox
pnpm add o-toolbox
yarn add o-toolbox
bun add o-toolbox
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 7.7.0 |
| Published | 2021-08-22 |
| First published | 2020-06-13 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 84 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Martin Rubi |
| Maintainers | haijindev |
| Keywords | utilities, helpers |

## Links

- npm: https://www.npmjs.com/package/o-toolbox
- Repository: https://bitbucket.org/haijin-development/toolbox
- Homepage: http://o-programming-language.org
- npm.io page: https://npm.io/package/o-toolbox

## Alternatives

- [lodash.assign](https://npm.io/package/lodash.assign.md) — 2.3M weekly downloads
- [lodash.chunk](https://npm.io/package/lodash.chunk.md) — 1.8M weekly downloads
- [react-native-ios-utilities](https://npm.io/package/react-native-ios-utilities.md) — 138.5K weekly downloads
- [@technically/lodash](https://npm.io/package/@technically/lodash.md) — 50.9K weekly downloads
- [@fluid-topics/ft-icon](https://npm.io/package/@fluid-topics/ft-icon.md) — 20.6K weekly downloads

## Recent versions

- 7.7.0 (latest) — 2021-08-22
- 7.6.0 — 2021-06-19
- 7.5.0 — 2021-02-12
- 7.4.0 — 2021-02-04
- 7.3.0 — 2021-01-29
- 7.2.0 — 2021-01-24
- 7.1.0 — 2021-01-20
- 7.0.0 — 2021-01-18
- 6.3.2 — 2021-01-14
- 6.3.1 — 2021-01-12
- 6.3.0 — 2021-01-11
- 6.2.1 — 2021-01-09
- 6.2.0 — 2021-01-09
- 6.1.0 — 2021-01-09
- 6.0.0 — 2021-01-06
- … 11 more at https://npm.io/package/o-toolbox/versions

## README

# Toolbox

Utilities for Node.js.

## Installation

```
npm install o-toolbox
```

## Documentation

[http://o-programming-language.org/](http://o-programming-language.org/)

## Usage

### Type

Utility to query the type of an object

```javascript
const {Type} = require('o-toolbox')

Type.isUndefined(object)
Type.isNull(object)
Type.isBoolean(object)
Type.isString(object)
Type.isInteger(object)
Type.isFloat(object)
Type.isFunction(object)
Type.isArray(object)
Type.isObject(object)
Type.isPromiseAlike(object)
Type.respondsTo(object, methodName)
```

Using `typeof` has some problems. `typeof(null) === 'object'`, `Number.isInteger()` exists but `Number.isFloat()` does not and there is no type `boolean` to name a few.

This utility aims to address some of these issues.


Using `instanceof` is also discouraged since it does not allow full polymorphism. Instead of asking for the class of an object in some cases asking whether the object responds to message or not might be a better choice.

To ask that there's the method


```javascript
const {Type} = require('o-toolbox')

Type.repondsTo(object, methodName)
```

Finally there's the method 

```javascript
Type.displayString(object)
```

which returns a string representation of the object suitable for debugging messages. For example

```javascript
const {Type} = require('o-toolbox')

Type.displayString(undefined) === 'undefined'
Type.displayString(null) === 'null'
Type.displayString('a string') === "'a string'"
Type.displayString(new User()) === "a User"
```

### Copy

Copies addresses the copy of an object without having to check first the type of the object.
It copies undefined and null values, basic type values, arrays, objects and class instances.

To make a shallow copy of an object do

```javascript
Copy.shallow(null)
Copy.shallow(1)
Copy.shallow({a: 1})
Copy.shallow(new SomeObject())
Copy.shallow([1,2,3])
```

If the object is an instance of a class it's expected to implement a `.copy()` method

```javascript
class SomeObject {
  copy() {
    return new this.constructor(this.props)
  }
}
```

To make a deep copy of an object use

```javascript
Copy.deep(null)
Copy.deep(1)
Copy.deep({a: 1})
Copy.deep(new SomeObject())
Copy.deep([1,2,3])
```

If the object is an instance of a class it's expected to implement a `.copy()` and/or a `.deepCopy()` method

```javascript
const Copy = require('o-toolbox')

class SomeObject {
  copy() {
    return new this.constructor(this.props)
  }

  deepCopy() {
    const propsCopy = this.props.map((prop)=> Copy.deep(prop))
    return new this.constructor(propsCopy)
  }
}
```

### Dictionary

A subclass of js `Map`.

It handles `Map` methods and adds the protocol


```javascript
const {Dictionary} = require('o-toolbox')

const dictionary = new Dictionary()

dictionary.isEmpty()
dictionary.getKeys()
dictionary.getValues()
dictionary.defines({ key: 'someKey' })
dictionary.define({ key: 'someKey', value: 10 })

dictionary.at ({ key: 'someKey'})
dictionary.at ({
  key: 'someKey',
  ifPresent: (value) => { ... }
})
dictionary.at ({
  key: 'someKey',
  ifAbsent: () => { ... }
})
dictionary.at ({
  key: 'someKey',
  ifPresent: (value) => { ... },
  ifAbsent: () => { ... }
})

dictionary.mergeKeysFrom ({prop1: 1, prop2: 2})
dictionary.mergeKeysFromDictionary(otherDictionary)

// shallow copy
dictionary.copy(anotherDictionary)

// The following methods are sync and will not work as expected with async code.

dictionary.keysDo((key) => {
  // ...
})
dictionary.valuesDo((key) => {
  // ...
})
dictionary.keysAndValuesDo((key, value) => {
  // ...
})
```

### Set

A subclass of js `Set`.

It handles `Set` methods and also adds the protocol


```javascript
const {Set} = require('o-toolbox')

const set = new Set()

set.isEmpty()
set.includes(item)
set.toArray()
set.equals(anotherSet)
set.contains(anotherSet)
set.intersects(anotherSet)
set.addAll(items)
set.remove(item)
set.removeAll(items)
set.union(anotherSet)
set.difference(anotherSet)
set.intersection(anotherSet)
set.copy()
```

### SequentialCollection

An Array like object, with convenience methods and validations of boundaries, and
polymorphic with other collections in this library


```javascript
const {SequentialCollection} = require('o-toolbox')

const sequentialCollection = new SequentialCollection()
const sequentialCollection = SequentialCollection.with(item)
const sequentialCollection = SequentialCollection.withAll(items)

sequentialCollection.add(item)
sequentialCollection.addAll(items)
sequentialCollection.at({ index: 1 }) // raises an error if index 1 is out of bounds
sequentialCollection.at({ index: 1, ifAbsent: () => null }) // returns null if index 1 is out of bounds
sequentialCollection.countAllThat( (item, i) => item > 1)
sequentialCollection.clear()
sequentialCollection.filter( (item, i) => item > 1 )
sequentialCollection.forEach( (item, i) => {
  // ...
})
sequentialCollection.forEachInReverseOrder( (item, i) => {
  // ...
})
sequentialCollection.getFirst() // raises an error if empty
sequentialCollection.getFirstIndexThat({ block: (item, i) => item > 1 })
sequentialCollection.getFirstThat({ block: (item, i) => item > 1 })
sequentialCollection.getIndexOf(item)
sequentialCollection.getSize()
sequentialCollection.getLast() // raises an error if empty
sequentialCollection.isEmpty()
sequentialCollection.map( (item, i) => {
  return // ...
})
sequentialCollection.remove(item)
sequentialCollection.removeAll(items)
sequentialCollection.removeAllThat( (item, i) => item > 1 )
const first = sequentialCollection.removeFirst()
const item = sequentialCollection.removeItemAt({ index: 1 })
const last = sequentialCollection.removeLast()
sequentialCollection.sortUsing({ block: (item1, item2) => {
  if (item1 < item2) { return -1 }
  if (item1 > item2) { return 1 }
  return 0
})
sequentialCollection.toArray()
```

### SortedCollection

A SequentialCollection that ensures the order of its items

Its interface is the same as SequentialCollection, except for the creation of the collection, which takes a sort block

```javascript
const {SortedCollection} = require('o-toolbox')

const sortedCollection = new SortedCollection()

const sortedCollection = new SortedCollection({
  sortBlock: (item1, item2) => {
    if (item1 < item2) { return -1 }
    if (item1 > item2) { return 1 }
    return 0
  }
})

const sortedCollection = SequentialCollection.with({
  item: item,
  sortBlock: (item1, item2) => {
    if (item1 < item2) { return -1 }
    if (item1 > item2) { return 1 }
    return 0
  }
})

const sortedCollection = SequentialCollection.withAll({
  items: items,
  sortBlock: (item1, item2) => {
    if (item1 < item2) { return -1 }
    if (item1 > item2) { return 1 }
    return 0
  }
})
```

### Path

A path in the file system.

`Path`s are immutable values, meaning that no `Path` method changes its state. When a modification is done it returns a new `Path` object.


```javascript
// Path is not included in index.js for it depends on 'path' and 'fs' modules
const Path = require('o-toolbox/src/Path')

const path = new Path('/etc/logs/app.log')

path.exists()
path.isDirectory()
path.isFile()

// Returns true of the path string matches the given regex
path.matches(regex)

path.toString()

// subPath can be a String or a Path
path.append(subPath)
path.back()
path.back({ n: 2 })

path.getStats()
path.getFileExtension()
path.getFileExtension({dot: false})
path.getFilename()
path.getFilename({ extension: true })

path.getBasePath()
path.getSegments()
path.getFirstSegment()
path.getFirstSegments({ n: 2 })
path.getLastSegment()
path.getLastSegments({ n: 2 })
path.toAbsolutePath()


path.getFiles()
path.getFiles({ recursive: true })
path.getFiles({ matching: /[.]log^/ })
path.getFiles({ recursive: true, matching: /[.]log^/ })

// Returns the contents of the file as a String.
// Raises an error if the file does not exist
path.getFileContents()
// Returns the contents of the file as a JSON object.
// Raises an error if the file does not exist
// Raises an error if the file contents is not a valid JSON string
path.getJSONFileContents()

// Writes the given contents to the file
// Returns the path object.
// Creates the file if it does not exist
// Overrides the file if it exists
// Raises an error if the file directory does not exist
path.writeFileContents("File contents")
// Writes the given object as a JSON string to the file
// Returns the path object.
// Creates the file if it does not exist
// Overrides the file if it exists
// Raises an error if the file directory does not exist
path.writeJSONFileContents(object)

// Deletes the file
// Raises an error if the path does not exist
// Raises an error if the path is a directory
path.deleteFile()
// Deletes the file
// Does nothing if the path does not exist
// Raises an error if the path is a directory
path.ensureDeleteFile()

path.getDirectories()
path.getDirectories({ recursive: true })
path.getDirectories({ matching: /[.]log^/ })
path.getDirectories({ recursive: true, matching: /[.]log^/ })

// Creates all the directories in the Path
// If the directory exists does nothing
// If the directory partially exists creates the missing subdiretories
path.createDirectory()

path.files({
  do: (filePath) => { ... }
})
path.files({
  recursive: true,
  do: (filePath) => { ... }
})
path.files({
  recursive: true,
  matching: /[.]log^/,
  do: (filePath) => { ... }
})

path.directories({
  do: (dirPath) => { ... }
})
path.directories({
  recursive: true,
  do: (dirPath) => { ... }
})
path.directories({
  recursive: true,
  matching: /[.]log^/,
  do: (dirPath) => { ... }
})

path.filesAndDirectories({
  do: (dirPath) => { ... }
})
path.filesAndDirectories({
  recursive: true,
  do: (dirPath) => { ... }
})
path.filesAndDirectories({
  recursive: true,
  matching: /[.]log^/,
  do: (dirPath) => { ... }
})
```

### Environment

Class to conditionally evaluate blocks of code depending on the environemnt where the script is running

The Environment object receives the values in `process.env` or a copy with the variables of interest and provides methods for evaluating functions only in certain environments:


```javascript
const {Environment} = require('o-toolbox')
const environment = Environment({ env: process.env })

environment.isProduction()  // env.NODE_ENV = 'production'
environment.isDevelopment() // env.NODE_ENV = 'development'
environment.isTesting()     // env.NODE_ENV = 'testing'

environment.on({
  development: () => { ... },
  staging: () => { ... },
  production: () => { ... },
  default: () => { ... }
})

environment.on({
  development: () => app.use('/', debuggingRoutes)
})
```

### Errors

A hierarchy of generic errors

```javascript
RuntimeError
NotYetImplementedError
SubclassResponsibilityError
AbsentKeyError
AbsentMethodError
PathDoesNotExistError
UnexpectedTypeError
ParamsError
MissingRequiredParamError
UnexpectedParamError
```

Each of these errors can be raised with sync `Exception` or with an async `Promise rejection`

```javascript
const {RuntimeError} = require('o-toolbox')

RuntimeError.raise({ message: 'Error message here ...' })
RuntimeError.raise({ message: 'Error message here ...', async: true })

// which is the same as doing
new RuntimeError().raise({ message: 'Error message here ...' })
```

The following errors can be raised also with arguments using the method `.raiseOn(...)`

```javascript
const {
  SubclassResponsibilityError,
  NotYetImplementedError
  MissingRequiredParamError,
  UnexpectedParamError
} = require('o-toolbox')

SubclassResponsibilityError.raiseOn({ methodName: 'someMethod', className: 'SomeClass' })
NotYetImplementedError.raiseOn({ methodName: 'someMethod', className: 'SomeClass' })
MissingRequiredParamError.raiseIfMissing({ param: n, paramName: 'n' })
MissingRequiredParamError.raiseOn({ paramName: 'n' })
UnexpectedParamError.raiseOn({ paramName: 'n' })
```


### ImportMethods

ImportMethods utility is a simplified implementation of Mixins.

It lacks of inheritance with other Mixins applied to the same class and does not keep its own set of instance variables but in many circumstances it still stands as a good enough solution.

To import the methods of a Mixin class into a concrete class do

```javascript
// A mixin to add an .equals(other) method to a class
class ComparableValueBehaviour {
  equals (other) {
    return this.getValue() == other.getValue()
  }
}
```

```javascript
const {ImportMethods} = require('o-toolbox')
const {ComparableValueBehaviour} = require('./ComparableValueBehaviour')

// A class
class CustomValue {
  constructor(value) {
    this.value = value
  }

  getValue() {
    return this.value
  }
}
// The addition of the mixin into the class
ImportMethods.all({ from: ComparableValueBehaviour, into: CustomValue })

// Now CustomValue instances respond to .equals()
const value1 = new CustomValue(1)
const value2 = new CustomValue(2)
value1.equals(value2)
```

ImportMethods also supports partial imports with

```javascript
ImportMethods.all({
  from: ComparableValueBehaviour,
  into: CustomValue,
  except: ['someMethod', 'anotherMethod']
})

ImportMethods.methods({
  methodNames: ['someMethod', 'anotherMethod']
  from: ComparableValueBehaviour,
  into: CustomValue
})

ImportMethods.method({
  methodName: 'someMethod',
  from: ComparableValueBehaviour,
  into: CustomValue
})
```

If possible it's encouraged to encapsulate the use of ImportMethods in a static method in the mixin to simplify its usage and avoid an additional `require` statement


```javascript
const {ImportMethods} = require('o-toolbox')

// A mixin to add an .equals(other) method
class ComparableValueBehaviour {
  static attachTo(aClass) {
    ImportMethods.all({ from: this, into: aClass })
  }

  equals (other) {
    return this.getValue() == other.getValue()
  }
}
```

```javascript
const {ComparableValueBehaviour} = require('./ComparableValueBehaviour')

// A class
class CustomValue {
  constructor(value) {
    this.value = value
  }

  getValue() {
    return this.value
  }
}
ComparableValueBehaviour.attachTo(CustomValue)

// Now CustomValue instances respond to .equals()
const value1 = new CustomValue(1)
const value2 = new CustomValue(2)
value1.equals(value2)
```

### NoOverridesImportMethods

`NoOverridesImportMethods` behaves like `ImportMethods` but if a method is defined in the target class it does not override it.

If the mixin provides default implementation of methods overriden in the target classes you should use `NoOverridesImportMethods.all` or `ImportMethods.all({ except: })`.


### Protocol

`Protocol` utility validates that a class defines all the methods in a given protocol.

To use it define a protocol to validate

```javascript
const {Protocol} = require('o-toolbox')
class ComparableValueProtocol extends Protocol {
  equals(other) {}
  above(other) {}
  below(other) {}
}
```

and with it validate that a class implements all its methods

```javascript
ComparableValueProtocol.validate(SomeClass)
```

If the validation fails it raises an error.

If you prefer to ask for a boolean instead of raising an error use

```javascript
ComparableValueProtocol.isFulfilledBy(SomeClass)
```

Typically you would validate the class right after its definition

```javascript
const ComparableValueProtocol = require('./ComparableValueProtocol')

class SomeClass {
  // ...
}

ComparableValueProtocol.validate(SomeClass)
```

Classes usually have 2 protocols. An implementation protocol stating the methods a subclass must implement and a public protocol stating the methods a user of the class can call.

The recomendation is to define and validate each protocol independently with its own Protocol class.

### Random

Utility to generate random numbers

**Important**

This random number generator is not a good random generator and it's **not** suitable for statistical calculations, nor to use it in cryptographics

For not critical uses, like graphical animations and games, it's good enough

Generate a random number with any of the following methods:

```javascript
const { Random } = require('o-toolbox')

const randomFloat = Random.numberBetween( 1, 10 ) // 1 and 10 are included
const randomInteger = Random.integerBetween( 1, 10 ) // 1 and 10 are included
const randomItem = Random.pickFrom( [ 'a', 'b', 'c' ] )
```

## DoMe commands

DoMe commands are intended to be self-documented, please take a look at the files in `DoMe/forDevelopment/inWindows`, or in `DoMe/forDevelopment/inDocker`

---
_Source: https://npm.io/package/o-toolbox · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
