Skip to content

Commit df782ec

Browse files
committed
Documentation Updates
1 parent 6852ed4 commit df782ec

34 files changed

+276
-153
lines changed

docs/cli/custom-commands.md

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,43 +39,41 @@ ARGUMENTS:
3939
```
4040

4141
## **With App Context Decorator**
42-
The `ellar_cli.click` module includes a command decorator function called `with_app_context`.
42+
The `ellar_cli.click` module includes a command decorator function called `with_injector_context`.
4343
This decorator ensures that a click command is executed within the application context,
4444
allowing `current_injector`, and `config` to have values.
4545

4646
For instance:
4747

4848
```python
4949
import ellar_cli.click as click
50-
from ellar.core import Config
51-
from ellar.app import current_injector
50+
from ellar.core import Config, current_injector
5251

5352
@click.command()
5453
@click.argument("arg1", required=True, help="Arg1 description")
55-
@click.with_app_context
54+
@click.with_injector_context
5655
def command_context(arg1):
5756
config = current_injector.get(Config)
5857
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
5958

6059
@click.command()
6160
@click.argument("arg1", required=True, help="Arg1 description")
62-
def command_wc(arg1):
61+
def command_woc(arg1):
6362
config = current_injector.get(Config)
6463
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
6564
```
6665

67-
In this example, `command_context` is wrapped with `with_app_context`, while `command_wc` is not.
66+
In this example, `command_context` is wrapped with `with_injector_context`, while `command_woc` is not.
6867
When executing both commands, `command_context` will run successfully, and `command_wc` will raise a RuntimeError
6968
because it attempts to access a value outside the context.
7069

7170
## **AppContextGroup**
72-
`AppContextGroup` extended from `click.Group` to wrap all its commands with `with_app_context` decorator.
71+
`AppContextGroup` extended from `click.Group` to wrap all its commands with `with_injector_context` decorator.
7372

7473

7574
```python
7675
import ellar_cli.click as click
77-
from ellar.core import Config
78-
from ellar.app import current_injector
76+
from ellar.core import Config, current_injector
7977

8078
cm = click.AppContextGroup(name='cm')
8179

@@ -94,37 +92,36 @@ def command_wc(arg1):
9492
```
9593
All commands registered under `cm` will be executed under within the context of the application.
9694

97-
### **Disabling `with_app_context` in AppContextGroup**
95+
### **Disabling `with_injector_context` in AppContextGroup**
9896
There are some cases where you may want to execute a command under `AppContextGroup` outside application context.
99-
This can be done by setting `with_app_context=False` as command parameter.
97+
This can be done by setting `with_injector_context=False` as command parameter.
10098

10199
```python
102100
import ellar_cli.click as click
103101

104102
cm = click.AppContextGroup(name='cm')
105103

106-
@cm.command(with_app_context=False)
104+
@cm.command(with_injector_context=False)
107105
@click.argument("arg1", required=True, help="Arg1 description")
108106
def command_wc(arg1):
109107
# config = current_injector.get(Config)
110108
print("ALLOWED_HOSTS:Unavailable;ELLAR_CONFIG_MODULE:Unavailable")
111109
```
112110

113111
## Async Command
114-
The `ellar_cli.click` package provides a utility decorator function, `run_as_async`,
112+
The `ellar_cli.click` package provides a utility decorator function, `run_as_sync`,
115113
specifically designed to execute coroutine commands.
116114
This is useful when you want to define asynchronous commands using the `click` package.
117115
Here's an example:
118116

119117
```python
120118
import ellar_cli.click as click
121-
from ellar.core import Config
122-
from ellar.app import current_injector
119+
from ellar.core import Config,current_injector
123120

124121
@click.command()
125122
@click.argument("arg1", required=True, help="Arg1 description")
126-
@click.with_app_context
127-
@click.run_as_async
123+
@click.with_injector_context
124+
@click.run_as_sync
128125
async def command_context(arg1):
129126
config = current_injector.get(Config)
130127
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
@@ -148,7 +145,7 @@ def makemigrations():
148145
"""Create DB Migration """
149146

150147
@db.command()
151-
def migrate():
148+
async def migrate():
152149
"""Applies Migrations"""
153150
```
154151

@@ -184,7 +181,7 @@ Commands:
184181

185182
```
186183

187-
Having explored various methods for crafting commands and understanding the roles of `wrap_app_context` and `run_as_async` decorators,
184+
Having explored various methods for crafting commands and understanding the roles of `with_injector_context` and `run_as_sync` decorators,
188185
you now possess the knowledge to create diverse commands for your Ellar application.
189186

