Advanced usage
==============

Below you can find some variants of advanced usage of *Objects*.

@inject decorator
-----------------

``@inject`` decorator could be used for patching any callable with injection.
Any Python object will be injected *as is*, except *Objects* providers,
that will be called to provide injectable value.

.. code-block:: python

    """`@inject` decorator example."""

    from objects.providers import NewInstance

    from objects.injections import KwArg
    from objects.injections import inject


    new_object = NewInstance(object)


    @inject(KwArg('object_a', new_object))
    @inject(KwArg('some_setting', 1334))
    def example_callback(object_a, some_setting):
        """This function has dependencies on object a and b.

        Dependencies are injected using `@inject` decorator.
        """
        assert isinstance(object_a, object)
        assert some_setting == 1334


    example_callback()
    example_callback()

Overriding providers
--------------------

Any provider can be overridden by another provider.

Example:

.. code-block:: python

    """Provider overriding example."""

    import sqlite3

    from objects.providers import Singleton
    from objects.providers import NewInstance

    from objects.injections import KwArg
    from objects.injections import Attribute


    class ObjectA(object):

        """ObjectA has dependency on database."""

        def __init__(self, database):
            """Initializer.

            Database dependency need to be injected via init arg."""
            self.database = database

        def get_one(self):
            """Select one from database and return it."""
            return self.database.execute('SELECT 1')


    class ObjectAMock(ObjectA):

        """Mock of ObjectA.

        Has no dependency on database.
        """

        def __init__(self):
            """Initializer."""

        def get_one(self):
            """Select one from database and return it.

            Mock makes no database queries and always returns two instead of one.
            """
            return 2


    # Database and `ObjectA` providers.
    database = Singleton(sqlite3.Connection,
                         KwArg('database', ':memory:'),
                         KwArg('timeout', 30),
                         KwArg('detect_types', True),
                         KwArg('isolation_level', 'EXCLUSIVE'),
                         Attribute('row_factory', sqlite3.Row))

    object_a = NewInstance(ObjectA,
                           KwArg('database', database))


    # Overriding `ObjectA` provider with `ObjectAMock` provider.
    object_a.override(NewInstance(ObjectAMock))

    # Creating several `ObjectA` instances.
    object_a_1 = object_a()
    object_a_2 = object_a()

    # Making some asserts.
    assert object_a_1 is not object_a_2
    assert object_a_1.get_one() == object_a_2.get_one() == 2

Overriding catalogs
-------------------

Any catalog can be overridden by another catalog.

Example:

.. code-block:: python

    """Catalog overriding example."""

    import sqlite3

    from objects.catalog import AbstractCatalog
    from objects.catalog import override

    from objects.providers import Singleton
    from objects.providers import NewInstance

    from objects.injections import KwArg
    from objects.injections import Attribute


    class ObjectA(object):

        """ObjectA has dependency on database."""

        def __init__(self, database):
            """Initializer.

            Database dependency need to be injected via init arg."""
            self.database = database

        def get_one(self):
            """Select one from database and return it."""
            return self.database.execute('SELECT 1')


    class ObjectAMock(ObjectA):

        """Mock of ObjectA.

        Has no dependency on database.
        """

        def __init__(self):
            """Initializer."""

        def get_one(self):
            """Select one from database and return it.

            Mock makes no database queries and always returns two instead of one.
            """
            return 2


    class Catalog(AbstractCatalog):

        """Catalog of objects providers."""

        database = Singleton(sqlite3.Connection,
                             KwArg('database', ':memory:'),
                             KwArg('timeout', 30),
                             KwArg('detect_types', True),
                             KwArg('isolation_level', 'EXCLUSIVE'),
                             Attribute('row_factory', sqlite3.Row))

        object_a = NewInstance(ObjectA,
                               KwArg('database', database))


    @override(Catalog)
    class SandboxCatalog(Catalog):

        """Sandbox objects catalog with some mocks that overrides Catalog."""

        object_a = NewInstance(ObjectAMock)


    # Creating several `ObjectA` instances.
    object_a_1 = Catalog.object_a()
    object_a_2 = Catalog.object_a()

    # Making some asserts.
    assert object_a_1 is not object_a_2
    assert object_a_1.get_one() == object_a_2.get_one() == 2