Skip to content

Commit ccd13af

Browse files
committed
updated modules.docs
1 parent b4382da commit ccd13af

File tree

8 files changed

+57
-124
lines changed

8 files changed

+57
-124
lines changed

docs/overview/controllers.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,7 @@ from .controllers import CarController
327327
routers=[],
328328
)
329329
class CarModule(ModuleBase):
330-
def register_providers(self, container: Container) -> None:
331-
# for more complicated provider registrations
332-
# container.register_instance(...)
333-
pass
330+
pass
334331
```
335332

336333
![controller_dog.gif](../img/car_controller.gif)

docs/overview/guards.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,7 @@ from .guards import RoleGuard
143143
providers=[CarRepository, ProviderConfig(GlobalGuard, use_class=RoleGuard)]
144144
)
145145
class CarModule(ModuleBase):
146-
def register_providers(self, container: Container) -> None:
147-
# for more complicated provider registrations
148-
# container.register_instance(...)
149-
pass
146+
pass
150147

151148
```
152149

docs/overview/module-router.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,7 @@ from .routers import math_router
6767
routers=[math_router],
6868
)
6969
class CarModule(ModuleBase):
70-
def register_providers(self, container: Container) -> None:
71-
# for more complicated provider registrations
72-
# container.register_instance(...)
73-
pass
70+
pass
7471
```
7572

7673
![math_router.png](../img/math_router.png)

docs/overview/modules.md

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ from .controllers import CarController
3131
providers=[CarRepository],
3232
)
3333
class CarModule(ModuleBase):
34-
def register_providers(self, container: Container) -> None:
35-
# Additional provider registrations can be done here
36-
pass
34+
pass
3735
```
3836

3937
## **Module Parameters**
@@ -179,15 +177,10 @@ from .controllers import CarController
179177
class CarModule(ModuleBase):
180178
def __init__(self, config: Config):
181179
self.config = config
182-
183-
def register_providers(self, container: Container) -> None:
184-
# Additional provider registrations can be performed here
185-
pass
186180
```
187181

188182
In this example, the `CarModule` class accepts a `Config` object in its constructor.
189183
This allows us to access configuration parameters within the module.
190-
Additionally, the `register_providers` method can be utilized for more advanced provider registrations within the container.
191184

192185
## **Module Middleware**
193186
Middleware functions can be defined at the module level using the `@middleware()` function decorator. Let's illustrate this with an example:
@@ -264,9 +257,42 @@ you can refer to the documentation [here](https://injector.readthedocs.io/en/lat
264257
## **ForwardRefModule**
265258
`ForwardRefModule` is a powerful feature that allows you to reference a `@Module()` class in your application
266259
without needing to instantiate or configure it directly at the point of reference.
260+
267261
This is particularly useful in scenarios where you have circular dependencies between modules
268262
or when you want to declare dependencies without tightly coupling your modules.
269263

264+
#### What problem does it solve?
265+
Let's consider an example where we have two modules, `ModuleA` and `ModuleB`. `ModuleB` depends on a service/provider, which is exported, from `ModuleA` and `ModuleB` does not want to instantiate `ModuleA` directly but rather reference its existing instance from the application scope. `ModuleB` can use `ForwardRefModule` to declare the `ModuleA` as a dependency. And `ModuleA` can be setup differently in any random order.
266+
267+
A typical example is a `CustomModule` that depends on `JWTModule` to sign the JWT tokens. The `CustomModule` would declare `JWTModule` as a dependency using `ForwardRefModule` and `JWTModule` can be configured separately in `ApplicationModule`.
268+
269+
```python
270+
from ellar.common import Module
271+
from ellar.core import ModuleBase, ForwardRefModule
272+
from ellar_jwt import JWTModule, JWTService
273+
274+
275+
@Module(name="CustomModule", modules=[ForwardRefModule(JWTModule)])
276+
class CustomModule(ModuleBase):
277+
def __init__(self, jwt_service: JWTService):
278+
self.jwt_service = jwt_service
279+
assert self.jwt_service
280+
281+
282+
@Module(modules=[
283+
CustomModule,
284+
JWTModule.setup(secret_key="super-secret")
285+
])
286+
class ApplicationModule(ModuleBase):
287+
pass
288+
```
289+
The `JWTModule` provides `JWTService` which is injected into `CustomModule`. By declaring `ForwardRefModule(JWTModule)` in `CustomModule`, the `JWTService` will be properly resolved during instantiation of `CustomModule`, regardless of the order in which the modules are configured in the application.
290+
291+
This pattern is particularly useful when:
292+
- You want to avoid direct module instantiation
293+
- You need to configure a module differently in different parts of your application
294+
- You want to maintain loose coupling between modules
295+
270296
### Forward Reference by Class
271297

272298
In the following example, we have two modules, `ModuleA` and `ModuleB`. `ModuleB`

docs/overview/providers.md

Lines changed: 17 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,7 @@ from .controllers import CarController
111111
routers=[],
112112
)
113113
class CarModule(ModuleBase):
114-
def register_providers(self, container: Container) -> None:
115-
# for more complicated provider registrations
116-
# container.register_instance(...)
117-
pass
114+
pass
118115
```
119116

