Skip to content

Commit f785f6e

Browse files
Merge pull request cerebral#34 from cerebral/readmes
docs: clear copy pasted README.md contents
2 parents c28b02d + f6c2c58 commit f785f6e

File tree

3 files changed

+6
-711
lines changed

3 files changed

+6
-711
lines changed
Lines changed: 2 additions & 237 deletions
Original file line numberDiff line numberDiff line change
@@ -1,238 +1,3 @@
1-
# action-chain
1+
# betsy
22

3-
## Why
4-
5-
All modern frameworks has some concept of an action. The purpose of an action is to perform side effects, this being changing the state of the application or talking to the server. How you express this action is different in the different frameworks and tools, but they typically have one thing in common... they are expressed as one function with imperative code.
6-
7-
There is nothing wrong with imperative code, we need it, but it has some limitations:
8-
9-
1. When a single function with imperative code grows it quickly becomes difficult to reason about what it does
10-
11-
2. There is no way to track what the function does, because it is low level and we typically point directly to other libraries and functions
12-
13-
3. It requires a lot of dicipline to make your code reusable and composable
14-
15-
**action-chain** moves you into a functional world by exposing a chaining API, much like [RxJS](http://reactivex.io/rxjs/manual/overview.html). But instead of being focused on value transformation, action-chain is focused on side effects. With its "developer experience" driven implementation it allows for building developer tools that can visual all execution.
16-
17-
## Create an action-chain
18-
19-
```ts
20-
import { Action, NoValueAction, actionChainFactory, actionFactory } from 'action-chain'
21-
22-
// The context holds all side effects you want to access in
23-
// your chain. Expressed as simple objects with methods. You would
24-
// use this to wrap existing libraries, exposing a domain specific
25-
// api for your action chain
26-
const context = {
27-
say: {
28-
hello: () => 'hello',
29-
goodbye: () => 'goodbye'
30-
}
31-
}
32-
33-
type Context = typeof context
34-
35-
// The action chain manages execution of the actions and
36-
// provides the context to them
37-
const actionChain = actionChainFactory<Context>(context)
38-
39-
// You define your own factory for creating actions. It
40-
// can define an initial value type and returns an
41-
// action factory with the chain defined. You type out
42-
// conditional "Action" or "NoValueAction" to allow
43-
// typed actions to require a value and untyped actions
44-
// to not require a value
45-
const action = function <InitialValue>(): InitialValue extends undefined
46-
? NoValueAction<Context, InitialValue>
47-
: Action<Context, InitialValue> {
48-
return actionFactory<Context, InitialValue>(actionChain)
49-
}
50-
```
51-
52-
## Define actions
53-
54-
```ts
55-
const test = action<string>()
56-
.map((name, { say }) => `${say.hello()} ${name}`)
57-
58-
test('Bob') // "hello Bob"
59-
```
60-
61-
## Track actions
62-
63-
```ts
64-
actionChain.on('action:start', (details) => {
65-
/*
66-
{
67-
actionId: 0,
68-
executionId: 0,
69-
}
70-
*/
71-
})
72-
actionChain.on('operator:start', (details) => {
73-
/*
74-
{
75-
actionId: 0,
76-
executionId: 0,
77-
operatorId: 0,
78-
name: '',
79-
type: 'map',
80-
path: []
81-
}
82-
*/
83-
})
84-
actionChain.on('operator:end', (details) => {
85-
/*
86-
{
87-
actionId: 0,
88-
executionId: 0,
89-
operatorId: 0,
90-
name: '',
91-
type: 'map',
92-
path: [],
93-
isAsync: false,
94-
result: 'hello Bob'
95-
}
96-
*/
97-
})
98-
actionChain.on('action:end', (details) => {
99-
/*
100-
{
101-
actionId: 0,
102-
executionId: 0,
103-
}
104-
*/
105-
})
106-
```
107-
108-
## Operators
109-
110-
### do
111-
Allows you to run effects and passes the current value a long to the next operator.
112-
```ts
113-
const test = action()
114-
.do((_, { localStorage }) => {
115-
localStorage.set('foo', 'bar')
116-
})
117-
```
118-
119-
### map
120-
Maps to a new value, passed to the next operator.
121-
```ts
122-
const test = action<string>()
123-
.map((value) => value.toUpperCase())
124-
```
125-
126-
### try
127-
If returning a promise, run paths based on resolved or rejected.
128-
```ts
129-
const test = action<string>()
130-
.try((_, { api }) => api.getUser(), {
131-
success: action(),
132-
error: action()
133-
})
134-
```
135-
136-
### when
137-
Executes true or false path based on boolean value.
138-
```ts
139-
const test = action<string>()
140-
.when((value) => value.length > 3, {
141-
true: action(),
142-
false: action()
143-
})
144-
```
145-
146-
### filter
147-
Stops execution when false.
148-
```ts
149-
const test = action<string>()
150-
.filter(() => false)
151-
// does not run
152-
.map(() => 'foo')
153-
```
154-
155-
### debounce
156-
Debounces execution.
157-
```ts
158-
const test = action<string>()
159-
.debounce(100)
160-
// Runs when 100 milliseconds has passed since
161-
// last time the "test" action was called
162-
.map(() => 'foo')
163-
```
164-
165-
## Extend operators
166-
167-
```ts
168-
import { Action, NoValueAction, actionChainFactory, actionFactory, Execution } from 'action-chain'
169-
170-
// There are two types of actions. Actions that takes an initial value
171-
interface MyAction<Context, InitialValue, Value = InitialValue>
172-
extends MyOperators<Context, InitialValue, Value>,
173-
Action<Context, InitialValue, Value> {}
174-
175-
// And those who do not
176-
interface NoValueMyAction<Context, InitialValue, Value = InitialValue>
177-
extends MyOperators<Context, InitialValue, Value>,
178-
NoValueAction<Context, InitialValue, Value> {}
179-
180-
// You type out your operators and all of them will return either
181-
// an action with an initial value or not, based on the "InitialValue"
182-
// typing
183-
interface MyOperators<Context, InitialValue, Value> {
184-
log(): InitialValue extends undefined
185-
? NoValueMyAction<Context, InitialValue, Value>
186-
: MyAction<Context, InitialValue, Value>
187-
}
188-
189-
// Create a new actionFactory which composes the default one and implements
190-
// the new operators
191-
function myActionFactory<Context, InitialValue, Value = InitialValue>(
192-
actionChain: ActionChain<Context>,
193-
initialActionId?: number,
194-
runOperators?: (
195-
value: any,
196-
execution: Execution,
197-
path: string[]
198-
) => any | Promise<any>
199-
): InitialValue extends undefined
200-
? NoValueMyAction<Context, InitialValue, Value>
201-
: MyAction<Context, InitialValue, Value> {
202-
return Object.assign(
203-
actionFactory<Context, InitialValue, Value>(
204-
actionChain,
205-
initialActionId,
206-
runOperators
207-
) as any,
208-
{
209-
log() {
210-
const operator = (value) => {
211-
console.log(value)
212-
return value
213-
}
214-
215-
const [
216-
chain,
217-
initialActionId,
218-
runOperators,
219-
] = this.createOperatorResult('log', '', operator)
220-
221-
return myActionFactory<Context, InitialValue, Value>(
222-
chain,
223-
initialActionId,
224-
runOperators
225-
)
226-
},
227-
}
228-
)
229-
}
230-
231-
const myAction = function<
232-
InitialValue = undefined
233-
>(): InitialValue extends undefined
234-
? NoValueMyAction<Context, InitialValue>
235-
: MyAction<Context, InitialValue> {
236-
return myActionFactory<Context, InitialValue>(actionChain)
237-
}
238-
```
3+
To be filled

0 commit comments

Comments
 (0)