Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New-hook-post_save_post #69

Merged
merged 7 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions example/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,3 @@

# Required for django-debug-toolbar
INTERNAL_IPS = ["127.0.0.1"]

DJPRESS_SETTINGS = {
"PLUGINS": ["djpress_example_plugin"],
"PLUGIN_SETTINGS": {
"djpress_example_plugin": {
"pre_text": "Hello, this text is configurable!",
"post_text": "Goodbye, this text is configurable!",
},
},
}
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "djpress"
version = "0.12.0"
version = "0.12.1"
description = "A blog application for Django sites, inspired by classic WordPress."
readme = "README.md"
requires-python = ">=3.10"
Expand Down Expand Up @@ -96,7 +96,7 @@ include = ["src/djpress/*"]
omit = ["*/tests/*", "*/migrations/*"]

[tool.bumpver]
current_version = "0.12.0"
current_version = "0.12.1"
version_pattern = "MAJOR.MINOR.PATCH"
commit_message = "👍 bump version {old_version} -> {new_version}"
commit = true
Expand Down
2 changes: 1 addition & 1 deletion src/djpress/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""djpress module."""

__version__ = "0.12.0"
__version__ = "0.12.1"
4 changes: 4 additions & 0 deletions src/djpress/models/post.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ def save(self: "Post", *args, **kwargs) -> None: # noqa: ANN002, ANN003
self.full_clean()
super().save(*args, **kwargs)

# If the post is a post and it's published, run the post_save_post hook
if self.post_type == "post" and self.is_published:
registry.run_hook(Hooks.POST_SAVE_POST, post=self)

def clean(self) -> None:
"""Custom validation for the Post model."""
# Check for circular references in the page hierarchy
Expand Down
15 changes: 14 additions & 1 deletion src/djpress/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ class Hooks(Enum):
PRE_RENDER_CONTENT = "pre_render_content"
POST_RENDER_CONTENT = "post_render_content"

POST_SAVE_POST = "post_save_post"

@property
def no_return(self) -> bool:
"""Whether this hook should return a value."""
return self in {
self.POST_SAVE_POST,
}


class PluginRegistry:
"""A registry for plugins.
Expand Down Expand Up @@ -72,9 +81,13 @@ def run_hook(self, hook_name: Hooks, value: Any = None, *args: Any, **kwargs: An
self.load_plugins()

if hook_name in self.hooks:
if hook_name.no_return:
for callback in self.hooks[hook_name]:
callback(*args, **kwargs)
return None

for callback in self.hooks[hook_name]:
value = callback(value, *args, **kwargs)

return value

def load_plugins(self) -> None:
Expand Down
95 changes: 95 additions & 0 deletions tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,98 @@ def callback2(content: str) -> str:
# Verify both callbacks are executed in order
result = registry.run_hook(Hooks.PRE_RENDER_CONTENT, "test")
assert result == "test12"


def test_content_modification_hook(clean_registry):
"""Test hooks that modify and return content."""

def test_callback(content: str) -> str:
return content + " modified"

registry.register_hook(Hooks.PRE_RENDER_CONTENT, test_callback)
result = registry.run_hook(Hooks.PRE_RENDER_CONTENT, "test")
assert result == "test modified"


def test_signal_hook(clean_registry):
"""Test hooks that don't return values (signal-like hooks)."""
calls = []

def test_callback(post=None):
calls.append(post)

# Create a mock post
class MockPost:
title = "Test Post"

post = MockPost()

registry.register_hook(Hooks.POST_SAVE_POST, test_callback)
result = registry.run_hook(Hooks.POST_SAVE_POST, post=post)

assert result is None # Signal hooks return None
assert len(calls) == 1 # Callback was called
assert calls[0] == post # Callback received the post object


def test_multiple_signal_hooks(clean_registry):
"""Test multiple callbacks for signal-like hooks."""
calls = []

def callback1(post=None):
calls.append(f"callback1: {post.title}")

def callback2(post=None):
calls.append(f"callback2: {post.title}")

class MockPost:
title = "Test Post"

post = MockPost()

registry.register_hook(Hooks.POST_SAVE_POST, callback1)
registry.register_hook(Hooks.POST_SAVE_POST, callback2)

registry.run_hook(Hooks.POST_SAVE_POST, post=post)

assert len(calls) == 2
assert calls == ["callback1: Test Post", "callback2: Test Post"]


def test_hook_type_properties():
"""Test that hooks correctly identify their type."""
assert Hooks.PRE_RENDER_CONTENT.no_return is False
assert Hooks.POST_RENDER_CONTENT.no_return is False
assert Hooks.POST_SAVE_POST.no_return is True


def test_mixed_hook_types(clean_registry):
"""Test that different hook types work together."""
content_calls = []
signal_calls = []

def content_callback(content: str) -> str:
content_calls.append(content)
return content + " modified"

def signal_callback(post=None):
signal_calls.append(post.title)

class MockPost:
title = "Test Post"

post = MockPost()

# Register both types of hooks
registry.register_hook(Hooks.PRE_RENDER_CONTENT, content_callback)
registry.register_hook(Hooks.POST_SAVE_POST, signal_callback)

# Test content modification hook
content_result = registry.run_hook(Hooks.PRE_RENDER_CONTENT, "test")
assert content_result == "test modified"
assert content_calls == ["test"]

# Test signal hook
signal_result = registry.run_hook(Hooks.POST_SAVE_POST, post=post)
assert signal_result is None
assert signal_calls == ["Test Post"]
Loading