Creating a injector is a matter of creating a Container
instance:
from mediapills.dependency_injection import Container
injector = Container()
As many other dependency injection containers, mediapills.dependency_injection manages two different kind of data: services and parameters.
A service is an object that does something as part of a larger system. Examples of services: a database connection, a templating engine, or a mailer. Almost any object can be a service.
Services are defined by anonymous functions that return an instance of an object:
# define some services
injector['session_storage'] = lambda di: (
SessionStorage('SESSION_ID')
)
injector['session'] = lambda di: (
Session(di['session_storage'])
)
Notice that the anonymous function has access to the current injector instance, allowing references to other services or parameters.
As objects are only created when you get them, the order of the definitions does not matter.
Using the defined services is also very easy:
# get the session object
session = injector['session']
# the above call is roughly equivalent to the following code:
# storage = SessionStorage('SESSION_ID')
# session = Session(storage)
By default, each time you get a service, Container
returns the
same instance of it. If you want a different instance to be returned for
all calls, wrap your anonymous function with the factory()
method
injector['session'] = injector.factory(lambda di: (
Session(di['session_storage'])
))
Now, each call to injector['session']
returns a new instance of the
session.
Defining a parameter allows to ease the configuration of your container from the outside and to store global values:
# define some parameters
injector['cookie_name'] = 'SESSION_ID'
injector['session_storage_cls'] = SessionStorage
If you change the session_storage
service definition like below:
injector['session_storage'] = lambda di: (
di['session_storage_cls'](di['cookie_name'])
)
You can now easily change the cookie name by overriding the
cookie_name
parameter instead of redefining the service
definition.
Because Pimple sees anonymous functions as service definitions, you need to
wrap anonymous functions with the protect()
method to store them as
parameters:
injector['random_func'] = lambda i: rand()
injector.protect('random_func')
In some cases you may want to modify a service definition after it has been
defined. You can use the extend()
method to define additional code to be
run on your service just after it is created:
injector['session_storage'] = lambda di: (
di['session_storage_class'](di['cookie_name'])
)
def session_storage_ext(storage: Callable, di: Container):
# Do something with base storage using di
return storage
injector.extend('session_storage', session_storage_ext)
The first argument is the name of the service to extend, the second a function that gets access to the object instance and the container.
When you access an object, Container
automatically calls the anonymous
function that you defined, which creates the service object for you. If you
want to get raw access to this function, you can use the raw()
method:
injector['session'] = lambda di: (
Session(di['session_storage'])
)
sessionFunction = container.raw('session')