diff --git a/docs/decorators.rst b/docs/decorators.rst index edaf9a27..5023260a 100644 --- a/docs/decorators.rst +++ b/docs/decorators.rst @@ -1,8 +1,51 @@ Decorators ========== +Current section of *Objects* documentation describes several useful decorators. + @inject decorator ----------------- +``@inject`` decorator can be used for making *inline* dependency injections. +It *patches* decorated callable in such way that dependency injection will be +done before every call of decorated callable. + +``@inject`` decorator takes only argument that is supposed to be an +``objects.injections.Injection`` instance. + +Any Python object will be injected *as is*, except *Objects* providers, +that will be called to provide injectable value. + +Below is an example of how Flask's view could be patched using ``@inject`` +decorator: + +.. 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() + + @override decorator ------------------- diff --git a/docs/providers.rst b/docs/providers.rst index 6ad64030..01b06752 100644 --- a/docs/providers.rst +++ b/docs/providers.rst @@ -10,6 +10,9 @@ provided. Instance providers & Injections ------------------------------- +Providers +~~~~~~~~~ + *Instance* providers are providers that deal with object's creation and initialization. @@ -50,16 +53,19 @@ Example: assert isinstance(object_1, object) and isinstance(object_2, object) +Injections +~~~~~~~~~~ + Objects can take dependencies in various forms. Some objects take init arguments, other are using attributes or methods to be initialized. It affects how such objects need to be created and initialized, and that is the place where *Injections* need to be used. -In terms of computer science, *Injection* of dependency is a way how +In terms of computer science, *Injection of dependency* is a way how dependency can be coupled with dependent object. In terms of *Objects*, *Injection* is an instruction how to provide -dependency for the particular object. +dependency for the particular provider. Every Python object could be an injection's value. Special case is an *Objects* provider as an injection's value. In such case, injection value is a result of diff --git a/examples/readme2/inject_decorator.py b/examples/readme2/inject_decorator.py new file mode 100644 index 00000000..fe91234c --- /dev/null +++ b/examples/readme2/inject_decorator.py @@ -0,0 +1,61 @@ +"""`@inject` decorator example. + +Flask is required to make this example work. +""" + +import sqlite3 + +from flask import Flask + +from objects.providers import NewInstance +from objects.providers import Singleton +from objects.injections import KwArg +from objects.injections import Attribute +from objects.decorators import inject + + +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').fetchone()[0] + + +# 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)) + + +# Flask application. +app = Flask(__name__) + + +# Flask view with inject decorator. +@app.route('/') +@inject(KwArg('database', database)) +@inject(KwArg('object_a', object_a)) +def hello(database): + one = database.execute('SELECT 1').fetchone()[0] + return 'Query returned {0}, db connection {1}'.format(one, database) + + +if __name__ == '__main__': + app.run() + +# Example output of "GET / HTTP/1.1" is: +# Query returned 1, db connection