Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/u8slvn/sutoppu
A simple python implementation of Specification pattern.
https://github.com/u8slvn/sutoppu
business-rules ddd python specification specification-pattern
Last synced: 9 days ago
JSON representation
A simple python implementation of Specification pattern.
- Host: GitHub
- URL: https://github.com/u8slvn/sutoppu
- Owner: u8slvn
- License: mit
- Created: 2018-12-08T13:59:55.000Z (almost 6 years ago)
- Default Branch: main
- Last Pushed: 2024-03-08T10:03:41.000Z (9 months ago)
- Last Synced: 2024-10-13T15:42:53.189Z (about 1 month ago)
- Topics: business-rules, ddd, python, specification, specification-pattern
- Language: Python
- Homepage:
- Size: 93.8 KB
- Stars: 32
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Sutoppu
[![Pypi Version](https://img.shields.io/pypi/v/sutoppu.svg)](https://pypi.org/project/sutoppu/)
[![Python Version](https://img.shields.io/pypi/pyversions/sutoppu)](https://pypi.org/project/sutoppu/)
[![CI](https://github.com/u8slvn/sutoppu/actions/workflows/ci.yml/badge.svg)](https://github.com/u8slvn/sutoppu/actions/workflows/ci.yml)
[![Coverage Status](https://coveralls.io/repos/github/u8slvn/sutoppu/badge.svg?branch=master)](https://coveralls.io/github/u8slvn/sutoppu?branch=master)
[![Project license](https://img.shields.io/pypi/l/sutoppu)](https://pypi.org/project/sutoppu/)
[![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)**Sutoppu** (ストップ - Japanese from English *Stop*) is a simple python implementation of Specification pattern.
## What is Specification Pattern?
See [Wikipedia](https://en.wikipedia.org/wiki/Specification_pattern).
> In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of domain-driven design.
More information: [Eric Evans and Martin Fowler article about Specifications](https://www.martinfowler.com/apsupp/spec.pdf)
## Basic usage
### Installation
```sh
$ pip install sutoppu
```### Usage
```python
from sutoppu import Specificationclass Fruit:
def __init__(self, color: str, sweet: bool, bitter: bool) -> None:
self.color = color
self.sweet = sweet
self.bitter = bitterclass FruitIsBitter(Specification[Fruit]):
description = 'The given fruit must be bitter.'def is_satisfied_by(self, fruit: Fruit) -> bool:
return fruit.bitter is Trueclass FruitIsSweet(Specification[Fruit]):
description = 'The given fruit must be sweet.'def is_satisfied_by(self, fruit: Fruit) -> bool:
return fruit.sweet is Trueclass FruitIsYellow(Specification[Fruit]):
description = 'The given fruit must be yellow.'def is_satisfied_by(self, fruit: Fruit) -> bool:
return fruit.color == 'yellow'
``````python
>>> lemon = Fruit(color='yellow', sweet=False, bitter=True)
>>> is_a_lemon = FruitIsYellow() & FruitIsBitter() & ~FruitIsSweet()
>>> is_a_lemon.is_satisfied_by(lemon)
True
```### Operators
**Sutoppu** uses bitwise operator overloading to provide simplified syntax.
And:
```python
>>> my_spec = SpecificationA() & SpecificationB()
```Or:
```python
>>> my_spec = SpecificationA() | SpecificationB()
```Not:
```python
>>> my_spec = ~SpecificationA()
```### Lighter syntax
If you find the `is_satisfied_by` method inconvenient, you can alternatively call the specification as shown below.
```python
>>> lemon = Fruit(color='yellow', sweet=False, bitter=True)
>>> is_a_lime = FruitIsGreen() & FruitIsBitter() & ~FruitIsSweet()
>>> is_a_lime(lemon)
False
```### Error reporting
It can be difficult to know which specification failed in complex concatenated rules. Sutoppu allows listing all the failed specifications by getting the `errors` attribute after use.
The `errors` attribute is reset each time the specification is used. For each failed specification, it returns a dict with the name of the specification class as key and the description provided in the class as value. In the case where the specification failed with a `not` condition, the description is prefixed with `Not ~`.```python
>>> apple = Fruit(color='red', sweet=True, bitter=False)
>>> is_a_lemon = FruitIsYellow() & FruitIsBitter() & ~ FruitIsSweet()
>>> is_a_lemon.is_satisfied_by(apple)
False
>>> is_a_lemon.errors
{
'FruitIsColored': 'The given fruit must be yellow.',
'FruitIsBitter': 'The given fruit must be bitter.',
'FruitIsSweet': 'Not ~ The given fruit must be sweet.'
}
```