Source code for revived.action

"""
This module implements helper functions and classes that can be used to define
``actions`` and ``action creators``, in the same fashion of redux ones, but
using decorators instead of anonymous functions.

Actions and action creators
===========================

Actions are payloads of information that send data from your application to your
``store``. They are the only source of information for the ``store``. You send
them to the store using :any:`revived.store.Store.dispatch`.

Actions are instances of :any:`revived.action.Action`. They have a type
property. Types should be defined in an enum inheriting
:any:`revived.action.ActionType`. Once your app is large enough, you may want to
move them into a separate module.

Action creators are exactly that: functions that create ``actions``. It's easy
to conflate the terms ``action`` and ``action creator``, so do your best to use
the *proper term*.

Define action types
===================

While you are free to define the action type enum as he prefers, it is
**strongly suggested** to write them down in this way:

.. code:: python

    from revived.actions import ActionType as BaseActionType

    # custom action types enum
    class ActionType(BaseActionType):
        AN_ACTION_TYPE = 'an_action_type'


Define action creators
======================

While it is possible to explicitly build :any:`revived.action.Action` instances
directly, it is **strongly suggested** to create ``actions`` using ``action
creators``.

Assuming you are in the same module of the ``action types`` defined previously,
you can define ``action creators`` in this way:

.. code:: python

    # define the action creator that takes two arguments and returns a
    # dictionary with those arguments in an arbitrary way.
    @action(ActionTypes.AN_ACTION_TYPE)
    def an_action_type_with_parameters(param1, param2):
        return {'1': param1, '2': param2}

    # create the action object
    action_obj = an_action_type_with_parameters(1, 2)
"""
from enum import Enum
from enum import unique
from functools import wraps
from typing import Any
from typing import Callable
from typing import Dict
from typing import Optional


@unique
[docs]class ActionType(str, Enum): """Action type base class. The idea behind this class is to use an unique enum to store action types for each module. Usually there would be no need for such a feature-less class, but it is pretty handy while using type hints. """ # FIXME: this method is added to avoid sphinx_autodoc_typehints errors: # see https://github.com/agronholm/sphinx-autodoc-typehints/issues/12 def __init__(*args, **kwargs): pass
[docs]class Action(dict): """Structure that stores all the required data for an action. Redux actions are plain objects - ie: python dicts - but having a specific class here helps for type hinting. The rationale behind this is that we store the type as ``metadata`` instead of part of the action data itself. While ``action_type`` is going to be stored as ``metadata``, the :any:`revived.action.Action` instance itself is going to behave exactly as a dict, with all the action data inside. :param action_type: The type of the action. :param data: An optional dict containing data. No restriction on depth and members type, as long as the keys are strings. """ def __init__(self, action_type: ActionType, data: Optional[Dict[str, Any]]=None) -> None: super().__init__(**(data or {})) self.type = action_type
[docs]def action(action_type: ActionType) -> Callable[[Callable], Callable]: """Decorator function to use as an ``action creator`` factory. This helper function is used to create action creators. The idea behind this is that we just want to define the relevant data as a ``dict``, instead of complex objects. This decorator will take care of simple-dict-returning functions preparing the proper :any:`revived.action.Action` instance that is needed by the revived API. :param action_type: The type of the action. :returns: The ``action creator``. """ def wrap(f: Callable[..., Dict]) -> Callable[..., Action]: @wraps(f) def wrapped(*args, **kwargs) -> Action: return Action(action_type, f(*args, **kwargs)) return wrapped return wrap