<commit_before>[![Build Status](https://travis-ci.org/YooShibu/soboku.svg?branch=master)](https://travis-ci.org/YooShibu/soboku)
[![codecov](https://codecov.io/gh/YooShibu/soboku/branch/master/graph/badge.svg)](https://codecov.io/gh/YooShibu/soboku)
[![gzip size](http://img.badgesize.io/https://unpkg.com/soboku/dist/soboku.min.js?compression=gzip)](https://unpkg.com/soboku/dist/soboku.min.js)


# soboku

A tiny javascript package for reactive programming. Reduce global mutable flags and if-else statement from your code.

## How to use
Node.js

    npm install soboku

Browser
~~~ html
<script src="https://unpkg.com/soboku/dist/soboku.min.js"></script>
<script>
    // soboku in global
    const { state } = soboku;
</script>
~~~
## Example

A countdown timer

~~~ typescript
import { state, gate, dependency, trigger, interval } from "soboku"


// -----------------------------------------
// Prepere countdown timer
// -----------------------------------------

function isZero(x: number): boolean {
    return x === 0;
}

function isGreaterThan0(x: number): boolean {
    return x > 0;
}

function getTimerMessage(isRunning: boolean) {
    return isRunning ? "Start!" : "Done!";
}

const _count = state(3),
      decCount = () => _count.next(_count.s() - 1),
      isEnd = trigger(isZero, _count),
      isCountGreaterThan0 = trigger(isGreaterThan0, _count),
      count = gate(isCountGreaterThan0, _count),
      timer = interval(1000),
      timerMessage = dependency(getTimerMessage, timer.input);
      
count.report(console.log);
timerMessage.report(console.log);
timer.output.report(decCount);
isEnd.report(end => timer.input.next(!end));


// -----------------------------------------
// Start countdown timer
// -----------------------------------------

timer.input.next(true);

// Start!
// 2
// 1
// Done!
~~~


## API

### Functions

#### `reporter<T>(): Reporter<T>`

~~~ typescript
import { reporter } from "soboku"

const message = reporter<string>();
message.report(console.log);
message.next("Hello soboku");

// console
// Hello soboku
~~~

#### `state<T>(initial: T): State<T>`

~~~ typescript
import { state } from "soboku"

const count = state(0);
console.log(count.s()); // 0

count.report(console.log);
count.next(100);
// console
// 100

console.log(count.s()) // 100
~~~

#### `sarray<T>(initial?: T[]): ISArray<T>`

~~~ typescript
import { sarray } from "soboku"

const nums = sarray([1, 2, 3]);
console.log(nums.s()) // [1, 2, 3]

nums.report(console.log);

nums.push(4);
// console
// [1, 2, 3, 4]
~~~


#### `dependency<T>(func: (...atoms: Atom<any>[]) => T, atoms: Atom<any>[], thisArg?: any): Calc<T>`

~~~ typescript
import { state, dependency } from "soboku"

function twice(num: number): number {
    return num * 2;
}

function add(num1: number, num2: number): number {
    return num1 + num2;
}

const x = state(10);
const y = dependency(twice, [x]);
const z = dependency(add, [x, y]);
console.log(z.s()) // 30

z.report(console.log);

x.next(52);
// console
// 156
~~~

#### `combine<T>(sobokuObj: { [K in keyof T]: Atom<T[K]> }): Calc<T>`

~~~ typescript
import { state, combine } from "soboku"

const x = state(10);
const y = state(2);
const point = combine({ x, y, z: 20 });
console.log(point.s()) // { x: 10, y: 2, z: 20 }

point.report(console.log);

y.next(100);
// console
// { x: 10, y: 100, z: 20 }

~~~

#### `publisher(permition: IReporter<boolean>, reporter: Calc<T>): Calc<T>`

~~~ typescript
import { state, publisher } from "soboku"

const complete = state(false);
const source = state(10);
const num = publisher(complete, source);
console.log(num.s()) // 10

num.report(console.log);

source.next(100);
source.next(20);
complete.next(true); // permition turns true then report num
// 20
source.next(2);
// 2
~~~

#### `trigger(predicate: (...atoms: Atom<any>[]), atoms<any>[], thisArg?: any): Calc<boolean>`

~~~ typescript
import { state, trigger } from "soboku"

function isGreaterThan10(x: number): boolean {
    return x > 10;
}

const count = state(0);
const isDone = trigger(isGreaterThan10, [count]);
console.log(isDone.s()) // false

isDone.report(console.log);

count.next(2);
count.next(8);
count.next(15);
// console
// true<commit_msg>fix function arguments<commit_after>[![Build Status](https://travis-ci.org/YooShibu/soboku.svg?branch=master)](https://travis-ci.org/YooShibu/soboku)
[![codecov](https://codecov.io/gh/YooShibu/soboku/branch/master/graph/badge.svg)](https://codecov.io/gh/YooShibu/soboku)
[![gzip size](http://img.badgesize.io/https://unpkg.com/soboku/dist/soboku.min.js?compression=gzip)](https://unpkg.com/soboku/dist/soboku.min.js)


# soboku

A tiny javascript package for reactive programming. Reduce global mutable flags and if-else statement from your code.

## How to use
Node.js

    npm install soboku

Browser
~~~ html
<script src="https://unpkg.com/soboku/dist/soboku.min.js"></script>
<script>
    // soboku in global
    const { state } = soboku;
</script>
~~~
## Example

A countdown timer

~~~ typescript
import { state, gate, dependency, trigger, interval } from "soboku"


// -----------------------------------------
// Prepere countdown timer
// -----------------------------------------

function isZero(x: number): boolean {
    return x === 0;
}

function isGreaterThan0(x: number): boolean {
    return x > 0;
}

function getTimerMessage(isRunning: boolean) {
    return isRunning ? "Start!" : "Done!";
}

const _count = state(3),
      decCount = () => _count.next(_count.s() - 1),
      isEnd = trigger(isZero, _count),
      isCountGreaterThan0 = trigger(isGreaterThan0, _count),
      count = gate(isCountGreaterThan0, _count),
      timer = interval(1000),
      timerMessage = dependency(getTimerMessage, timer.input);
      
count.report(console.log);
timerMessage.report(console.log);
timer.output.report(decCount);
isEnd.report(end => timer.input.next(!end));


// -----------------------------------------
// Start countdown timer
// -----------------------------------------

timer.input.next(true);

// Start!
// 2
// 1
// Done!
~~~


## API

### Functions

#### `reporter<T>(): Reporter<T>`

~~~ typescript
import { reporter } from "soboku"

const message = reporter<string>();
message.report(console.log);
message.next("Hello soboku");

// console
// Hello soboku
~~~

#### `state<T>(initial: T): State<T>`

~~~ typescript
import { state } from "soboku"

const count = state(0);
console.log(count.s()); // 0

count.report(console.log);
count.next(100);
// console
// 100

console.log(count.s()) // 100
~~~

#### `sarray<T>(initial?: T[]): ISArray<T>`

~~~ typescript
import { sarray } from "soboku"

const nums = sarray([1, 2, 3]);
console.log(nums.s()) // [1, 2, 3]

nums.report(console.log);

nums.push(4);
// console
// [1, 2, 3, 4]
~~~


#### `dependency<T>(func: (...atoms: Atom<any>[]) => T, ...atoms: Atom<any>[]): Calc<T>`

~~~ typescript
import { state, dependency } from "soboku"

function twice(num: number): number {
    return num * 2;
}

function add(num1: number, num2: number): number {
    return num1 + num2;
}

const x = state(10);
const y = dependency(twice, x);
const z = dependency(add, x, y);
console.log(z.s()) // 30

z.report(console.log);

x.next(52);
// console
// 156
~~~

#### `combine<T>(sobokuObj: { [K in keyof T]: Atom<T[K]> }): Calc<T>`

~~~ typescript
import { state, combine } from "soboku"

const x = state(10);
const y = state(2);
const point = combine({ x, y, z: 20 });
console.log(point.s()) // { x: 10, y: 2, z: 20 }

point.report(console.log);

y.next(100);
// console
// { x: 10, y: 100, z: 20 }

~~~

#### `publisher(permition: IReporter<boolean>, reporter: Calc<T>): Calc<T>`

~~~ typescript
import { state, publisher } from "soboku"

const complete = state(false);
const source = state(10);
const num = publisher(complete, source);
console.log(num.s()) // 10

num.report(console.log);

source.next(100);
source.next(20);
complete.next(true); // permition turns true then report num
// 20
source.next(2);
// 2
~~~

#### `trigger(predicate: (...atoms: Atom<any>[]), ...atoms<any>[]): Calc<boolean>`

~~~ typescript
import { state, trigger } from "soboku"

function isGreaterThan10(x: number): boolean {
    return x > 10;
}

const count = state(0);
const isDone = trigger(isGreaterThan10, count);
console.log(isDone.s()) // false

isDone.report(console.log);

count.next(2);
count.next(8);
count.next(15);
// console
// true
{'fraction_non_alphanumeric': 0.22912945099440277, 'fraction_numerical': 0.022388948433964512, 'mean_word_length': 3.335570469798658, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 61, 'https://': 14, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5572594', 'n_tokens_mistral': 3174, 'n_tokens_neox': 2963, 'n_words': 667}