Code Monkey home page Code Monkey logo

nucleo's Introduction

Nucleo

Nucleo creates and manages a strongly typed and predictable state container.

Roadmap

It's in this project milestones https://github.com/mtmr0x/nucleo/milestones;

For requesting any feature, please open an issue with the prefix "[FEATURE REQUEST]".

Why

JavaScript is a really dynamic language which we can't always rely in the language resilience from types perspective. Every project has types problems and developers can not make sure all the time the data is predictable. Inspired by how GraphQL does a great job with safety and trustful data and Redux by its simplicity on storing state, Nucleo was idealized to make sure the data model (contracts) and the data types are expected and reliable.

Installation

Using NPM:

npm install nucleojs --save

Using Yarn:

yarn add nucleojs

Documentation

The links below take you to our API_DOCUMENTATION.md file present in this repository with deeper information and documentation to Nucleo usage.

Basic usage

Nucleo is written in TypeScript and compatible with es2016+. Importing for a ECMAScript usage:

import { createStore } from 'nucleojs';

Importing from Nucleo source for TypeScript usage just add /src after nucleojs module:

import { createStore } from 'nucleojs/src';

Defining a data model (contract):

import {
  NucleoString,
  NucleoNumber,
  NucleoObject,
  createStore
} from 'nucleojs'

const completeNameContract = new NucleoObject({
  name: 'completeNameContract',
  fields:  {
    firstName: NucleoString,
    lastName: NucleoString
  }
});

const userContract = new NucleoObject({
  name: 'user', // don't need to be the same name as the variable, but need to be unique
  fields: {
    name: completeNameContract,
    age: NucleoNumber
  }
});

const productsContract = new NucleoObject({
  name: 'products',
  fields: {
    title: NucleoString
  }
});

const contracts = {
  user: userContract,
  products: productsContract
};

Creating the store

import { createStore } from 'nucleojs';
import * as contracts from './contracts';

const store = createStore(contracts); // send contracts to create the store
const { dispatch, update, cloneState, subscribe } = store; // these 4 functions are returned from store creation

Dispatching and updating the store

Nucleo provides two methods of saving data, used for different approaches.

dispatch: works for saving data according to the full contract, used to save the very first contract state in the store or to update the whole contract in the store;

update: works for updating parts of data, it performs a index search in the object and save it. update will fail if you try to first save a contract to the store using it.


Dispatch function, considering user contract above:

let user = dispatch('user')({ name: { firstName: 'John', lastName: 'Nor' } });
// it'll fail because it's missing age field

user = dispatch('user')({ name: { firstName: 'John', lastName: 'Nor' }, age: 27 });
// it'll save the data to store properly

console.log(user);
/*
{
  status: 'OK',
  errors: [],
  data: {
    name: {
      firstName: 'John',
      lastName: 'Nor'
    },
    age: 27
  },
}
*/

Update function, considering user contract above:

const user = update('user')({ name: { firstName: 'Robert' }});
// it'll update only the user first name and only if this item has been already created in the store before

console.log(user);
/*
{
  status: 'OK',
  errors: [],
  data: {
    name: {
      firstName: 'Robert',
    },
  },
}

It'll return the data you asked to save. To receive the new store, you will have to clone this state or subscribe to Nucleo changes through subscribe function
Check documentation for cloneState in the next section
*/
const newUser = cloneState('user');
console.log(newUser);
/*
{
  name: {
    firstName: 'Robert',
    lastName: 'Nor',
  },
  age: 27,
}
*/

Update and Dispatch function signature

