diff --git a/docs/usage/result.md b/docs/usage/result.md index a30a4bd..3d94ab3 100644 --- a/docs/usage/result.md +++ b/docs/usage/result.md @@ -13,6 +13,7 @@ | [`unwrap_or_else(on_failure_handler)`](#unwrap_or_else) | Returns the encapsulated value if this instance is a success or execute the `on_failure_handler` when it is failure. | | [`unwrap_and(on_success_handler)`](#unwrap_and) | Returns the encapsulated value if this instance is a success and execute the `on_success_handler` when it is success. | | [`handle(on_success_handler,on_failure_handler)`](#handle) | Returns itself and execute the `on_success_handler` when the instance is a success and the `on_failure_handler` when it is failure. | +| [`bind(func)`](#bind) | Returns itself binding success value with input func | | [`transform()`](#transform) | Transform the result with a transformer function. You can give the transformer callable or use the set_transformer function to pre-set the callable to be used. | @@ -320,6 +321,50 @@ Returns itself and execute the on_success_handler when the instance is a success run(result) ``` +### bind + +Returns itself binding success value with input func + +!!! question + + What's the difference with handle? + + It's quite similar but simpler. Bind only be applied to success value and don't accept external arguments + This function is very convenient for chaining actions on a result. + + +```python +from typing import Any + +from meiga import Success + +user = {"name": "rosalia de castro", "age": 186} + +result = Success(user) + + +def upper_name(value: Any) -> Any: + value.update({"name": value["name"].upper()}) + return value + +def update_age(value: Any) -> Any: + value.update({"age": value["age"] + 1}) + return value + +def add_location(value: Any) -> Any: + value.update({"location": "GALIZA"}) + return value + + +result = ( + result + .bind(upper_name) + .bind(update_age) + .bind(add_location) +) +``` + + ### `transform` Transform the result with a transformer function. You can give the transformer callable or use the `set_transformer` diff --git a/meiga/result.py b/meiga/result.py index bc6a557..6269eaa 100644 --- a/meiga/result.py +++ b/meiga/result.py @@ -229,6 +229,19 @@ def handle( return self + def bind(self, func: Callable[..., None]) -> Result[TS, TF]: + """ + Returns itself binding success value with input func + """ + value = self.unwrap() + if value is None: + return self + + new_value = func(value) + self.set_value(new_value) + + return self + def map(self, mapper: Callable[[TS | TF], Any]) -> None: """ Modifies encapsulate value applying a mapper function. diff --git a/tests/unit/doc/__init__.py b/tests/unit/doc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/doc/test_example_with_meiga.py b/tests/unit/doc/test_example_with_meiga.py index 7745fdd..2425ed7 100644 --- a/tests/unit/doc/test_example_with_meiga.py +++ b/tests/unit/doc/test_example_with_meiga.py @@ -1,6 +1,6 @@ import pytest -from tests.unit.doc.example_with_meiga import NoSuchKey, TypeMismatch, string_from_key +from .example_with_meiga import NoSuchKey, TypeMismatch, string_from_key @pytest.mark.unit diff --git a/tests/unit/doc/test_example_without_meiga.py b/tests/unit/doc/test_example_without_meiga.py index 23dd4b9..c1acdd5 100644 --- a/tests/unit/doc/test_example_without_meiga.py +++ b/tests/unit/doc/test_example_without_meiga.py @@ -1,10 +1,6 @@ import pytest -from tests.unit.doc.example_without_meiga import ( - NoSuchKey, - TypeMismatch, - string_from_key, -) +from .example_without_meiga import NoSuchKey, TypeMismatch, string_from_key @pytest.mark.unit diff --git a/tests/unit/test_result_bind.py b/tests/unit/test_result_bind.py new file mode 100644 index 0000000..6e78e18 --- /dev/null +++ b/tests/unit/test_result_bind.py @@ -0,0 +1,41 @@ +import pytest + +from meiga import Failure, Success + + +@pytest.mark.unit +def test_should_bind_a_success_result_encapsulated_value(): + result = Success("hi") + + def func(value: str): + return value.capitalize() + + result.bind(func) + + assert result.value == "Hi" + + +@pytest.mark.unit +def test_should_bind_several_times_success_result(): + result = Success(0) + + def func(value: int): + return value + 1 + + result.bind(func).bind(func).bind(func).bind(func).bind(func).bind(func).bind( + func + ).bind(func).bind(func).bind(func) + + assert result.value == 10 + + +@pytest.mark.unit +def test_should_bind_and_keep_same_object_when_failure(): + result = Failure("failure_value") + + def func(value: str): + return value.capitalize() + + result.bind(func) + + assert result.value == "failure_value" diff --git a/tests/unit/test_result_map.py b/tests/unit/test_result_map.py index f04a3f1..b6860d3 100644 --- a/tests/unit/test_result_map.py +++ b/tests/unit/test_result_map.py @@ -4,25 +4,25 @@ @pytest.mark.unit -def test_should_transform_a_success_result_encapsulated_value(): - def transform(value): +def test_should_map_a_success_result_encapsulated_value(): + def mapper(value): return f"{value} Meiga" result = Success("Hi") - result.map(transform) + result.map(mapper) assert result.value == "Hi Meiga" @pytest.mark.unit -def test_should_transform_a_failure_result_encapsulated_value(): - def transform(domain_error): +def test_should_map_a_failure_result_encapsulated_value(): + def mapper(domain_error): if isinstance(domain_error, Error): return "Error" else: return domain_error result = Failure(Error()) - result.map(transform) + result.map(mapper) assert result.value == "Error"