Skip to content

Commit

Permalink
Merge pull request #27 from itskihaga/dev
Browse files Browse the repository at this point in the history
fix: doc
  • Loading branch information
eatski authored Nov 4, 2021
2 parents c182b4a + 8c05479 commit 59f626f
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 30 deletions.
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
# Exprocess for Rust
Exprocess is a framework for declaratively describing application logic.


# Core
https://github.com/itskihaga/exprocess-rust/blob/main/libs/exprocess/core.rs

# Usage
https://github.com/itskihaga/exprocess-rust/blob/main/src/domain/state.rs
[doc](libs/exprocess/readme.md)

# Sample App
https://pick-role.web.app/
77 changes: 66 additions & 11 deletions libs/exprocess/core.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,91 @@
pub trait ExprocessCore {

type State;
type Command;
type Result;
///
/// Initializes and returns the state of the application.
///
fn init() -> Self::State;
fn resolve(prev: &Self::State, command: Self::Command) -> Self::Result;
fn reducer(prev: &mut Self::State, result: Self::Result);
///
/// Returns a Result from Command and the current state.
/// This function does not need to be idempotent.
/// For example, it is possible to incorporate a random number into the logic.
///
fn resolve(state: &Self::State, command: Self::Command) -> Self::Result;
///
/// Update the state of the application from Result.
/// This operation must be idempotent.
/// By the idempotence, Exprocess can reconstruct the state of the application from the history of Result and this function.
///
fn reducer(state: &mut Self::State, result: Self::Result);
}


/* sample code */

struct SampleCore;

struct SampleState {
hoge: Vec<String>,
fuga: Vec<String>
pub health: i32,
pub sociability: Sociability
}

enum Sociability {
#[allow(dead_code)]
Popular,
Solitude
}

enum SampleCommand {
#[allow(dead_code)]
EatSteak,
#[allow(dead_code)]
MeetFriend
}

enum SampleResult {
FeelBetter(i32),FeelWorse(i32)
}

impl ExprocessCore for SampleCore {
type State = SampleState;

type Command = ();
type Command = SampleCommand;

type Result = ();
type Result = SampleResult;

fn init() -> Self::State {
todo!()
SampleState {
health: 100,
sociability: Sociability::Solitude
}
}

fn resolve(_prev: &Self::State, _command: Self::Command) -> Self::Result {
todo!()
fn resolve(state: &Self::State, command: Self::Command) -> Self::Result {
match command {
SampleCommand::EatSteak => {
SampleResult::FeelBetter(10)
},
SampleCommand::MeetFriend => {
match &state.sociability {
Sociability::Popular => SampleResult::FeelBetter(30),
Sociability::Solitude => SampleResult::FeelWorse(10),
}

},
}
}

fn reducer(prev: &mut Self::State, _result: Self::Result) {
prev.fuga = prev.hoge.drain(0..).collect();
fn reducer(state: &mut Self::State, result: Self::Result) {
match result {
SampleResult::FeelBetter(helth) => {
state.health = helth;
},
SampleResult::FeelWorse(helth) => {
state.health = -helth;
},
}
}

}
39 changes: 39 additions & 0 deletions libs/exprocess/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# What's Exprocess?
Exprcess provides a thin framework for declaratively describing the logic of state transitions in applications, and a library that helps to apply the logic created according to the framework to your application.

## What are the benefits of using Exprocess?
- Forces a highly testable implementation.
- Implemented logic can be run anywhere.
- Provides easy state reconstruction and Redo/Undo operations.

## Let's take a look at the simple code.
Let's take a look at the core of Exprcess.[code](/libs/exprocess/core.rs)
Exprocess provides a trait called ExprocessCore. This trait requires the implementation of the following three functions.
- init
- resolve
- reducer

For more details about each function, please refer to the comment doc.

## Runners
Core is merely a declarative description of logic.
Exprocess provides "Runner" as helpers to make the declarative logic work and apply it to applications.

|||
|---|---|
| Web Client Runner | Runner that is designed to work in a browser and with multiple people sharing the state. |

# Let's take a look at this repository and the sample.
## URL
https://pick-role.web.app/
## What's this app?
In this application, you can assign "roles" to multiple participants at random.
The front-end is designed so that the roles are only visible to the assigned participants themselves.

## Let's take a look at the code.
Now that I've explained it, you should be able to see it in the code.
The following is a list of some of the key points.
- [Core Logic](/src/domain/state.rs)
- [Use Runner](/src/containers/main.rs)


34 changes: 21 additions & 13 deletions src/domain/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ pub struct PickedState {
pub picked: Vec<(Member,Role)>
}

//FIXME ドメインモデルにつけて問題ない?
#[derive(Serialize, Deserialize,Clone)]
pub enum AppCommand {
Init(Vec<Member>),
Expand All @@ -46,7 +45,6 @@ pub struct PickCommand {
pub roles: Vec<ItemAndHowMany<Role>>
}

//FIXME ドメインモデルにつけて問題ない?
#[derive(Serialize, Deserialize,Clone)]
pub enum AppResult {
Init(Vec<Member>),
Expand All @@ -63,16 +61,25 @@ impl ExprocessCore for AppCore {
type State = AppState;
type Command = AppCommand;
type Result = AppResult;

///
/// It simply returns a blank state.
/// Originally, we need to pass the data of the members to initialize this application,
/// but "currently" Exprocess cannot pass arguments to init.
/// So, they implemented it to create a blank state once and pass the data as Command later.
///
fn init() -> Self::State {
AppState::Blank
}

fn resolve(prev: &Self::State,command: Self::Command) -> Self::Result {
///
/// Takes as input roles and the number of members to be assigned to each role, and assigns the roles to the members.
/// It is important to note that all that is happening in this function is to return the result of assigning the role.
/// No state update will occur here.
///
fn resolve(state: &Self::State,command: Self::Command) -> Self::Result {
match command {
AppCommand::Init(members) => AppResult::Init(members.clone()),
AppCommand::Init(members) => AppResult::Init(members),
AppCommand::Pick(command) => {
match prev {
match state {
AppState::Standby(members) => AppResult::Picked(
PickResult {
picked: pick_roles_to_members(
Expand All @@ -87,12 +94,14 @@ impl ExprocessCore for AppCore {
},
}
}

fn reducer(prev: &mut Self::State, result: Self::Result) {
*prev = match result {
AppResult::Init(members) => AppState::Standby(members.clone()),
///
/// Update the state based on the result of the assignment.
///
fn reducer(state: &mut Self::State, result: Self::Result) {
*state = match result {
AppResult::Init(members) => AppState::Standby(members),
AppResult::Picked(result) => {
match prev {
match state {
AppState::Standby(members) => {
AppState::Picked(PickedState {
picked: result
Expand All @@ -116,7 +125,6 @@ fn pick_roles_to_members<M,R : Clone,Rn: Rng>(
) -> Vec<R> {
let mut roles : Vec<_>= roles
.iter()
// FIXME: なんかきもい
.flat_map(|(num,role)| (vec![role]).repeat(*num))
.collect();
roles.shuffle(&mut rng);
Expand Down

0 comments on commit 59f626f

Please sign in to comment.