Adding docs for overriding of providers

This commit is contained in:
Roman Mogilatov 2015-07-25 04:57:20 +03:00
parent bfa40aec10
commit 024b148c54
7 changed files with 204 additions and 15 deletions

BIN
docs/images/internals.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -6,7 +6,7 @@ Factory providers
Nothing could be better than brief example: Nothing could be better than brief example:
.. image:: /images/providers/factory.png .. image:: /images/providers/factory.png
:width: 80% :width: 75%
:align: center :align: center
.. code-block:: python .. code-block:: python
@ -47,13 +47,13 @@ All of those instructions are defined in ``objects.injections`` module and are
subclasses of ``objects.injections.Injection``. There are several types of subclasses of ``objects.injections.Injection``. There are several types of
injections that are used by ``Factory`` provider: injections that are used by ``Factory`` provider:
- ``KwArg`` - injection is done by passing injectable value in object's + ``KwArg`` - injection is done by passing injectable value in object's
``__init__()`` method in time of object's creation via keyword argument. ``__init__()`` method in time of object's creation via keyword argument.
Takes keyword name of ``__init__()`` argument and injectable value. Takes keyword name of ``__init__()`` argument and injectable value.
- ``Attribute`` - injection is done by setting specified attribute with + ``Attribute`` - injection is done by setting specified attribute with
injectable value right after object's creation. Takes attribute's name injectable value right after object's creation. Takes attribute's name
and injectable value. and injectable value.
- ``Method`` - injection is done by calling of specified method with + ``Method`` - injection is done by calling of specified method with
injectable value right after object's creation and attribute injections injectable value right after object's creation and attribute injections
are done. Takes method name and injectable value. are done. Takes method name and injectable value.
@ -70,6 +70,8 @@ Example below shows how to create ``Factory`` of particular class with
provided by another factories: provided by another factories:
.. image:: /images/providers/factory_init_injections.png .. image:: /images/providers/factory_init_injections.png
:width: 85%
:align: center
.. code-block:: python .. code-block:: python
@ -358,13 +360,18 @@ injectable value. ``Factory`` delegation is performed by wrapping delegated
``Factory`` into special provider type - ``Delegate``, that just returns ``Factory`` into special provider type - ``Delegate``, that just returns
``Factory`` itself. ``Factory`` itself.
Another one, more *convenient*, method of creating ``Delegate`` for ``Factory`` Actually, there are two ways of creating factory delegates:
is just calling ``Factory.delegate()`` method that returns delegate for current
factory. + ``Delegate(Factory(...))`` - obviously wrapping factory into ``Delegate``
provider.
+ ``Factory().delegate()`` - calling factory ``delegate()`` method, that
returns delegate wrapper for current factory.
Example: Example:
.. image:: /images/providers/factory_delegation.png .. image:: /images/providers/factory_delegation.png
:width: 85%
:align: center
.. code-block:: python .. code-block:: python

View File

