https://github.com/develephant/propsaware
Properties as dispatchers. A "living" global properties data store.
https://github.com/develephant/propsaware
data-store dispatcher events globals nodejs property-store property-watcher pub-sub
Last synced: about 1 year ago
JSON representation
Properties as dispatchers. A "living" global properties data store.
- Host: GitHub
- URL: https://github.com/develephant/propsaware
- Owner: develephant
- License: other
- Created: 2017-03-13T08:14:51.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2017-03-15T05:19:54.000Z (over 9 years ago)
- Last Synced: 2025-04-06T12:06:25.748Z (about 1 year ago)
- Topics: data-store, dispatcher, events, globals, nodejs, property-store, property-watcher, pub-sub
- Language: JavaScript
- Homepage:
- Size: 60.5 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# `PropsAware`
### Properties as dispatchers. A "living" global properties data store.

## Install
```
npm i @develephant/props-aware --save
```
## Usage
```js
const PropsAware = require('@develephant/props-aware')
```
# Overview
__PropsAware__ (or `PA`) is a global property object that emits events on property changes.
You can listen for these changes virtually anywhere in your program.
_Events are only emitted when the underlying property data changes._
In code it looks like this:
```js
//file-one.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/props-aware')
let props = PA.props()
PA.on('score', (val) => {
console.log(val)
})
props.score = 300
/* outputs 300 */
```
Lets create another JS file, and add the following:
```js
//file-two.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/props-aware')
props = PA.props()
props.score = 500 //will trigger an update
/* file-one.js will output 500 */
props.score = 500 //will not trigger an update, same data
```
# Discussion
Some things to consider before, or when using __PropsAware__:
- Only properties are managed; strings, numbers, booleans, arrays, and "data" objects.
- Properties are stored as a flat tree. Only root keys trigger update events. [1]
- When you set a `PA` property, its immediately available to all listeners.
- Callbacks are set on the `PropsAware` (`PA`) object directly, _not_ the property itself. [2]
- To change a property without emitting an update event, use `PA.set(p, v)`.
- Limit yourself to a handful of base `PA` properties, and pass primitives for flow control.
- There are no guarantees on delivery order or timing. it will get there though.
- When creating object instances with `PA` properties, each instance will have its own listener. [3]
- Dont set `PA` properties in an `onAll` handler. [4]
### Footnotes
#### 1) Only root keys trigger an update:
```js
//example PA props object
...
let props = {
score: 200
user: {
name: 'User',
color: 'green'
}
}
```
In the object above, when the `score` property is updated with a _new_ value, an event will be emitted.
The `user` key holds an object. When the key is set with an updated _object_, the `user` key will trigger:
```js
//only root keys emit
PA.on('user', (val) => {
console.log(val.color) //blue
})
props.user = { color: 'blue' }
```
Changing `user.color` directly will not trigger an event. Additionally, in the code above, the `name` key will be stripped away. This may be fine if thats whats intended.
An easy way to handle this, is pulling the object first:
```js
let obj = props.user //by ref
obj.color = 'yellow'
props.user = obj //will trigger
//yellow
```
Or, if you want to be super fancy...
```js
let obj = Object.assign({}, props.user) //cloned
obj.color = 'yellow'
props.user = obj //will trigger
```
> ___The above holds true for Arrays too.___
#### 2) Callbacks are set on the `PA` object, _not_ the property:
```js
...
//works!
PA.on('score', (val) => {
console.log(val)
})
//does NOT work
score.on((val) => {
console.log(val)
}
```
#### 3) Created instances of a Class will all emit:
```js
class MyClass {
constructor() {
this.props = PA.props()
PA.on('score', (val) => {
console.log(val)
})
}
}
const instance1 = new MyClass()
const instance2 = new MyClass()
const instance3 = new MyClass()
instance1.props.score = 100
/* instance1 outputs 100 */
/* instance2 outputs 100 */
/* instance3 outputs 100 */
```
#### 4) Dont set properties in an `onAll` handler:
```js
let props = PA.props()
PA.onAll((val, prop) => { //infinite loop
props[prop] = val //dont do it!
props.dontsetme = 'oops' //think of the puppies!!
})
```
If you _really, really_ need/want to set a `PA` property in an `onAll` handler you can either:
___Set it silently, without triggering an update___
```js
let props = PA.props()
PA.onAll((val, prop) => {
PA.set(prop, val) //does not emit
})
```
Or, you can use this workaround/hack:
```js
let props = PA.props()
PA.onAll((val, prop) => {
setTimeout(() => { props[prop] = val }, 1)
//will run until your computer starts smoking
})
```
But, at the end of the day, thats an infinite loop. Its not advisable.
# Keep it simple
Try to keep the amount of __PropsAware__ properties to a minimum. Pass strings, numbers, and booleans to handle messaging and state.
Consider the following:
```js
const PA = require('@develephant/props-aware')
let props = PA.props()
//this is okay...
props.walking = true
props.running = false
//but this is better!
props.pace = 'walking'
//OR
props.pace = 'running'
```
> _In most basic programs, you shouldnt need more than 4-5 `PA` properties._
# API
## `props() -> PropsAware_properties`
Retreives the __PropsAware__ property object. All properties on this object emit when set.
```js
const PA = require('@develephant/props-aware')
let props = PA.props()
```
## `on(prop, cb)`
__Callback receives:__
|Name|Purpose|
|----|-------|
|`val`|The property value|
|`prop`|The name of the property|
Listen for a property update.
```js
const PA = require('@develephant/props-aware')
PA.on('score', (val, prop) => {
console.log(val)
})
```
## `has(prop) -> success_bool`
Checks to see if a specific __root__ property is housed in the __PropsAware__ property table.
```js
const PA = require('@develephant/props-aware')
let has_username = PA.has('username')
```
## `del(prop) -> success_bool`
Removes a properties update listeners. This destroys __all__ update listeners for the property, except the global `onAll`.
```js
let success = PA.del('score')
```
## `onAll(cb)`
__Callback receives:__
|Name|Purpose|
|----|-------|
|`val`|The property value|
|`prop`|The name of the property|
Any part of the program can listen for __all__ `PA` property changes. When using this method, you need to filter the messages with control statements.
```js
PA.onAll((val, prop) => {
console.log('changed', prop, val)
})
```
_Property based flow control:_
```js
PA.onAll((val, prop) => {
if (prop === 'goback') {
console.log('go back')
} else if (prop === 'goforward') {
console.log('go forward')
}
})
//...somewhere else
/* PA Props reference */
let props = PA.props()
props.goback = true
//or
props.goforward = true
```
_State based flow control (using `on`):_
```js
PA.on('state', (val) => {
if (val === 'goback') {
console.log('go back')
} else if (val === 'goforward') {
console.log('go forward')
}
})
//...somewhere else
/* PA Props reference */
let props = PA.props()
props.state = 'goback'
//...or
props.state = 'goforward'
```
> _State based flow control is generally the better choice._
>
> With the pattern above, its entirely possible to manipulate the bulk of your program with one `PA` property!
## `onDel(cb)`
__Callback receives:__
|Name|Purpose|
|----|-------|
|`prop`|The name of the property|
Fired when the `del` method is used in the __PropsAware__ object
```js
PA.onDel((prop) => {
console.log('del', prop)
})
```
## `sync() --> success_bool`
Dispatch all properties, to all listeners. Should not be used often, especially with large property objects.
> _Note:_ Any `onAll` listeners will be called with __all__ emitted properties.
```js
const PA = require('@develephant/props-aware')
//Set a property "silently"
PA.set('prop', 'val')
//Nevermind, resend everything
PA.sync()
```
# Examples
### In Classes
```js
//ClassA.js
const PA = require('@develephant/props-aware')
class ClassA {
constructor() {
this.props = PA.props()
PA.on('score', (val) => {
console.log(val)
})
}
}
module.exports = ClassA
```
```js
//ClassB.js
const PA = require('@develephant/props-aware')
class ClassB {
constructor() {
this.props = PA.props()
this.props.score = 100
}
}
module.exports = ClassB
```
__Run__
```js
//main.js
const ClassA = require('./ClassA')
const ClassB = require('./ClassB')
const A = new ClassA()
const B = new ClassB() /* Class A will output "100" */
```
### In Objects
```js
//obj-one.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/parse-aware')
const ObjA = {
props: PA.props(),
listen() {
PA.on('greeting', (val) => {
console.log(greeting)
})
}
}
module.exports = ObjA
```
```js
//obj-two.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/props-aware')
const ObjB = {
props: PA.props()
}
module.exports = ObjB
```
__Run__
```js
//main.js
const objA = require('./obj-one')
const objB = require('./obj-two')
objA.listen()
objA.props.greeting = 'Hola' /* ObjA will output "Hola" */
//Props object is "global" so this works via objB:
objB.props.greeting = 'Hello' /* ObjA will output "Hello" */
```
### A Function
```js
const PA = require('@develephant/props-aware')
modules.export = function() {
this.props = PA.props()
this.props.greeting = 'Howdy'
PA.on('score', (val) => {
console.log(val)
})
}
// Assuming the Objects above...
/* ObjA will output 'Howdy' */
// Call score through Class B above
classB.props.score = 1000
/* This Function will output 1000 *\
/* ObjA will output 1000 */
```
# Next Steps
- Immutability
^_^
---
#### `PropsAware` ⋆ © 2017 develephant ⋆ Apache-2.0 license