120117
By adding `CarRepository` to the list of providers, Ellar ensures that it is available for dependency injection within the `CarModule`.
@@ -159,7 +156,8 @@ a_foo_instance = AFooClass()
159156
@Module(
160157
providers=[
161158
ProviderConfig(IFoo, use_class=AFooClass),
162-
ProviderConfig(IFooB, use_value=a_foo_instance)
159+
ProviderConfig(IFooB, use_value=a_foo_instance),
160+
ProviderConfig(IFoo, use_class="path.to:AFooClass")
163161
]
164162
)
165163
class AModule(ModuleBase):
@@ -184,66 +182,7 @@ if __name__ == "__main__":
184182
validate_provider_config()
185183
```
186184

187-
In the above example, `ProviderConfig` is used as a value type for `IFooB` and as a concrete type for `IFoo`.
188-
189-
### **2. `register_providers`:**
190-
Another method is by overriding `register_services` in any Module class.
191-
192-
For example:
193-
```python
194-
# main.py
195-
196-
from ellar.common import Module
197-
from ellar.core import ModuleBase, Config
198-
from ellar.di import Container, EllarInjector, injectable
199-
from ellar.core.modules.ref import create_module_ref_factor
200-
201-
injector = EllarInjector(auto_bind=False)
202-
203-
204-
class IFoo:
205-
pass
206-
207-
208-
class IFooB:
209-
pass
210-
211-
212-
@injectable
213-
class AFooClass(IFoo, IFooB):
214-
pass
215-
216-
217-
a_foo_instance = AFooClass()
218-
219-
220-
@Module()
221-
class AModule(ModuleBase):
222-
def register_services(self, container: Container) -> None:
223-
container.register(IFoo, AFooClass)
224-
container.register(IFooB, a_foo_instance)
225-
226-
227-
def validate_register_services():
228-
module_ref = create_module_ref_factor(
229-
AModule, container=injector.container, config=Config(),
230-
)
231-
module_ref.run_module_register_services()
232-
233-
ifoo_b = injector.get(IFooB)
234-
ifoo = injector.get(IFoo)
235-
236-
assert isinstance(ifoo_b, AFooClass)
237-
assert isinstance(ifoo, AFooClass)
238-
assert ifoo_b == a_foo_instance
239-
240-
if __name__ == "__main__":
241-
validate_register_services()
242-
243-
```
244-
245-
In the illustration above, the `AModule` `register_services` method was used to register `IFoo` and `IFooB`
246-
with their respective concrete implementations.
185+
In the above example, `ProviderConfig` is used as a value type for `IFooB` and as a concrete type for `IFoo`. Also, the `use_class` argument can be used to specify the path to the class to be used as the provider which is useful when you want to lazy load the provider class.
247186

248187
## **Tagging Registered Providers**
249188

@@ -264,40 +203,23 @@ class Foo:
264203
class FooB:
265204
pass
266205

267-
injector.container.register_singleton(Foo, tag="first_foo")
268-
injector.container.register(FooB, tag="second_foo")
269206

270-
first_foo_instance = injector.get("first_foo")
271-
second_foo_instance = injector.get("second_foo")
207+
@Module(
208+
providers=[
209+
ProviderConfig(Foo, tag="first_foo"),
210+
ProviderConfig(FooB, tag="second_foo"),
211+
]
212+
)
213+
class AModule(ModuleBase):
214+
def __init__(self, foo: InjectByTag("first_foo"), foo_b: InjectByTag("second_foo")):
215+
self.foo = foo
216+
self.foo_b = foo_b
272217

273-
assert first_foo_instance is injector.get(Foo)
274-
assert isinstance(second_foo_instance, Foo)
218+
assert isinstance(self.foo, Foo)
219+
assert isinstance(self.foo_b, FooB)
275220
```
276221

277222
In the above example, we are tagging `Foo` as `first_foo` and `FooB` as `second_foo`. By doing this, we can resolve both services using their tag names, thus providing the possibility of resolving services by tag name or type.
278223

279-
Also, services can be injected as a dependency by using tags. To achieve this, the `InjectByTag` decorator is used.
280-
281-
For example:
282-
283-
```python
284-
from ellar.di import EllarInjector, InjectByTag, scopes
285-
286-
injector = EllarInjector(auto_bind=False)
287-
288-
289-
class Foo:
290-
name: str = "foo"
291-
292-
293-
class FooB:
294-
def __init__(self, foo: InjectByTag("fooTag")):
295-
self.foo = foo
296-
297-
injector.container.register(Foo, tag="fooTag", scope=scopes.singleton_scope)
298-
injector.container.register(FooB)
299-
300-
assert injector.get(FooB).foo == 'foo'
301-
```
302-
224+
Also, services can be injected as a dependency by using tags. To achieve this, the `InjectByTag` decorator is used as a `**constructor**` argument.
303225
This allows for more flexibility in managing dependencies and resolving services based on tags.

docs/quick-project.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,7 @@ from .services import CarDummyDB
220220
routers=[],
221221
)
222222
class CarModule(ModuleBase):
223-
def register_providers(self, container: Container) -> None:
224-
# for more complicated provider registrations
225-
# container.register_instance(...)
226-
pass
223+
pass
227224
```
228225

229226
## **Registering Module**

docs/techniques/staticfiles.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ from .controllers import CarController
5151
controllers=[CarController], static_folder='my_static'
5252
)
5353
class CarModule(ModuleBase):
54-
def register_providers(self, container: Container) -> None:
55-
# for more complicated provider registrations
56-
# container.register_instance(...)
57-
pass
54+
pass
5855
```
5956

6057
## **Other Static Configurations**

ellar/core/modules/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def post_build(cls, module_ref: "ModuleRefBase") -> None:
115115
"""Executed after a Subclass build process is done"""
116116

117117
def register_services(self, container: Container) -> None:
118-
"""Register other services manually"""
118+
"""This method is used to register other services manually. But its advised to use the `ProviderConfig` class instead."""
119119

120120
def configure(self, container: Binder) -> None:
121121
"""Injector Module Support. Override register_services instead"""

0 commit comments

Comments
 (0)