Extendable object constructors, build for speed, low memory consumption and simplicity
npm install brisky-base



---
###Manipulation
set
Set method, set values or objects on a base object, always does a deep merge on objects
``javascript
const base = require('brisky-base')
const foo = base({
a: {},
b: {}
}) // → Base { a: {}, b: {} }
foo.set({
a: {
c: true
}
}) // → Base { a: { c: true }, b: {} }
`
Base objects allow fields to be an object and a primitive at the same time
`javascript
const base = require('brisky-base')
const foo = base({
a: true,
b: true // internaly primitives are stored on the field .val
}) // → Base { a: true, b: true }
foo.set({
a: {
c: true
}
}) // → Base { a: { val: true, c: true }, b: true }
`
remove
Remove a base object
`javascript`
const base = require('brisky-base')
const b = base({ foo: true, bar: true, })
b.foo.remove() // removes foo and all nested properties
b.set({ bar: null }) // same as b.bar.remove()
b.set(null) // removes b and all nested properties
reset
Overwrite base, removes all properties that end up in .keys()
`javascript
const base = require('brisky-base')
const foo = base({
a: true,
b: true,
properties: { c: true },
c: 'haha non-key property' // c is not a part of .keys()
})
foo.set({ reset: true, x: true }) // removes a and b, but not c, adds x
// reset can also be used as a method foo.reset()
`
move
Move a property to another property
`javascript`
const base = require('brisky-base')
const foo = base({ a: 100, b: true, }) // → Base { a: 100, b: true }
foo.move('a', 'b') // → Base { b: 100 }
foo.move('b', 'c') // → Base { c: 100 }
inject
Like a set but only called once, used for composition of modules and behaviour.
When injecting an module or object (injectable) several times, it will only execute the first time. This is useful when working in a highly modularized structure where multiple modules might inject the same injectable.
`javascript
const base = require('brisky-base')
const foo = base()
const someModule = require('someModule')
// somemodule is eg. { a: 'hello' }
const otherModule = require('someModule')
// otherModule is eg. { b: 'bye' }
foo.inject(someModule, otherModule) // calls a set for both modules → Base { a: 'hello', b: 'bye' }
foo.set({
inject: [ someModule, otherModule ] // set notation
})
`
define
Shortcut for Object.defineProperty
Wraps define-configurable
`javascript
const base = require('brisky-base')
const foo = base({
key: 'base'
define: {
x: true, // defines { value: true }
bla (val) {} // defines a method "bla"
}
})
foo.define({
extend: {
bla (method, val) { // extends "bla" method
return method.call(this, val)
}
}
})
`
To conclude .set() is used to hook into non-default object behvaiour added by base, define() creates non-eunumerable, configurable objects. Non-enumerable means, these properties won't appear in keys list, on iterations or serializations.
`javascript`
const base = require('brisky-base')
const foo = base()
foo.set({ bar: true }) // → Base "bar"
foo.bar = true // → true (normal object)
foor.define({ bar: true }) // → non enumerable, configurable property descriptor
---
###Context
Context enables deep memory efficient prototypes.
Stores information on fields about first non-shared ancestors.
The context syntax can be used to create mem efficient immutables for example
basic
Notice that base.a.b.c === instance.a.b.c is true but the paths are different
`javascript
const base = require('brisky-base')
const obj = base({
key: 'base'
a: { b: { c: 'its c' } }
})
const instance = new obj.Constructor({
key: 'instance'
})
console.log(base.a.b.c === instance.a.b.c) // → true
console.log(instance.a.b.c.path()) // → [ 'instance', 'a', 'b', 'c' ]
console.log(base.a.b.c.path()) // → [ 'base', 'a', 'b', 'c' ]
`
store and apply context
Allows storage and restoration of context.
Useful for edge cases where you need to make a handle to a nested field in a certain context
Consists of 2 methods
- applyContext(context)storeContext()
-
`javascript
const base = require('brisky-base')
const obj = base({
key: 'base'
a: { b: { c: 'its c' } }
})
const instance = new obj.Constructor({
key: 'instance'
})
const b = instance.a.b
const context = b.storeContext()
console.log(obj.a.b.c) // this will remove the context "instance", and replace it with base
b.applyContext(context) // will reset the context of b to instance
`
Apply context can return 3 different types
- undefined Context is restored without any differencesBase
- A set has happened in the path leading to the target of apply contextnull
- A remove has happened in the path leading to the target of apply context
---
###Get
Simple get api, useful when dealing with defaults
basic
`javascript`
const base = require('brisky-base')
const obj = base({ a: { b: { c: 'c!' } } })
var c = obj.get('a.b.c') // get c
c = obj.get(['a', 'b', 'c']) // also gets c
default
`javascript`
const base = require('brisky-base')
const obj = base({ a: { b: { c: 'c!' } } })
const d = obj.get('a.b.d', {}) // creates new property d with as a value an empty object
const c = obj.get('a.b.c', 'default!') // gets a.b.c, does not set it to "default!""
index
`javascript`
const base = require('brisky-base')
const obj = base({ a: { b: { c: 'c!' } } })
c = obj.get('[0][0][0]') // also get c (gets first key of every object)
c = obj.get('[-1][-1][-1]') // also get c (gets last key of every object)
c = obj.get('[2]') // returns undefined (tries to get the 3rd key)
---
###Keys
The .keys() method allows fast object iteration, sorting and filtering of properties
basic
`javascript`
const base = require('brisky-base')
const obj = base({ foo: {}, bar: {} })
console.log(obj.keys()) // → [ 'foo', 'bar' ]
keyType
`javascript`
const base = require('brisky-base')
const obj = base({
foo: { keyType: 'special' }, // by setting keyType it will not show up in .keys()
bar: true,
baz: true,
})
console.log(obj.keys()) // → [ 'bar', 'baz' ] other keyTypes get filtered out
console.log(obj.keys('special')) // → [ 'foo' ]
keyFilter
Default filter used for keys, default of base requires the property to be a base object
`javascript`
const base = require('brisky-base')
const obj = base({
foo: true,
bar: true,
baz: true,
keyFilter: (key) => key[0] === 'b'
})
console.log(obj.keys()) // → [ 'bar', 'baz' ]
sort
As a key
`javascript
const base = require('brisky-base')
const obj = base({
sort: 'name',
foo: { name: 'foo' },
bar: { name: 'bar' },
list: [ 5, 2, 1, 3, 4 ]
})
console.log(obj.keys()) // → [ 'bar', 'foo' ]
obj.list.set({ sort: 'val' }) // corresponds to the primitive value (val)
// val is the default field used in sort
console.log(obj.list.keys()) // → [ 2, 1, 3, 0, 4 ]
`
Replace the internal method, follows default js sort (default field is val)
`javascript
const base = require('brisky-base')
const obj = base({
sort: {
exec: (a, b) => a < b ? 1 : a > b ? -1 : 0,
val: 'val'
},
foo: 1,
bar: 2
})
console.log(obj.keys()) // → [ 'bar', 'foo' ]
obj.set({ sort: (a, b) => a > b ? 1 : a < b ? -1 : 0 })
// reverse sort order
console.log(obj.keys()) // → [ 'foo', 'bar' ]
`
---
###Iteration
Base exposes as much standard apis for iteration as possible, .each is the exception
array
Like standard array methods, but iterating over base properties
push
Similar to array push, but generates a key based on current time, does not support multiple arguments
`javascriptDate.now()
const base = require('brisky-base')
base.push('hello') // creates a key based on → { 21321232323: 'hello' }`
base.push('hello') // pushing at the same time adds a decimal → { '21321232323.1': 'hello' }
base.push('x', 111) // second argument is stamp, does not support multiple values
each
Loop trough values of a base object, differs slightly from forEach
`javascript
const base = require('brisky-base')
const foo = base({
a: {},
b: {},
c: { keyType: 'hello' }
})
foo.each(p => {
console.log(p) // iterates over each key
// returning a value in each will break the each loop and return the value
})
foo.each(p => {
// iterates over keyType 'hello'
}, 'hello')
`
Returning a truthy value in each will break the each loop and return the value
`javascript`
const base = require('brisky-base')
const foo = base({
a: {}, b: {}, c: {}
})
console.log(foo.each(p => p.key === 'b' ? 'hello' : false)) // → "hello"
---
###Types
Use the types api to reduce complexity of dealing with classes, prototypes and components.
Especialy useful for composition when combined with inject
`javascript
const base = require('brisky-base')
const articleModule = {
types: {
article: {
text: 'some text',
title: 'hello'
}
}
}
base({
inject: articleModule,
field: { type: 'article' }, // will make field into an instance of article,
bla: { type: 'article' }, // will make bla into an instance of article
nested: {
types: {
article: { text: 'lullz special article' },
},
something: { type: 'article' } // will get the fist "types.article" it can find in a parent
}
})
base({
types: {
// types is just an object
myType: { field: 'hello' }
},
bla: { type: 'myType' },
hello: { type: 'base' } // base is a default type
})
`
Big advantage of this system is that it allows you to change types not as dependents but as extensions, bit similar to for example web components.
-
###Compute
Compute is used to return the computed value of a base object - this allows for special hooks and, for example function support
basic
`javascript`
const base = require('brisky-base')
const a = base(() => Date.now())
a.compute() // → "return the current date"
---
###References
Serializable references, this is handy for, for example dealing with server/client side or multiple processes.
basic
`javascript`
const base = require('brisky-base')
const a = base('a')
const b = base(a)
console.log(b.compute()) // → "a"
a.set('A!')
console.log(b.compute()) // → "A!"
string notation
`javascript`
const base = require('brisky-base')
const obj = base({
a: '$root.b', // creates b on the root when its not there
c: {
d: '$.parent.parent.a', // gets a
e: '$.parent.d' // gets the parent object "d"
}
})
obj.set({ b: 'its b!' })
console.log(a.c.e.compute()) // → returns "its b!"
no reference
In some cases you may want to escape the behaviour of creating references and just set a base object as a field
`javascript`
const base = require('brisky-base')
const a = base({ noReference: true })
const obj = base({ a }) // obj.a === a (no reference)
---
###Serialize
Serialize base objects to normal objects, that you can set again
basic
`javascript`
const base = require('brisky-base')
const obj = base({
a: 'a!',
b: {
val: 'b!',
c: true
}
})
obj.set({ d: obj.a })
obj.serialize() // → { a: 'a!', b: { val: 'b!', c: true }, d: '$root.a' }
compute
Computes values instead of making them into set objects
`javascript`
const base = require('brisky-base')
const obj = base({
a: {
val: 'hello',
b: true
},
b: '$root.a'
})
obj.serialize(true) // → { a: 'hello', b: 'hello' }
filter
Filter certain fields that you, for example, don't want to send from a server to a client
`javascript`
const base = Base({
yuzi: {
james: {
marcus: true,
secret: true
}
}
})
base.serialize(false, (prop) => prop.key !== 'secret') // → { yuzi: { james: { marcus: true } } },
---
###Traversal
Base objects have properties and methods to make object traversal easier
parent
`javascript`
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.parent === obj.a) // → true
root
`javascript`
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.root === obj) // → true
key
`javascript`
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.key) // → "b"
path
Generates a path array, works over context paths (virtual paths)
`javascript`
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.path()) // → [ "a", "b" ]
lookUp
Look up to find if a parent has certain properties
`javascript`
const base = require('brisky-base')
const obj = base({
a: { b: { c: true } },
b: true
})
obj.a.b.c.lookUp('b') // → returns "obj.b"
obj.b.lookUp([ 'a', 'b', 'c' ]) // → returns "obj"
---
###Child
The child property is used to set default types for properties of a Base
The default child of a base object is a base
object
`javascript
const base = require('brisky-base')
const obj = base({
child: { text: 'hello' },
foo: {},
bar: {}
})
console.log(obj.foo.text.compute()) // → "hello"
console.log(obj.bar.text.compute()) // → "hello"
// both fields are instances of the base.child
`
types
Mixit with types
`javascript
const base = require('brisky-base')
const obj = base({
types: { myType: { text: 'hello' } },
child: { type: 'myType' },
foo: {},
bar: {}
})
console.log(obj.foo.text.compute()) // → "hello"
console.log(obj.bar.text.compute()) // → "hello"
// both fields are instances of the base.child
`
constructor
Using a string Constructor as a child value, means use myself for my children (creates deep inheritance)
`javascript
const base = require('brisky-base')
const obj = base({
child: {
text: 'hello',
child: 'Constructor'
},
x: { y: { z: true } }
})
console.log(obj.x.y.z.text.compute()) // → "hello"
// all fields (deep and shallow) inherit text: 'hello'
`
default behaviour
Using true as a child value, results in using normal objects / primitives for child values
`javascript
const base = require('brisky-base')
const obj = base({
child: true
x: 'hello'
})
console.log(obj.x) // → "hello"
// all properties of obj that you set will become "normal" object properties
`
---
###Properties
The properties field is used to add property definitions for certain keys within set objects.
There are 4 types of property definitions:
- true clears any special base behaviour for the keyfunction
- calls the function when the key is set instead of the normal behaviournull
- removes property definition and any existing instancesanything else
- uses the set function
basic
`javascript
const base = require('brisky-base')
const foo = base({
properties: {
normal: true,
special (val, stamp) {
this.special = val * 10
},
base: { nested: true }
}
})
foo.set({
normal: 'hello', // → 'hello'
special: 10, // → 100
base: 'a base' // → Base { val: 'a base', nested: true }
})
foo.set({
properties: {
normal: null
// removes property defintion and removes "normal"
}
})
`
set
`javascript
const base = require('brisky-base')
const special = base({
type: 'special'
})
const obj = base({
properties: {
// uses "noReference" for a base
special: special
}
})
obj.set({
special: 10 // → Special 10
})
// add something to the "special" property
obj.set({
properties: {
special: {
aField: true
}
}
})
// → base.special.aField.val === true, inherits from the property
`
define
Allows for extra customisation of property definitions.
It has 3 options:
- :key when a property is set uses this key to store its value:reset
- resets a previously defined property:val
- property value
`javascript
const base = require('brisky-base')
const obj = base({
properties: {
define: {
x: { key: 'y' },
something: {
key: 'else',
val: {
a: true,
b: true
}
},
hello: {
key: 'bye',
val: 100
}
}
},
x: 10 // → y: 10
something: { c: true }, // → else: Base { a: true, b: true, c: true }
hello: { field: true } // → bye: Base { val: 100, field: true }
})
obj.set({
properties: {
define: {
something: {
// removes the "else" field on base
// creates a new property definition for "something"
reset: true,
val: 'hello'
},
x: {
key: 'z',
reset: false // will not move "y"
},
hello: {
// moves "bye" → "hello"
key: null
}
}
}
})
`
---
###Examples
Some fun use cases
Create an immutable class
`javascript
const Immutable = base({
define: {
change (val, stamp) { return new this.Constructor(val, stamp) }
},
child: 'Constructor'
}).Constructor
const state1 = new Immutable(10)
const state2 = state1.change(20)
const state3 = state3.change(30)
``