190187
It's crucial to keep in mind that any custom command you develop needs to be registered within a `@Module` class, which,

docs/overview/custom_decorators.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ The **Inject[Type]** annotation is used to inject a service registered in Ellar
4545

4646
For example:
4747
```python
48+
from ellar.app import App
4849
from ellar.common import ModuleRouter, Inject
49-
from ellar.core import App, Config
50+
from ellar.core import Config
5051
from sqlalchemy.ext.asyncio import AsyncSession
5152

5253

@@ -172,7 +173,7 @@ def get_requests_case_2(
172173
## **Custom Parameter Decorators**
173174
You can create your own route parameter decorators whenever necessary. You simply need to follow a contract, `NonParameterResolver`, and override the resolve function.
174175

175-
The `NonParameterResolver` has two attribute, `type_annotation` and `parameter_name`, that are provided automatically when computing route function parameter dependencies.
176+
The `NonParameterResolver` has two attributes, `type_annotation` and `parameter_name`, that are provided automatically when computing route function parameter dependencies.
176177
The `type_annotation` and `parameter_name` are determined from the parameter declaration like so - `def route_function(parameter_name:type_annotation = NonParameterResolver())`.
177178

178179
All `NonParameterResolver` receives current `IExecutionContext` during route function execution, and it must return a tuple of dict object of the resulting resolve data with `parameter_name` and list of errors if any.
@@ -445,11 +446,13 @@ See [Ellar-CLI Custom Commands](../cli/introduction.md){target="_blank"}
445446

446447
- `@exception_handler`: This decorator is used to register a function as an exception handler. This function will be called when an unhandled exception occurs during a request. It should take the exception instance as its only argument and return a response object.
447448

448-
- `@middleware`: This decorator is used to register a function as a middleware. Middlewares are called for each incoming request and can be used to modify the request or response, or perform any other actions before or after the request is handled.
449+
- `@middleware`: This decorator is used to register a function as middleware. Middlewares are called for each incoming request and can be used to modify the request or response, or perform any other actions before or after the request is handled.
449450

450-
- `@template_filter`: This decorator is used to register a function as a Jinja2 template filter. The function should take one or more arguments and return a modified value.
451+
- `@template_filter`: This decorator is used to register a function as a Jinja2 template filter.
451452

452-
- `@template_global`: This decorator is used to register a function as a global variable available in all Jinja2 templates. The function can be called without any arguments and should return a value.
453+
- `@template_global`: This decorator is used to register a function as a global variable available in all Jinja2 templates.
453454

455+
- `@template_context`: This decorator is used to register a function as a global template context for dynamic template context processing.
456+
454457
These decorators can be used to define functions that will be executed at specific points in the application's lifecycle.
455458
They provide a way to separate and organize the different parts of an application. See [Module Additional Configuration](modules.md#additional-module-configurations){target="_blank"} for examples on how these decorator functions are used.

docs/overview/exception_handling.md

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ Service Unavailable
9191

9292

9393
## **Exception Handlers**
94-
Exception Handlers are classes or functions that handles what response that is returned to the client for specific exception types.
94+
Exception Handlers are classes or functions
95+
that handle what response that is returned to the client for specific exception types.
9596

9697
Here is an example of an ExceptionHandler that handles `HTTPException` in the application:
9798

@@ -298,38 +299,31 @@ To make the `exception_handler_fun` work as an ExceptionHandler, you will need t
298299
```python
299300
from starlette.responses import PlainTextResponse
300301
from ellar.common import IExecutionContext
301-
from ellar.common.exceptions import CallableExceptionHandler
302+
from ellar.core.exceptions import CallableExceptionHandler, as_exception_handler
302303

303304

304-
def exception_handler_fun(ctx: IExecutionContext, exc: Exception):
305+
@as_exception_handler
306+
def exception_400_handler(ctx: IExecutionContext, exc: Exception):
305307
return PlainTextResponse('Bad Request', status_code=400)
306-
307-
308-
exception_400_handler = CallableExceptionHandler(
309-
exc_class_or_status_code=400, callable_exception_handler=exception_handler_fun
310-
)
311308
```
312309
In the example above, you have created an `exception_400_handler` Exception Handler to handle HTTP exceptions with a status code of 400.
313310
You can then register it as an exception handler in your configuration, as we did in the previous section:
314311

315-
Additionally, `exception_handler_fun` can be configured to handle a custom exception type, as shown below:
312+
Additionally, We can create a handler to handle custom exception types, as shown below:
316313

317314
```python
318315
from starlette.responses import PlainTextResponse
319316
from ellar.common import IExecutionContext
320-
from ellar.common.exceptions import CallableExceptionHandler
317+
from ellar.core.exceptions import as_exception_handler
321318

