Skip to content

Commit

Permalink
Create support for calling callbacks on specific actions (#56)
Browse files Browse the repository at this point in the history
* feat(callback_middleware.py): add register method to CallbackMiddleware class to register before_request and after_request hooks

* chore(controllers.md): update callback syntax in HomeController to use dictionary format for better readability and maintainability
docs(usage.md): update form method override syntax to use `{{ method('PUT|DELETE|PATCH') }}` for better clarity and consistency
chore(__version__.py): bump up version to 2.9.0
chore(pyproject.toml): bump up version to 2.9.0
test(version_test.py): update test case to check for version 2.9.0 instead of 2.8.2

* update docs

* chore(callback_middleware.py): remove unnecessary blank lines for better code readability
  • Loading branch information
marcuxyz authored Nov 28, 2023
1 parent a4ef4ff commit 567248c
Show file tree
Hide file tree
Showing 65 changed files with 11,678 additions and 53 deletions.
6 changes: 3 additions & 3 deletions docs/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ class HomeController:
return render_template("index.html")
```

If you have a question, please, check the app directory for more details.
## Callbacks

To use the hooks as `before_request`, `after_request`, etc... Just describe it in the controller, see:
You can use the callbacks as `before_request` and `after_request` to called the function before or after request... See:

```python
class HomeController:
before_request = ["hi"]
before_request = dict(callback="hi", actions="index")

def index(self):
return "home"
Expand Down
6 changes: 3 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ class MessagesController:
<!-- app/views/messages/edit.html -->
{% block content %}
<form action="{{ url_for('messages.update', id=message.id) }}" method="POST">
<input type="hidden" name="_method" value="put">
<form action="{{ url_for('messages.update', id=message.id) }}" method="post">
{{ method('PUT') }}
<input type="text" name="title" id="title" value="Yeahh!">
<input type="submit" value="send">
</form>
{% endblock %}
```

The <input type="hidden" name="_method" value="put"> is necessary to work successfully!
You can use the `{{ method('PUT|DELETE|PATCH') }}` to creates supports for PUT and DELETE methods to forms.
2 changes: 1 addition & 1 deletion mvc_flask/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.8.2"
__version__ = "2.9.0"
16 changes: 8 additions & 8 deletions mvc_flask/middlewares/blueprint_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from flask.blueprints import Blueprint

from .hook_middleware import HookMiddleware
from .callback_middleware import CallbackMiddleware

from .http.router_middleware import RouterMiddleware as Router

Expand All @@ -18,20 +18,20 @@ def __init__(self, app: Flask, path: str) -> None:

def register(self):
for route in Router._method_route().items():
controller = route[0]
blueprint = Blueprint(controller, controller)
controller_name = route[0]
blueprint = Blueprint(controller_name, controller_name)

obj = import_module(f"{self.path}.controllers.{controller}_controller")
view_func = getattr(obj, f"{controller.title()}Controller")
instance_of_controller = view_func()
obj = import_module(f"{self.path}.controllers.{controller_name}_controller")
view_func = getattr(obj, f"{controller_name.title()}Controller")
instance_controller = view_func()

HookMiddleware().register(instance_of_controller, blueprint)
CallbackMiddleware(self.app, controller_name, instance_controller).register()

for resource in route[1]:
blueprint.add_url_rule(
rule=resource.path,
endpoint=resource.action,
view_func=getattr(instance_of_controller, resource.action),
view_func=getattr(instance_controller, resource.action),
methods=resource.method,
)

Expand Down
89 changes: 89 additions & 0 deletions mvc_flask/middlewares/callback_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from flask import Flask, request


class CallbackMiddleware:
def __init__(self, app: Flask, controller_name: str, controller) -> None:
"""
Initializes the CallbackMiddleware instance.
Parameters:
app (Flask): The Flask application where the middleware is being registered.
controller_name (str): The name of the controller where the hooks are defined.
controller: The controller instance where the hooks are defined.
"""
self.app = app
self.controller_name = controller_name
self.controller = controller

def register(self):
"""
Registers before_request and after_request hooks to the Flask application.
The before_request hook is executed before the request is processed.
The after_request hook is executed after the request is processed.
The hooks are retrieved using the get_hook_method function and executed using the execute_hook function.
"""

def before_request_hook():
hook_method, actions = self.get_hook_method("before_request")
if hook_method:
self.execute_hook(hook_method, actions)

def after_request_hook(response):
hook_method, actions = self.get_hook_method("after_request")
if hook_method:
self.execute_hook(hook_method, actions, response)
return response

self.app.before_request_funcs.setdefault(None, []).append(before_request_hook)
self.app.after_request_funcs.setdefault(None, []).append(after_request_hook)

def get_hook_method(self, hook_name):
"""
Retrieves the hook method associated with the given hook name from the controller.
Parameters:
hook_name (str): The name of the hook method to retrieve.
Returns:
tuple: A tuple containing the callback method associated with the hook and the actions to be performed.
If the hook does not exist, returns (False, False).
"""
if hasattr(self.controller, hook_name):
hook_attribute = getattr(self.controller, hook_name)
callback = hook_attribute["callback"]
actions = self.actions(hook_attribute)

return getattr(self.controller, callback), actions

return False, False

def execute_hook(self, hook_method, actions, response=None):
"""
Executes the specified hook method for each action in the provided list of actions
if the current request endpoint matches the controller and action name.
Parameters:
hook_method (function): The hook method to be executed.
actions (list): A list of action names.
response (flask.Response, optional): The response object to be passed to the hook method. Defaults to None.
"""
for action in actions:
endpoint = f"{self.controller_name}.{action}"
if request.endpoint == endpoint:
if response is None:
hook_method()
else:
hook_method(response)

def actions(self, values):
"""
Splits the actions string from the given values dictionary into a list of individual actions.
Parameters:
values (dict): A dictionary containing an "actions" key whose value is a string of action names separated by spaces.
Returns:
list: A list of individual action names.
"""
return values["actions"].split()
24 changes: 0 additions & 24 deletions mvc_flask/middlewares/hook_middleware.py

This file was deleted.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mvc-flask"
version = "2.8.2"
version = "2.9.0"
description = "turn standard Flask into mvc"
authors = ["Marcus Pereira <[email protected]>"]

Expand Down
Loading

0 comments on commit 567248c

Please sign in to comment.