Skip to content

Commit 567248c

Browse files
authored
Create support for calling callbacks on specific actions (#56)
* 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
1 parent a4ef4ff commit 567248c

Some content is hidden

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

65 files changed

+11678
-53
lines changed

docs/controllers.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ class HomeController:
1010
return render_template("index.html")
1111
```
1212

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

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

1717
```python
1818
class HomeController:
19-
before_request = ["hi"]
19+
before_request = dict(callback="hi", actions="index")
2020

2121
def index(self):
2222
return "home"

docs/usage.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ class MessagesController:
2828
<!-- app/views/messages/edit.html -->
2929
3030
{% block content %}
31-
<form action="{{ url_for('messages.update', id=message.id) }}" method="POST">
32-
<input type="hidden" name="_method" value="put">
31+
<form action="{{ url_for('messages.update', id=message.id) }}" method="post">
32+
{{ method('PUT') }}
3333
<input type="text" name="title" id="title" value="Yeahh!">
3434
3535
<input type="submit" value="send">
3636
</form>
3737
{% endblock %}
3838
```
3939

40-
The <input type="hidden" name="_method" value="put"> is necessary to work successfully!
40+
You can use the `{{ method('PUT|DELETE|PATCH') }}` to creates supports for PUT and DELETE methods to forms.

mvc_flask/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "2.8.2"
1+
__version__ = "2.9.0"

mvc_flask/middlewares/blueprint_middleware.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from flask.blueprints import Blueprint
55

6-
from .hook_middleware import HookMiddleware
6+
from .callback_middleware import CallbackMiddleware
77

88
from .http.router_middleware import RouterMiddleware as Router
99

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

1919
def register(self):
2020
for route in Router._method_route().items():
21-
controller = route[0]
22-
blueprint = Blueprint(controller, controller)
21+
controller_name = route[0]
22+
blueprint = Blueprint(controller_name, controller_name)
2323

24-
obj = import_module(f"{self.path}.controllers.{controller}_controller")
25-
view_func = getattr(obj, f"{controller.title()}Controller")
26-
instance_of_controller = view_func()
24+
obj = import_module(f"{self.path}.controllers.{controller_name}_controller")
25+
view_func = getattr(obj, f"{controller_name.title()}Controller")
26+
instance_controller = view_func()
2727

28-
HookMiddleware().register(instance_of_controller, blueprint)
28+
CallbackMiddleware(self.app, controller_name, instance_controller).register()
2929

3030
for resource in route[1]:
3131
blueprint.add_url_rule(
3232
rule=resource.path,
3333
endpoint=resource.action,
34-
view_func=getattr(instance_of_controller, resource.action),
34+
view_func=getattr(instance_controller, resource.action),
3535
methods=resource.method,
3636
)
3737

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from flask import Flask, request
2+
3+
4+
class CallbackMiddleware:
5+
def __init__(self, app: Flask, controller_name: str, controller) -> None:
6+
"""
7+
Initializes the CallbackMiddleware instance.
8+
9+
Parameters:
10+
app (Flask): The Flask application where the middleware is being registered.
11+
controller_name (str): The name of the controller where the hooks are defined.
12+
controller: The controller instance where the hooks are defined.
13+
"""
14+
self.app = app
15+
self.controller_name = controller_name
16+
self.controller = controller
17+
18+
def register(self):
19+
"""
20+
Registers before_request and after_request hooks to the Flask application.
21+
The before_request hook is executed before the request is processed.
22+
The after_request hook is executed after the request is processed.
23+
24+
The hooks are retrieved using the get_hook_method function and executed using the execute_hook function.
25+
"""
26+
27+
def before_request_hook():
28+
hook_method, actions = self.get_hook_method("before_request")
29+
if hook_method:
30+
self.execute_hook(hook_method, actions)
31+
32+
def after_request_hook(response):
33+
hook_method, actions = self.get_hook_method("after_request")
34+
if hook_method:
35+
self.execute_hook(hook_method, actions, response)
36+
return response
37+
38+
self.app.before_request_funcs.setdefault(None, []).append(before_request_hook)
39+
self.app.after_request_funcs.setdefault(None, []).append(after_request_hook)
40+
41+
def get_hook_method(self, hook_name):
42+
"""
43+
Retrieves the hook method associated with the given hook name from the controller.
44+
45+
Parameters:
46+
hook_name (str): The name of the hook method to retrieve.
47+
48+
Returns:
49+
tuple: A tuple containing the callback method associated with the hook and the actions to be performed.
50+
If the hook does not exist, returns (False, False).
51+
"""
52+
if hasattr(self.controller, hook_name):
53+
hook_attribute = getattr(self.controller, hook_name)
54+
callback = hook_attribute["callback"]
55+
actions = self.actions(hook_attribute)
56+
57+
return getattr(self.controller, callback), actions
58+
59+
return False, False
60+
61+
def execute_hook(self, hook_method, actions, response=None):
62+
"""
63+
Executes the specified hook method for each action in the provided list of actions
64+
if the current request endpoint matches the controller and action name.
65+
66+
Parameters:
67+
hook_method (function): The hook method to be executed.
68+
actions (list): A list of action names.
69+
response (flask.Response, optional): The response object to be passed to the hook method. Defaults to None.
70+
"""
71+
for action in actions:
72+
endpoint = f"{self.controller_name}.{action}"
73+
if request.endpoint == endpoint:
74+
if response is None:
75+
hook_method()
76+
else:
77+
hook_method(response)
78+
79+
def actions(self, values):
80+
"""
81+
Splits the actions string from the given values dictionary into a list of individual actions.
82+
83+
Parameters:
84+
values (dict): A dictionary containing an "actions" key whose value is a string of action names separated by spaces.
85+
86+
Returns:
87+
list: A list of individual action names.
88+
"""
89+
return values["actions"].split()

mvc_flask/middlewares/hook_middleware.py

Lines changed: 0 additions & 24 deletions
This file was deleted.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "mvc-flask"
3-
version = "2.8.2"
3+
version = "2.9.0"
44
description = "turn standard Flask into mvc"
55
authors = ["Marcus Pereira <marcus@negros.dev>"]
66

0 commit comments

Comments
 (0)