brisky-base
Extendable object constructors, build for speed, low memory consumption and simplicity
- set method
- easy extendable property defintions
- deep, memory efficient prototypes
- types (simple inheritance)
- inject pattern
- traversal helpers
###Manipulation
set
Set method, set values or objects on a base object, always does a deep merge on objects
const base = require('brisky-base')
const foo = base({
a: {},
b: {}
})
foo.set({
a: {
c: true
}
})
Base objects allow fields to be an object and a primitive at the same time
const base = require('brisky-base')
const foo = base({
a: true,
b: true
})
foo.set({
a: {
c: true
}
})
remove
Remove a base object
const base = require('brisky-base')
const b = base({ foo: true, bar: true, })
b.foo.remove()
b.set({ bar: null })
b.set(null)
reset
Overwrite base, removes all properties that end up in .keys()
const base = require('brisky-base')
const foo = base({
a: true,
b: true,
properties: { c: true },
c: 'haha non-key property'
})
foo.set({ reset: true, x: true })
move
Move a property to another property
const base = require('brisky-base')
const foo = base({ a: 100, b: true, })
foo.move('a', 'b')
foo.move('b', 'c')
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.
const base = require('brisky-base')
const foo = base()
const someModule = require('someModule')
const otherModule = require('someModule')
foo.inject(someModule, otherModule)
foo.set({
inject: [ someModule, otherModule ]
})
define
Shortcut for Object.defineProperty
Wraps define-configurable
const base = require('brisky-base')
const foo = base({
key: 'base'
define: {
x: true,
bla (val) {}
}
})
foo.define({
extend: {
bla (method, val) {
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.
const base = require('brisky-base')
const foo = base()
foo.set({ bar: true })
foo.bar = true
foor.define({ bar: true })
###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
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)
console.log(instance.a.b.c.path())
console.log(base.a.b.c.path())
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()
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)
b.applyContext(context)
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
const base = require('brisky-base')
const obj = base({ a: { b: { c: 'c!' } } })
var c = obj.get('a.b.c')
c = obj.get(['a', 'b', 'c'])
default
const base = require('brisky-base')
const obj = base({ a: { b: { c: 'c!' } } })
const d = obj.get('a.b.d', {})
const c = obj.get('a.b.c', 'default!')
index
const base = require('brisky-base')
const obj = base({ a: { b: { c: 'c!' } } })
c = obj.get('[0][0][0]')
c = obj.get('[-1][-1][-1]')
c = obj.get('[2]')
###Keys
The .keys()
method allows fast object iteration, sorting and filtering of properties
basic
const base = require('brisky-base')
const obj = base({ foo: {}, bar: {} })
console.log(obj.keys())
keyType
const base = require('brisky-base')
const obj = base({
foo: { keyType: 'special' },
bar: true,
baz: true,
})
console.log(obj.keys())
console.log(obj.keys('special'))
keyFilter
Default filter used for keys, default of base requires the property to be a base object
const base = require('brisky-base')
const obj = base({
foo: true,
bar: true,
baz: true,
keyFilter: (key) => key[0] === 'b'
})
console.log(obj.keys())
sort
As a key
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())
obj.list.set({ sort: 'val' })
console.log(obj.list.keys())
Replace the internal method, follows default js sort (default field is val)
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())
obj.set({ sort: (a, b) => a > b ? 1 : a < b ? -1 : 0 })
console.log(obj.keys())
###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
map,
filter,
reduce,
forEach
push
Similar to array push, but generates a key based on current time, does not support multiple arguments
const base = require('brisky-base')
base.push('hello')
base.push('hello')
base.push('x', 111)
each
Loop trough values of a base object, differs slightly from forEach
const base = require('brisky-base')
const foo = base({
a: {},
b: {},
c: { keyType: 'hello' }
})
foo.each(p => {
console.log(p)
})
foo.each(p => {
}, 'hello')
Returning a truthy value in each will break the each loop and return the value
const base = require('brisky-base')
const foo = base({
a: {}, b: {}, c: {}
})
console.log(foo.each(p => p.key === 'b' ? 'hello' : false))
###Types
Use the types api to reduce complexity of dealing with classes, prototypes and components.
Especialy useful for composition when combined with inject
const base = require('brisky-base')
const articleModule = {
types: {
article: {
text: 'some text',
title: 'hello'
}
}
}
base({
inject: articleModule,
field: { type: 'article' },
bla: { type: 'article' },
nested: {
types: {
article: { text: 'lullz special article' },
},
something: { type: 'article' }
}
})
base({
types: {
myType: { field: 'hello' }
},
bla: { type: 'myType' },
hello: { type: 'base' }
})
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
const base = require('brisky-base')
const a = base(() => Date.now())
a.compute()
###References
Serializable references, this is handy for, for example dealing with server/client side or multiple processes.
basic
const base = require('brisky-base')
const a = base('a')
const b = base(a)
console.log(b.compute())
a.set('A!')
console.log(b.compute())
string notation
const base = require('brisky-base')
const obj = base({
a: '$root.b',
c: {
d: '$.parent.parent.a',
e: '$.parent.d'
}
})
obj.set({ b: 'its b!' })
console.log(a.c.e.compute())
no reference
In some cases you may want to escape the behaviour of creating references and just set a base object as a field
const base = require('brisky-base')
const a = base({ noReference: true })
const obj = base({ a })
###Serialize
Serialize base objects to normal objects, that you can set again
basic
const base = require('brisky-base')
const obj = base({
a: 'a!',
b: {
val: 'b!',
c: true
}
})
obj.set({ d: obj.a })
obj.serialize()
compute
Computes values instead of making them into set objects
const base = require('brisky-base')
const obj = base({
a: {
val: 'hello',
b: true
},
b: '$root.a'
})
obj.serialize(true)
filter
Filter certain fields that you, for example, don't want to send from a server to a client
const base = Base({
yuzi: {
james: {
marcus: true,
secret: true
}
}
})
base.serialize(false, (prop) => prop.key !== 'secret')
###Traversal
Base objects have properties and methods to make object traversal easier
parent
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.parent === obj.a)
root
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.root === obj)
key
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.key)
path
Generates a path array, works over context paths (virtual paths)
const base = require('brisky-base')
const obj = base({ a: { b: true } })
console.log(obj.a.b.path())
lookUp
Look up to find if a parent has certain properties
const base = require('brisky-base')
const obj = base({
a: { b: { c: true } },
b: true
})
obj.a.b.c.lookUp('b')
obj.b.lookUp([ 'a', 'b', 'c' ])
###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
const base = require('brisky-base')
const obj = base({
child: { text: 'hello' },
foo: {},
bar: {}
})
console.log(obj.foo.text.compute())
console.log(obj.bar.text.compute())
types
Mixit with types
const base = require('brisky-base')
const obj = base({
types: { myType: { text: 'hello' } },
child: { type: 'myType' },
foo: {},
bar: {}
})
console.log(obj.foo.text.compute())
console.log(obj.bar.text.compute())
constructor
Using a string Constructor
as a child value, means use myself for my children (creates deep inheritance)
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())
default behaviour
Using true
as a child value, results in using normal objects / primitives for child values
const base = require('brisky-base')
const obj = base({
child: true
x: 'hello'
})
console.log(obj.x)
###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
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',
special: 10,
base: 'a base'
})
foo.set({
properties: {
normal: null
}
})
set
const base = require('brisky-base')
const special = base({
type: 'special'
})
const obj = base({
properties: {
special: special
}
})
obj.set({
special: 10
})
obj.set({
properties: {
special: {
aField: true
}
}
})
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
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
something: { c: true },
hello: { field: true }
})
obj.set({
properties: {
define: {
something: {
reset: true,
val: 'hello'
},
x: {
key: 'z',
reset: false
},
hello: {
key: null
}
}
}
})
###Examples
Some fun use cases
Create an immutable class
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)