322319

323320
class CustomException(Exception):
324321
pass
325322

326323

327-
def exception_handler_fun(ctx: IExecutionContext, exc: Exception):
324+
@as_exception_handler(CustomException)
325+
def exception_custom_handler(ctx: IExecutionContext, exc: Exception):
328326
return PlainTextResponse('Bad Request', status_code=400)
329327

330-
331-
exception_custom_handler = CallableExceptionHandler(
332-
exc_class_or_status_code=CustomException, callable_exception_handler=exception_handler_fun
333-
)
334328
```
335329
In this example, `exception_custom_handler` is configured to handle a custom exception type, CustomException.

docs/overview/guards.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# **Guards**
22

33
A **Guard** in Ellar is a way to add **authentication** and **authorization** checks to your application.
4-
It acts as a middleware and runs before executing the route handler. If the guard returns **false**, the request is rejected and the execution is stopped.
4+
It acts as middleware and runs before executing the route handler.
5+
If the guard returns **false**, the request is rejected and the execution is stopped.
56

67
**Guards** can be used to check for specific roles, permissions, or any other arbitrary condition.
78
They can be easily applied to individual routes or groups of routes using `@guard` decorator.

docs/overview/interceptors.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,6 @@ class InterceptCustomException(EllarInterceptor):
145145
return {"message": str(cex)}
146146
```
147147

148-
In the above code, the `InterceptCustomException` interceptor catches any `CustomException` raised during the execution of the request/response cycle. It then modifies the response object to set the status code to 400 and returns a JSON response containing the exception message. This allows for custom handling of exceptions within the interceptor before they are propagated to the system's exception handlers.
148+
In the above code, the `InterceptCustomException` interceptor catches any `CustomException` raised during the execution of the request/response cycle.
149+
It then modifies the response object to set the status code to 400 and returns a JSON response containing the exception message.
150+
This allows for custom handling of exceptions within the interceptor before they are propagated to the system's exception handlers.

docs/overview/modules.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ from ellar.core import ModuleBase
4848
modules=[],
4949
providers=[],
5050
controllers=[],
51+
exports=[],
5152
routers=[],
5253
commands=[],
5354
base_directory=None,
@@ -63,6 +64,7 @@ class BookModule(ModuleBase):
6364
| `name` | The name of the module. It's relevant for identification purposes. |
6465
| `modules` | A list of dependencies required by this module. |
6566
| `providers` | Providers to be instantiated by the Ellar injector, possibly shared across this module. |
67+
| `exports` | List of services accessible at application scope level. |
6668
| `controllers` | Controllers defined in this module that need instantiation. |
6769
| `routers` | ModuleRouters defined in this module. |
6870
| `commands` | Functions decorated with `EllarTyper` or `command` that serve as commands. |
@@ -76,19 +78,19 @@ class BookModule(ModuleBase):
7678
The Ellar framework offers additional module configurations to handle various aspects of the application lifecycle and behavior.
7779

7880
### Module Events
79-
Modules can define `before_init` class method to configure additional initialization parameters before instantiation. This method receives the current application config as a parameter, allowing for further customization.
81+
Modules can define `post_build` class method
82+
can be used to define additional `Module` properties after `Module` has built successfully.
8083

8184
```python linenums="1"
82-
import typing
8385
from ellar.common import Module
84-
from ellar.core import ModuleBase, Config
86+
from ellar.core.modules import ModuleBase, ModuleRefBase
8587

8688

8789
@Module()
8890
class ModuleEventSample(ModuleBase):
8991
@classmethod
90-
def before_init(cls, config: Config) -> typing.Any:
91-
"""Called before creating Module object"""
92+
def post_build(cls, module_ref: ModuleRefBase) -> None:
93+
"""Executed after a module build process is done"""
9294
```
9395

9496
### Module Application Cycle
@@ -127,7 +129,7 @@ from ellar.core import ModuleBase
127129
@Module()
128130
class ModuleExceptionSample(ModuleBase):
129131
@exception_handler(404)
130-
def exception_404_handler(cls, context: IHostContext, exc: Exception) -> Response:
132+
def exception_404_handler(self, context: IHostContext, exc: Exception) -> Response:
131133
return JSONResponse(dict(detail="Resource not found."))
132134
```
133135

@@ -141,15 +143,15 @@ from ellar.core import ModuleBase
141143
@Module()
142144
class ModuleTemplateFilterSample(ModuleBase):
143145
@template_filter()
144-
def double_filter(cls, n):
146+
def double_filter(self, n):
145147
return n * 2
146148

147149
@template_global()
148-
def double_global(cls, n):
150+
def double_global(self, n):
149151
return n * 2
150152

151153
@template_filter(name="dec_filter")
152-
def double_filter_dec(cls, n):
154+
def double_filter_dec(self, n):
153155
return n * 2
154156
```
155157

@@ -198,20 +200,20 @@ from ellar.core import ModuleBase
198200
@Module()
199201
class ModuleMiddlewareSample(ModuleBase):
200202
@middleware()
201-
async def my_middleware_function_1(cls, context: IHostContext, call_next):
203+
async def my_middleware_function_1(self, context: IHostContext, call_next):
202204
request = context.switch_to_http_connection().get_request() # for HTTP response only
203205
request.state.my_middleware_function_1 = True
204206
await call_next()
205207

206208
@middleware()
207-
async def my_middleware_function_2(cls, context: IHostContext, call_next):
209+
async def my_middleware_function_2(self, context: IHostContext, call_next):
208210
if context.get_type() == 'websocket':
209211
websocket = context.switch_to_websocket().get_client()
210212
websocket.state.my_middleware_function_2 = True
211213
await call_next()
212214

213215
@middleware()
214-
async def my_middleware_function_3(cls, context: IHostContext, call_next):
216+
async def my_middleware_function_3(self, context: IHostContext, call_next):
215217
connection = context.switch_to_http_connection().get_client() # for HTTP response only
216218
if connection.headers['somekey']:
217219
# response = context.get_response() -> use the `response` to add extra definitions to things you want to see on

docs/overview/providers.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ a_foo_instance = AFooClass()
220220
@Module()
221221
class AModule(ModuleBase):
222222
def register_services(self, container: Container) -> None:
223-
container.register_singleton(IFoo, AFooClass)
223+
container.register(IFoo, AFooClass)
224224
container.register(IFooB, a_foo_instance)
225225

226226

@@ -281,7 +281,7 @@ Also, services can be injected as a dependency by using tags. To achieve this, t
281281
For example:
282282

283283
```python
284-
from ellar.di import EllarInjector, InjectByTag
284+
from ellar.di import EllarInjector, InjectByTag, scopes
285285

286286
injector = EllarInjector(auto_bind=False)
287287

@@ -294,7 +294,7 @@ class FooB:
294294
def __init__(self, foo: InjectByTag("fooTag")):
295295
self.foo = foo
296296

297-
injector.container.register_singleton(Foo, tag="fooTag")
297+
injector.container.register(Foo, tag="fooTag", scope=scopes.singleton_scope)
298298
injector.container.register(FooB)
299299

300300
assert injector.get(FooB).foo == 'foo'

docs/overview/step-one.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,6 @@ The result of this CLI command is stored in `project-name/project_name/apps`
127127
```
128128
apps/
129129
├─ car/
130-
│ ├─ tests/
131-
│ │ ├─ test_controllers.py
132-
│ │ ├─ test_routers.py
133-
│ │ ├─ test_services.py
134130
│ ├─ controllers.py
135131
│ ├─ module.py
136132
│ ├─ schemas.py
@@ -145,7 +141,6 @@ Brief overview of the generated files:
145141
| `car.module.py` | car module/app `Module` metadata definition. |
146142
| `car.services.py` | For Car module service declarations. |
147143
| `car.schemas.py` | Data-transfer-object or Serializers declarations. |
148-
| `car.tests/` | testing directory for the car module. |
149144
150145
To finish up with the created `car` module, we need to register it to the
151146
`project_name.root_module.py`
@@ -179,7 +174,7 @@ then add the below.
179174
180175
import os
181176
from ellar.common.constants import ELLAR_CONFIG_MODULE
182-
from ellar.core.factory import AppFactory
177+
from ellar.app import AppFactory
183178
from ellar.openapi import OpenAPIDocumentModule, OpenAPIDocumentBuilder, SwaggerUI
184179
from .root_module import ApplicationModule
185180
@@ -197,12 +192,12 @@ document_builder.set_title('Project Name API') \
197192
.set_license('MIT Licence', url='https://www.google.com')
198193
199194
document = document_builder.build_document(application)
200-
module_config = OpenAPIDocumentModule.setup(
195+
OpenAPIDocumentModule.setup(
196+
app=application,
201197
docs_ui=SwaggerUI(),
202198
document=document,
203199
guards=[]
204200
)
205-
application.install_module(module_config)
206201
```
207202
208203
Goto your browser and visit: [http://localhost:8000/docs/](http://localhost:8000/docs){target="_blank"}

0 commit comments

Comments
 (0)