stories
stories copied to clipboard
Instantiate domain entities using variable normalization.
Ideally, data storage layer functions should return naked data like dictionaries and tuples.
We could avoid implementation of a private variable concept, if all dirty data from database and third-party services would be encapsulated in the domain objects without being stored in state.
from dataclasses import dataclass
from typing import Callable
from generics import defended, private
from marshmallow import Schema, post_load
from marshmallow.fields import Integer, Nested, String
from stories import I, Story
from stories_marshmallow import Argument, state
@defended
@dataclass
class Action(Story):
I.find_user
I.check_balance
def find_user(self, state):
state.user = self.load_user(state.user_id)
def check_balance(self, state):
if not state.user.has_enough():
raise Exception
load_user: Callable
@private
@dataclass
class User:
name: str
balance: int
def has_enough(self):
return self.balance > 0
class UserSchema(Schema):
name = String()
balance = Integer()
@post_load
def make_user(self, data, **kwargs):
return User(**data)
@state
class ActionState(Schema):
user_id = Argument(Integer())
user = Nested(UserSchema())
def load_user(user_id):
return {"name": "Jeff", "balance": 14}
action = Action(load_user)
action(ActionState(user_id=1))
See https://marshmallow.readthedocs.io/en/stable/quickstart.html#deserializing-to-objects
from typing import Callable
from generics import private
from pydantic import BaseModel
from pydantic_initialized import initialized
from stories_pydantic import Argument, I, state, Story
@initialized
class Action(Story):
I.find_user
I.check_balance
def find_user(self, state):
state.user = self.load_user(state.user_id)
def check_balance(self, state):
if not state.user.has_enough():
raise Exception
load_user: Callable
@private
@initialized
class User(BaseModel):
name: str
balance: int
class Config:
allow_mutation = False
def has_enough(self):
return self.balance > 0
@state
class ActionState(BaseModel):
user_id = Argument[int]
user = User
def load_user(user_id):
return {"name": "Jeff", "balance": 14}
action = Action(load_user)
action(ActionState(user_id=1))
from attrs import define
from attrs import field
from trafaret import Bool
from trafaret import Int
from trafaret import Mapping
from cruftbot.entities import Invitations
from cruftbot.entities import Repository
@define
class AcceptInvitations:
invitations = field(
init=False, converter=Mapping(Int(), Bool() >> Repository) >> Invitations
)