DEV Community

loading...

Wizar devlog 08 - Ravioli transformation

dagatsoin profile image Daniel Neveux ・2 min read

Achievement of the week so far:

User Stories:

  • user can connect Crafter observable to React. I finished the first version of Crafter React which makes React (really) reactive to Cragter observables changes. It was mainly a copy past of Mobx-React but without the hooks for now.
  • user can add Transformation to a Ravioli Component In the SAM pattern the model is isolated from the view. That makes possibles to write application in "privacy first" because what the world sees about your component is not necessarily the exact copy of the underlying model. Eg. As a player I can't see the contains of other players inventory.
  • transformation applies only on certains control state. To keep a good developer experience, Ravioli provides an API to effectively split your code. Instead of having one representation which handles multiple cases, we can easily put many representation as needed, depending on the control states (and many others params if needed). Eg. As a player, I can see the other dead players inventory. Instead of using if/else, let's be more declarative and use two different transformation. One for an alive player, one for a dead player. See the example below.

Technical Stories:

  • refactor Crafter to make it compatible on Node.js app. Basically removed a singleton and implements a container pattern to isolate each observers/observables context.
test('representation predicate', function() {
  const player = component(object({
    name: string(),
    health: number(),
    inventory: array(object({ id: string(), quantity: number() }))
  }))
    // Define which control state my player can be
    .setControlStatePredicate('isAlive', ({model}) => model.health > 0)
    .setControlStatePredicate('isDead', ({model}) => model.health <= 0)

    // Define acceptors
    .addAcceptor('setHealth', model => ({mutator({hp}: {hp: number}): void { model.health += hp }}))

    // Define actions (if you use TS, it will autocomplete magically only with possible mutations <3 )
    .addActions({ hit: () => [{ type: 'setHealth', payload: {hp: -10}}]})

    // How the world will see me?
    .setTransformation('alive', {
      predicate: 'isAlive', // I am alive, no inventory visible.
      computation: ({modelSnapshot}) => ({ name: modelSnapshot.name, health: modelSnapshot.health})
    })
    .setTransformation('dead', {
      predicate: 'isDead', // I died. Be my guest. Loot me.
      computation: ({modelSnapshot}) => ({ name: modelSnapshot.name, inventory: modelSnapshot.inventory })
    })

    // Instantiate a player
    .create({
      name: 'Fraktar',
      health: 10,
      inventory: [
        { id: 'sword', quantity: 1},
        { id: 'shield', quantity: 1},
      ]
    })

  // Player is alive, I can't see its inventory.
  expect(player.state.representation.inventory).toBeUndefined()

  // Take that!
  player.actions.hit()

  // Haha, here is one a lovely sword!
  expect(player.state.representation.inventory).toBeDefined()
})

Discussion (0)

pic
Editor guide