@ -2,3 +2,185 @@ Overriding of providers
----------------------- -----------------------
Every provider could be overridden by another provider. Every provider could be overridden by another provider.
This gives opportunity to make system behaviour more flexible in some points.
The main feature is that while your code is using providers, it depends on
providers, but not on the objects that providers provide. As a result of this,
you can change providing by provider object to a different one, but still
compatible one, without chaning your previously written code.
Provider overriding functionality has such interface:
.. image:: /images/provider_override.png
:width: 45%
:align: center
+ ``Provider.override()`` - takes another provider that will be used instead of
current provider. This method could be called several times. In such case,
last passed provider would be used as overriding one.
+ ``Provider.reset_override()`` - resets all overriding providers. Provider
starts to behave itself like usual.
+ ``Provider.is_overridden`` - bool, ``True`` if provider is overridden.
.. note::
Actually, initial provider forms stack from overriding providers. There is
some, not so common, but still usefull, functionality that could be used:
+ ``Provider.last_overriding`` - always keeps reference to last overriding
provider.
+ ``Provider.reset_last_overriding()`` - remove last overriding provider
from stack of overriding providers.
Example:
.. image:: /images/providers/overriding_simple.png
:width: 80%
:align: center
.. code-block:: python
"""Simple providers overriding example."""
from objects.providers import Factory
class User(object):
"""Example class User."""
# Users factory:
users_factory = Factory(User)
# Creating several User objects:
user1 = users_factory()
user2 = users_factory()
# Making some asserts:
assert user1 is not user2
assert isinstance(user1, User) and isinstance(user2, User)
# Extending User:
class SuperUser(User):
"""Example class SuperUser."""
# Overriding users factory:
users_factory.override(Factory(SuperUser))
# Creating some more User objects using overridden users factory:
user3 = users_factory()
user4 = users_factory()
# Making some asserts:
assert user4 is not user3
assert isinstance(user3, SuperUser) and isinstance(user4, SuperUser)
Example:
.. image:: /images/providers/overriding_users_model.png
:width: 100%
:align: center
.. code-block:: python
"""Overriding user's model example."""
from objects.providers import Factory
from objects.injections import KwArg
class User(object):
"""Example class User."""
def __init__(self, id, password):
"""Initializer."""
self.id = id
self.password = password
super(User, self).__init__()
class UserService(object):
"""Example class UserService."""
def __init__(self, user_cls):
"""Initializer."""
self.user_cls = user_cls
super(UserService, self).__init__()
def get_by_id(self, id):
"""Find user by his id and return user model."""
return self.user_cls(id=id, password='secret' + str(id))
# Users factory and UserService provider:
users_service = Factory(UserService,
KwArg('user_cls', User))
# Getting several users and making some asserts:
user1 = users_service().get_by_id(1)
user2 = users_service().get_by_id(2)
assert isinstance(user1, User)
assert user1.id == 1
assert user1.password == 'secret1'
assert isinstance(user2, User)
assert user2.id == 2
assert user2.password == 'secret2'
assert user1 is not user2
# Extending user model and user service for adding custom attributes without
# making any changes to client's code.
class ExtendedUser(User):
"""Example class ExtendedUser."""
def __init__(self, id, password, first_name=None, last_name=None,
gender=None):
"""Initializer."""
self.first_name = first_name
self.last_name = last_name
self.gender = gender
super(ExtendedUser, self).__init__(id, password)
class ExtendedUserService(UserService):
"""Example class ExtendedUserService."""
def get_by_id(self, id):
"""Find user by his id and return user model."""
user = super(ExtendedUserService, self).get_by_id(id)
user.first_name = 'John' + str(id)
user.last_name = 'Smith' + str(id)
user.gender = 'male'
return user
# Overriding users_service provider:
extended_users_service = Factory(ExtendedUserService,
KwArg('user_cls', ExtendedUser))
users_service.override(extended_users_service)
# Getting few other users users and making some asserts:
user3 = users_service().get_by_id(3)
user4 = users_service().get_by_id(4)
assert isinstance(user3, ExtendedUser)
assert user3.id == 3
assert user3.password == 'secret3'
assert user3.first_name == 'John3'
assert user3.last_name == 'Smith3'
assert isinstance(user4, ExtendedUser)
assert user4.id == 4
assert user4.password == 'secret4'
assert user4.first_name == 'John4'
assert user4.last_name == 'Smith4'
assert user3 is not user4

View File

@ -19,12 +19,12 @@ assert user1 is not user2
assert isinstance(user1, User) and isinstance(user2, User) assert isinstance(user1, User) and isinstance(user2, User)
# Overriding users factory: # Extending User:
class SuperUser(User): class SuperUser(User):
"""Example class SuperUser.""" """Example class SuperUser."""
# Overriding users factory:
users_factory.override(Factory(SuperUser)) users_factory.override(Factory(SuperUser))
# Creating some more User objects using overridden users factory: # Creating some more User objects using overridden users factory: