Skip to content

Commit 4c004fb

Browse files
authored
Merge pull request #123 from python-ellar/intro_doc_change
Doc Updates
2 parents 692cb58 + f57369a commit 4c004fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2300
-322
lines changed

Makefile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ clean: ## Removing cached python compiled files
99
find . -name \*pyo | xargs rm -fv
1010
find . -name \*~ | xargs rm -fv
1111
find . -name __pycache__ | xargs rm -rfv
12-
ruff --clean
1312

1413
install: ## Install dependencies
1514
flit install --deps develop --symlink
@@ -33,8 +32,7 @@ test: ## Run tests
3332
test-cov: ## Run tests with coverage
3433
pytest --cov=ellar --cov-report term-missing
3534

36-
doc-deploy: ## Run Deploy Documentation
37-
make clean
35+
doc-deploy:clean ## Run Deploy Documentation
3836
mkdocs gh-deploy --force --ignore-version
3937

4038
doc-serve: ## Launch doc local server

README.md

Lines changed: 62 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -43,244 +43,90 @@ Ellar is based on [Starlette (ASGI toolkit)](https://www.starlette.io/), a light
4343
- Pydantic
4444

4545
## **Installation**
46-
### Poetry Installation
47-
For [Poetry](https://python-poetry.org/) usages
48-
49-
```shell
50-
poetry add ellar-cli
51-
```
52-
53-
### Pip Installation
54-
For normal pip installation
55-
```shell
56-
pip install ellar-cli
57-
```
58-
59-
## **Creating a project**
60-
To create an ellar project, you need to have a `pyproject.toml` available on your root directory.
61-
This is necessary for ellar to store some `metadata` about your project.
62-
63-
For Pip Users, you need to create `pyproject.toml` file
64-
```shell
65-
ellar new carsite
66-
```
67-
If you are using `Poetry`, at your project root directory with `pyproject.toml`,
68-
run the ellar create project cli command,
69-
```shell
70-
ellar create-project carsite
71-
```
72-
73-
## **Run your project**
74-
Ellar runs [UVICORN - ASGI Server](https://www.uvicorn.org/) under the hood.
75-
```shell
76-
ellar runserver --reload
77-
```
78-
`--reload` is to watch for file changes
79-
80-
Now go to [http://127.0.0.1:8000](http://127.0.0.1:8000)
81-
![Swagger UI](https://python-ellar.github.io/ellar/img/ellar_framework.png)
82-
83-
For more info on Ellar CLI, click [here](https://github.com/python-ellar/ellar-cli)
84-
85-
## **Adding a project module**
86-
A project module is a project app defining a group of controllers or services including templates and static files.
87-
So, now we have a project created, lets add an app to the project.
8846
```shell
89-
ellar create-module car
47+
$(venv) pip install ellar
9048
```
9149

92-
## **Add Schema**
93-
In `car/schema.py`, lets add some serializer for car input and output data
50+
## **Try This**
9451
```python
95-
from ellar.common import Serializer
52+
import uvicorn
53+
from ellar.common import Body, Controller, ControllerBase, delete, get, post, put, Serializer, Provide
54+
from ellar.core import AppFactory
55+
from ellar.di import injectable, request_scope
56+
from ellar.openapi import OpenAPIDocumentModule, OpenAPIDocumentBuilder, SwaggerDocumentGenerator
57+
from pydantic import Field
58+
from pathlib import Path
59+
9660

97-
class CarSerializer(Serializer):
61+
class CreateCarSerializer(Serializer):
9862
name: str
63+
year: int = Field(..., gt=0)
9964
model: str
100-
brand: str
101-
102-
103-
class RetrieveCarSerializer(CarSerializer):
104-
pk: str
105-
```
106-
107-
## **Add Services**
108-
In `car/services.py`, lets create a dummy repository `CarDummyDB` to manage our car data.
109-
```python
110-
import typing as t
111-
import uuid
112-
from ellar.di import injectable, singleton_scope
113-
11465

115-
@injectable(scope=singleton_scope)
116-
class CarDummyDB:
117-
class CarDummyDBItem:
118-
pk: str
11966

120-
def __init__(self, **data: t.Dict) -> None:
121-
self.__dict__ = data
122-
123-
def __eq__(self, other):
124-
if isinstance(other, CarDummyDB.CarDummyDBItem):
125-
return self.pk == other.pk
126-
return self.pk == str(other)
127-
128-
def __init__(self) -> None:
129-
self._data: t.List[CarDummyDB.CarDummyDBItem] = []
130-
131-
def add_car(self, data: t.Dict) -> str:
132-
pk = uuid.uuid4()
133-
_data = dict(data)
134-
_data.update(pk=str(pk))
135-
item = self.CarDummyDBItem(**_data)
136-
self._data.append(item)
137-
return item.pk
138-
139-
def list(self) -> t.List["CarDummyDB.CarDummyDBItem"]:
140-
return self._data
141-
142-
def update(self, car_id: str, data: t.Dict) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
143-
idx = self._data.index(car_id)
144-
if idx >= 0:
145-
_data = dict(data)
146-
_data.update(pk=str(car_id))
147-
self._data[idx] = self.CarDummyDBItem(**_data)
148-
return self._data[idx]
149-
150-
def get(self, car_id: str) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
151-
idx = self._data.index(car_id)
152-
if idx >= 0:
153-
return self._data[idx]
154-
155-
def remove(self, car_id: str) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
156-
idx = self._data.index(car_id)
157-
if idx >= 0:
158-
return self._data.pop(idx)
159-
```
160-
## **Add Controller**
161-
In `car/controllers.py`, lets create `CarController`
162-
163-
```python
164-
import typing as t
165-
from ellar.common import Controller, delete, get, put, post, ControllerBase
166-
from ellar.common.exceptions import NotFound
167-
from .schemas import CarSerializer, RetrieveCarSerializer
168-
from .services import CarDummyDB
67+
@injectable(scope=request_scope)
68+
class CarService:
69+
def __init__(self):
70+
self.detail = 'a service'
16971

17072

17173
@Controller
172-
class CarController(ControllerBase):
173-
def __init__(self, db: CarDummyDB) -> None:
174-
self.car_db = db
175-
176-
@post("/create", response={200: str})
177-
async def create_cat(self, payload: CarSerializer):
178-
pk = self.car_db.add_car(payload.dict())
179-
return pk
180-
181-
@put("/{car_id:str}", response={200: RetrieveCarSerializer})
182-
async def update_cat(self, car_id: str, payload: CarSerializer):
183-
car = self.car_db.update(car_id, payload.dict())
184-
if not car:
185-
raise NotFound("Item not found")
186-
return car
187-
188-
@get("/{car_id:str}", response={200: RetrieveCarSerializer})
189-
async def get_car_by_id(self, car_id: str):
190-
car = self.car_db.get(car_id)
191-
if not car:
192-
raise NotFound('Item not found.')
193-
return car
194-
195-
@delete("/{car_id:str}", response={204: dict})
196-
async def deleted_cat(self, car_id: str):
197-
car = self.car_db.remove(car_id)
198-
if not car:
199-
raise NotFound('Item not found.')
200-
return 204, {}
201-
202-
@get("/", response={200: t.List[RetrieveCarSerializer]})
203-
async def list(self):
204-
return self.car_db.list()
205-
206-
```
207-
## **Register Service and Controller**
208-
In `car/module.py`, lets register `CarController` and `CarDummyDB`
209-
210-
```python
211-
from ellar.common import Module
212-
from ellar.core import ModuleBase
213-
from ellar.di import Container
214-
215-
from .controllers import CarController
216-
from .services import CarDummyDB
217-
218-
219-
@Module(
220-
controllers=[CarController],
221-
providers=[CarDummyDB],
222-
routers=[],
223-
)
224-
class CarModule(ModuleBase):
225-
def register_providers(self, container: Container) -> None:
226-
# for more complicated provider registrations
227-
# container.register_instance(...)
228-
pass
229-
```
230-
231-
## **Registering Module**
232-
Ellar is not aware of `CarModule` yet, so we need to add it to the `modules` list of `ApplicationModule` at the `carsite/root_module.py`.
233-
```python
234-
from ellar.common import Module, exception_handler, JSONResponse, Response, IHostContext
235-
from ellar.core import ModuleBase
236-
237-
from ellar.samples.modules import HomeModule
238-
from .car.module import CarModule
239-
240-
241-
@Module(modules=[HomeModule, CarModule])
242-
class ApplicationModule(ModuleBase):
243-
@exception_handler(404)
244-
def exception_404_handler(cls, context: IHostContext, exc: Exception) -> Response:
245-
return JSONResponse(dict(detail="Resource not found."))
246-
247-
```
248-
## **Enabling OpenAPI Docs**
249-
To start up openapi, we need to go back to project folder in the `server.py`
250-
then add the following below.
251-
```python
252-
import os
253-
254-
from ellar.constants import ELLAR_CONFIG_MODULE
255-
from ellar.core import AppFactory
256-
from ellar.openapi import OpenAPIDocumentModule, OpenAPIDocumentBuilder, SwaggerDocumentGenerator
257-
from .root_module import ApplicationModule
258-
259-
application = AppFactory.create_from_app_module(
260-
ApplicationModule,
261-
config_module=os.environ.get(
262-
ELLAR_CONFIG_MODULE, "carsite.config:DevelopmentConfig"
263-
),
74+
class MotoController(ControllerBase):
75+
def __init__(self, service: CarService):
76+
self._service = service
77+
78+
@post()
79+
async def create(self, payload: CreateCarSerializer = Body()):
80+
assert self._service.detail == 'a service'
81+
result = payload.dict()
82+
result.update(message='This action adds a new car')
83+
return result
84+
85+
@put('/{car_id:str}')
86+
async def update(self, car_id: str, payload: CreateCarSerializer = Body()):
87+
result = payload.dict()
88+
result.update(message=f'This action updated #{car_id} car resource')
89+
return result
90+
91+
@get('/{car_id:str}')
92+
async def get_one(self, car_id: str, service: CarService = Provide()):
93+
assert self._service == service
94+
return f"This action returns a #{car_id} car"
95+
96+
@delete('/{car_id:str}')
97+
async def delete(self, car_id: str):
98+
return f"This action removes a #{car_id} car"
99+
100+
101+
app = AppFactory.create_app(
102+
controllers=[MotoController],
103+
providers=[CarService],
104+
base_directory=str(Path(__file__).parent),
105+
config_module=dict(REDIRECT_SLASHES=True),
106+
template_folder='templates'
264107
)
265-
266108
document_builder = OpenAPIDocumentBuilder()
267-
document_builder.set_title('CarSite API') \
268-
.set_version('1.0.0') \
269-
.set_contact(name='Eadwin', url='https://www.yahoo.com', email='eadwin@gmail.com') \
109+
document_builder.set_title('Ellar API') \
110+
.set_version('1.0.2') \
111+
.set_contact(name='Author', url='https://www.yahoo.com', email='author@gmail.com') \
270112
.set_license('MIT Licence', url='https://www.google.com')
271113

272-
document = document_builder.build_document(application)
114+
document = document_builder.build_document(app)
273115
module = OpenAPIDocumentModule.setup(
274-
document=document,
275116
document_generator=SwaggerDocumentGenerator(),
117+
document=document,
276118
guards=[]
277119
)
278-
application.install_module(module)
120+
app.install_module(module)
121+
122+
123+
if __name__ == "__main__":
124+
uvicorn.run("main:app", port=5000, reload=True)
279125
```
280126

281-
Now we can test our API at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs#/)
282-
Please ensure your server is running
283-
![Swagger UI](https://python-ellar.github.io/ellar/img/car_api.png)
127+
Now we can test our API at [http://127.0.0.1:5000/docs](http://127.0.0.1:5000/docs#/)
128+
129+
You can also try the [quick-project](https://python-ellar.github.io/ellar/quick-project/) setup to get a good idea of the library.
284130

285131
## **HTML Templating**
286132
Ellar has built-in support for Jinja2, which is a popular template engine for HTML. This feature allows for easy and efficient HTML templating similar to that of Flask. Jinja2 can be used to create reusable templates, and to insert dynamic data into HTML pages. It also has support for template inheritance, control structures, and other useful features that can help to simplify and streamline the process of creating HTML templates.

docs/cli/custom-commands.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,25 @@ Commands:
8787
runserver - Starts Uvicorn Server -
8888
say-hi
8989
```
90+
91+
## **Using Click Commands**
92+
If prefer click commands, Ellar-CLI supports that too. Simply create a click command and register it to any module registered in
93+
the `ApplicationModule`. For example
94+
95+
```python
96+
import click
97+
from ellar.common import JSONResponse, Module, Response, exception_handler
98+
from ellar.core import ModuleBase
99+
from ellar.core.connection import Request
100+
101+
@click.command()
102+
def say_hello():
103+
click.echo("Hello from ellar.")
104+
105+
106+
@Module(commands=[say_hello])
107+
class ApplicationModule(ModuleBase):
108+
@exception_handler(404)
109+
def exception_404_handler(cls, request: Request, exc: Exception) -> Response:
110+
return JSONResponse({"detail": "Resource not found."})
111+
```

0 commit comments

Comments
 (0)