@brainbytes/immutable-store
ES6 Flux-based immutable data store with Immutable.js
Store
import Store, {MessageTypes} from 'immutable-store'
import schema from './my-schema.json'
// Store **requires** a schema to be constructed
let myStore = new Store(schema);
myStore.handleMessage({
type: MessageTypes.Write,
payload: {
table: 'user',
row: {
id: '2361',
username: 'someguy37'
}
},
promise: myApi.updateUser()
});
Methods
Currently the store has a single method, handleMessage, which is used to update the store's internal state.
handleMessage
handleMessage(message)
handleMessage accepts a single parameter, message which contains a type, and a payload. Each message type has it's own format for the payload
Message Types
When creating a message, it should have a type
field which is exported on the
MessageType
object. The value of the type
field determines the expected
shape of the message.payload
MessageType.Write
Writing implies an 'additive' operation, including but not limitied to creating and updating records.
{
table: 'someTable',
row: {
id: 'someId'
field1: 'value1',
field2: 'value2'
}
}
table
required
the name of the collection which you want to perform the operation on
row
required
the actual data you want to write. id
is required on the row, and all other
fields are optional. Any fields omitted in the row
object which are on the
referenced record will remain unchanged, and all other fields in the record will
be set to their corresponding value on row
.
rows
optional
rows can be provided instead of row, as an array of row objects. Any invariants
that hold for row or operations involving row should also hold for every object
in the rows
p
MessageType.Replace
Replace operations work mostly the same as write operations, but instead of
being 'additive', they will fully replace the record being written.
The message format is exactly the same, the exception of the semantics of row
and rows
being that any fields not in row
will be removed from the record
during the update.
Any records not in the rows
array will be unmodified. If you wish to replace
the contents of an entire collection, see MessageType.ReplaceAll
MessageType.Delete
Delete operations are for removing one or more records from a collection. To delete a single record the syntax is as follows:
{
table: 'someTable',
row: 'someId'
}
For deleting multiple records, you can use rows
instead, and provide an array
of ids to delete.
{
table: 'someTable',
rows: ['id1', 'id2', 'id3']
}
Lastly, you can omit row
and rows
to delete the entire collection
{
table: 'someTable'
}
MessageType.ReplaceAll
Replace all as an operation works similar to Replace
but applied to the level
of the collection.
{
table: 'someTable',
rows: [{
id: 'id1'
field1: 'value1',
field2: 'value2'
}, {
id: 'id2',
field1: 'otherValue'
}],
promise: myApi.writeLots()
}
The collection someTable
will now contain exactly two records, regardless of
how many it had before the operation.
Unlike Replace
, the row
field cannot be supplied.
MessageType.DeleteAll
This message type does not require a payload, because it simply deletes everything in the store. This is useful for clearing the store when a user logs out or otherwise wants to 'forget' all data.
Store instance properties
The store instance has a few read-only properties once it is constructed.
data
This is the current state of all the collections represented as an immutable collection.
messages
This is a total history of all the messages which have been passed to the store
since it was constructed. messages
is an immutable list.
schema
This is an immutable representation of the schema passed into the store that it uses to create/maintain the store's representation of data.
Message.Promise
Optimistic by default
immutable-store is capable of handling all operations optimistically, simply by
including a promise
field an operation will be committed or rolled back when
the promise is resolved or rejected.
What this means in practice is that if you perform some effect-ful operation on the server via a web request, and if you have a promise representing that action you can send that along with the message to immutable-store, and it will watch the promise for resolution.
Semantics
Internally, immutable-store keeps an ordered list of all messages it receives,
and any message without a promise is considered 'final' by default. The most
recent 'final' message not preceeded by some pending operation is the HEAD
message. The HEAD
message is stored, alongside the state the store was in
after the HEAD
message has been applied.
Any message with a promise that is resolved will be marked as final.
Any time a message is marked as final, HEAD
will be recomputed.
Any message with a promise that is rejected will be removed from the queue and placed in a special 'failed' queue, which retains the ordering from the message queue.
Any time a promise is rejected, after the message is removed from the queue, the
state of the store is then reset to the HEAD
and each message after HEAD
is
re-applied to the store's state, until all the messages have been applied.
install
npm install @brainbytes/immutable-store
Testing
In order for Karma to test on all browsers make sure Google Chrome and Firefox are both installed. (IE and Safari will be loaded depending on your platform)
If Karma is unable to find one of the browsers it is trying to launch, it will
not run the tests. If you cannot install the necessary browsers, edit the
karma.config.js
file, and remove the appropriate entry from browsers
.
First, make sure Karma is installed
npm install -g karma-cli
Install dependencies
npm install
Finally,
npm test
license
MIT