update and dispatch functions have the same signature albeit a discrete behavioral difference (you can find this difference in .

Both are curried functions:

  • update(<contract_name>)(<data_to_save_in_contract>);
  • dispatch(<contract_name>)(<data_to_save_in_contract>).

<contract_name>: a string for the contract name you want to update or dispatch. It's the name field for every new NucleoObject in the contracts definition. Those must be unique. You can find more information about contracts in API_DOCUMENTATION.md.

<data_to_save_in_contract>: must follow its contract model. For understanding how to use update and dispatch for saving data, check API_DOCUMENTATION.md in "Dispatching and updating the store" section.

Both return the same object interface:

{
  status: 'OK' | 'NOK', // a string return 'OK' for success cases and 'NOK' for errors
  errors: [], // in case of errors, it will return the list of errors
  data: { ... } // the data you just tried to save in store
}
  • status: a string return 'OK' for success cases and 'NOK' for errors;
  • errors: a list of objects containing the errors in this operation. Usually related to contract violations. You can find more details in API_DOCUMENTATION.md at "Error management" area.
  • data: This is the exactly same object you tried to save at store for comparison reasons in cases of errors.

Getting a state clone from store

The cloneState function receives one argument which is the contract name in store, performs a deep clone using the contracts data model as a map to predict the key/values of that contract and be able to return it with great performance.

const user = cloneState('user');
console.log(user);
/*
{
  name: {
    firstName: 'Robert',
    lastName: 'Nor'
  },
  age: 27
}
*/

Development

Tasks available

  • npm start - Start development mode.
  • npm run nodemon - Start development mode and waiting for changes.
  • npm run tests - Run automated tests.
  • npm run lint - Validate syntax of all Typescript files.
  • npm run compile - Compile for production.

Contributing

Want to contribute? Follow these recommendations.

Versioning

To keep better organization of releases we follow the Semantic Versioning 2.0.0 guidelines.

Licence

MIT Licence ยฉ Matheus Marsiglio

nucleo's People

Contributors

afonsopacifer avatar dependabot[bot] avatar mtmr0x avatar rafaellincoln avatar rogefm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

nucleo's Issues

Store and Store Tree creation

Description

It must create the storeTree and the store objects.

Tasks

  • storeTree architecture;
  • save storeTree in memory;
  • store architecture;
  • save store in memory
  • Have a consultable function for the store || or store only inside the dispatcher function scope?
  • Subscribe listener to the store events update

Questions

  • Do store start without its contracts if not provided initial state?

Create NucleoList class

We should be able to create lists of NucleoTypes and Lawyer function must validate it.

checklist

  • Create the instance file with it's values
  • Create the validation in lawyer for NucleoList instance
  • Create the validation in lawyer to each value type in provided NucleoList

Tests

  • Store creation with contract containing a NucleoList of NucleoString
  • Store creation with contract containing a NucleoList of NucleoObject
  • Dispatching data to it and validating wrong values
  • DIspatching data to it and saving updated values

Javascript error when you call the cloneState of a non-existent contract

When you call the cloneState method of a non-existent contract instead of receiving a undefined value, the javascript throw a TypeError as a invalid code.

So, as I said before, when you ask for a contract non-existent we need to receive something that does not stop the application, and that we can manipulate.

It is not returning the `newStore` in the dispatch or update method

Well, the problem consist in:

  1. I try to use the update method, something like -> const user = update('user')({ name: { firstName: 'Robert' }});
  2. I receive status "OK"
  3. Instead receive newStore inside data value, as showed on documentation example:
{
  status: 'OK',
  errors: [],
  data: {
    name: {
      firstName: 'Robert',
      lastName: 'Nor'
    },
    age: 27
  },
}

I receive the same data, that I used into update method as a parameter, in another words, I receive

{
  status: 'OK',
  errors: [],
  data: {
    name: {
      firstName: 'Robert',
    },
  }
}

Unsubscribe functionality

Having subscribed as listener, it should provide a way to unsubscribe that listener.

  • Callback function in subscribe
  • Performance tests for solution
  • Unsubscribe unit tests
  • Update documentation

PR: #33

Null and/or optional values in Nucleo objects

We discussed about how to treat possible optional fields and/or null fields and how Nucleo can work with that.

Right now Nucleo must receive data as expected, has no optional field (which is actually really coherent to its purpose) and does not accept null as value or as possibility.

Making it easy, we have to decide two main things here:

Should Nucleo have optional fields? Why? And if yes, how?

Should Nucleo have a way to treat or understand somehow null values? Why? And if yes, how?

Overall clean up in Lawyer

Lawyer is quite complex and tricky to understand, there is for sure a better way to make that code be better.

Avoid cyclic objects

I really don't know if it's a good idea or not, but for now I would say to avoid entering cyclic objects in store for avoiding n problems.

Primitive Types architecture

Task

Primitive types must be their architecture well defined for be able to storeTree assure the store contracts are aligned

Tasks

  • Define types names
  • Define object types architecture considering their name and functionality for comparison with the provided data from the dispatcher

Lawyer function

Tasks

  • documentation at Wiki
  • verification for non primitive or non Nucleo Object types
  • recursively call lawyer for Nucleo Object Types

Tests

  • Recursively call lawyer for NucleoObjectType fields
  • verify data types
  • verify fields not presented in contract

What about create an Enum type?

I've been thinking about create some immutable entries in some contract fields, what you guys think about create an Enum type that can be used by the contracts?

be able to change only one data level of data in store

Imagine the user want to save one specific field only and there is no need to re-save others. Could dispatch be able to identify it somehow and save a single data?

Or maybe a update function responsible for that?

considering the store value for userTest is:

{
  userTest: {
    name: { firstName: 'John', lastName: 'Smith' },
    age: 20
  }
}

If we run:

update('userTest')({ name: { firstName:  'Joe' } });

we get this result:

{
  userTest: {
    name: { firstName: 'Joe', lastName: 'Smith' },
    age: 20
  }
}

NucleoObjectType

Task

Every object in Nucleo must be a Nucleo Object Type.

Checklist

  • Create class to instance objects
  • Make store creation validate every object as NucleoObjectType
  • Lawyer must understand instances of NucleoObjectType

Can't update contract with `false` values.

When you call a update function, and pass false values like:

  • Empty strings, of string type
  • zero, of number type
  • false, of bool type

These values doesn't update the store, and return OK with the supposed new values.
When this situation happens, was verified in the indexSearch, on rec and save methods inside dataReflection that, the validation of these new values use a conditional OR in javascript used like || and this don't let pass those false values, as said before.

screen shot 2019-02-18 at 19 38 43

screen shot 2019-02-18 at 19 38 50

Contracts

Task

Contract is the Object Model for creating NucleoObjects. Creating the store, every object passed must be a valid contract created with NucleoObject;

Checklist

  • Contracts must be passed as Objects;
  • Contracts must have exclusive names. This must not be allowed to create 2 contracts with the same name;
  • Creating store, the contracts must be validated if it's instanceof NucleoObject;

Tests

  • You can't create two contracts with the same name per store
  • Every contract must be instance of NucleoObject
  • Creating the store and generating contracts, dispatch and subscribe must be returned

Improve NucleoList type tests

Task

Improve NucleoList tests, exploring possible flaws using update method

Checklist

flaws tests

  • try to update a list with primitive values
  • try to update a list with NucleoObject values
  • try to update values to a list not according to the list primitive type expected
  • try to update values to a list not according to its expected NucleoObject type

Make subscribe send alongside with contractName, the new data in that state

Task

When a listener be executed from inside Nucleo, increment in the object the new data being stored as copy.

Checklist

  • Make the object copy inside indexSearch;
  • Increment the subscriber arguments the data;
  • Send data to subscriber
  • Enhance documentation for subscribers and listeners, explaining the flow

Custom primitive types

As quoted in #9, Nucleo needs to give freedom to primitive/scalar types. This way Nucleo can receive contracts like this:

const ageRule = (value) => {
  return value < 100; // you defined here that age can not be higher than 100
}

const userType = new NucleoObject({
  name: 'user',
  fields: {
    name: NucleoString(),
    age: NucleoString(ageRule)
  }
});

The age field will be validated by the rule the user sent.

It will demand:

  • Refactor in Nucleo Pimitive type
    • These types must be a function
    • Inside these functions must validate if the received argument is a function
    • nucleo primitive type must store in its scope the validator to be ran
  • Lawyer must be refactored to validate the field rule alongside with its validations

Clone method - a immutable way to get references from store

The Nucleo memory usage in saving data and sending information to listeners makes it possible, the only thing that is necessary to discuss and understand is how to make it and still provide performance and simple code.

Today we have:

update -> validations -> index state search with state clone -> save

In index state search with state clone is possible store another copy that is easy to consult and can be send to listeners as new reference.

Custom primitive types for verifying a specific model of something

In discussion with my friend Hugo Bessa, we spoke about how could I make sure a ISO date would be always a ISO date and creating other types might not be a great idea but what about my primitive type be custom somehow?

Examples

Custom primitive number type

I know it's a number, but I can create rules for this number don't be greater than x or non-zero values

Custom primitive string type

It must be a string, but is a specific type of string, as a only numeric string or not having spaces, validated by a regex, for example.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.