diff --git a/.gitignore b/.gitignore index 2eb4f17..3ac9816 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ tools.db .mytools_db .idea/MyManagingTools.iml .idea/misc.xml +.idea_bak **/*.prof # Created by .ignore support plugin (hsz.mobi) diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/src/components/jsonviewer/Readme.md b/src/components/jsonviewer/Readme.md index 720fcc6..91642a0 100644 --- a/src/components/jsonviewer/Readme.md +++ b/src/components/jsonviewer/Readme.md @@ -224,7 +224,13 @@ editable_hook = (HookBuilder() ``` ### `when_custom(condition)` -Use custom condition objects for complex logic. +Use custom condition objects or callable predicates for complex logic. + +The `when_custom()` method accepts either: +- **Condition instances**: Objects that inherit from the `Condition` base class +- **Callable predicates**: Functions that take a `HookContext` parameter and return a boolean + +When a callable is provided, it's automatically wrapped in a `PredicateCondition` class internally. ```python class BusinessLogicCondition(Condition): diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 3936085..83d0ce6 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -1,7 +1,7 @@ import pytest from components.jsonviewer.hooks import ( - HookContext, EventType, Hook, HookManager, + HookContext, EventType, Hook, HookManager, HookBuilder, WhenLongText, WhenEditable, WhenType, WhenKey, WhenPath, WhenValue, CompositeCondition ) @@ -157,3 +157,72 @@ def test_i_can_clear_hooks_in_manager(): hook_manager.clear_hooks() assert len(hook_manager.hooks) == 0 + + +# ================ +# Test HookBuilder with Callable Conditions +# ================ + +def test_i_can_use_callable_with_when_custom(): + """Test that when_custom() accepts callable predicates""" + + # Define a simple callable condition + def custom_condition(context): + return isinstance(context.get_value(), str) and context.get_value().startswith("CUSTOM_") + + # Create hook using callable condition + hook = (HookBuilder() + .on_render() + .when_custom(custom_condition) + .execute(lambda ctx: "Custom hook executed")) + + # Test with matching context + matching_context = create_mock_context(value="CUSTOM_test_value") + assert hook.matches(EventType.RENDER, matching_context) == True + assert hook.execute(matching_context) == "Custom hook executed" + + # Test with non-matching context + non_matching_context = create_mock_context(value="regular_value") + assert hook.matches(EventType.RENDER, non_matching_context) == False + + +def test_i_can_use_lambda_with_when_custom(): + """Test that when_custom() accepts lambda expressions""" + + # Create hook using lambda condition + hook = (HookBuilder() + .on_render() + .when_custom(lambda ctx: ctx.key == "special" and isinstance(ctx.get_value(), int) and ctx.get_value() > 100) + .execute(lambda ctx: f"Special value: {ctx.get_value()}")) + + # Test with matching context + matching_context = create_mock_context(value=150, key="special") + assert hook.matches(EventType.RENDER, matching_context) == True + assert hook.execute(matching_context) == "Special value: 150" + + # Test with non-matching contexts + wrong_key_context = create_mock_context(value=150, key="normal") + assert hook.matches(EventType.RENDER, wrong_key_context) == False + + wrong_value_context = create_mock_context(value=50, key="special") + assert hook.matches(EventType.RENDER, wrong_value_context) == False + + +@pytest.mark.parametrize("value, key, json_path, expected", [ + ("CUSTOM_hook_test", "test_key", "root.test", True), # Matches callable condition + ("regular_text", "test_key", "root.test", False), # Doesn't match callable condition + (123, "test_key", "root.test", False), # Wrong type +]) +def test_callable_condition_evaluation(value, key, json_path, expected): + """Test callable condition evaluation with different inputs""" + + def custom_callable_condition(context): + return isinstance(context.get_value(), str) and context.get_value().startswith("CUSTOM_") + + hook = (HookBuilder() + .on_render() + .when_custom(custom_callable_condition) + .execute(lambda ctx: "Executed")) + + context = create_mock_context(value=value, key=key, json_path=json_path) + assert hook.matches(EventType.RENDER, context) == expected