Skip to content

Commit

Permalink
refactor Component rendering by introducing BoundComponent class (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuadavidthomas authored Jan 23, 2025
1 parent fb1491d commit 3f3fddb
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 17 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/

## [Unreleased]

### Changed

- **Internal**: Refactored component rendering by introducing a new `BoundComponent` class and moving some of the rendering logic from `Component` and `BirdNode` to this new class.

### Removed

- **Internal**: Removed `Component.render` method in favor of new `BoundComponent.render` method.

## [0.11.2]

### Changed
Expand Down
31 changes: 29 additions & 2 deletions src/django_bird/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
from django.template.exceptions import TemplateDoesNotExist
from django.template.loader import select_template

from django_bird.params import Param
from django_bird.params import Params
from django_bird.params import Value
from django_bird.staticfiles import Asset

from .conf import app_settings
Expand All @@ -33,8 +36,9 @@ def get_asset(self, asset_filename: str) -> Asset | None:
return asset
return None

def render(self, context: Context):
return self.template.template.render(context)
def get_bound_component(self, attrs: list[Param]):
params = Params.with_attrs(attrs)
return BoundComponent(component=self, params=params)

@property
def id(self):
Expand Down Expand Up @@ -72,6 +76,29 @@ def from_name(cls, name: str):
return cls(name=name, template=template, assets=frozenset(assets))


@dataclass
class BoundComponent:
component: Component
params: Params

def render(self, context: Context):
if app_settings.ENABLE_BIRD_ID_ATTR:
self.params.attrs.append(
Param("data_bird_id", Value(self.component.id, True))
)

props = self.params.render_props(self.component.nodelist, context)
attrs = self.params.render_attrs(context)

with context.push(
**{
"attrs": attrs,
"props": props,
}
):
return self.component.template.template.render(context)


class ComponentRegistry:
def __init__(self, maxsize: int = 100):
self._components: LRUCache[str, Component] = LRUCache(maxsize=maxsize)
Expand Down
16 changes: 3 additions & 13 deletions src/django_bird/templatetags/tags/bird.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
from django_bird._typing import override
from django_bird.components import Component
from django_bird.components import components
from django_bird.conf import app_settings
from django_bird.params import Param
from django_bird.params import Params
from django_bird.params import Value
from django_bird.slots import DEFAULT_SLOT
from django_bird.slots import Slots

Expand Down Expand Up @@ -77,11 +74,12 @@ def render(self, context: Context) -> str:
component_name = self.get_component_name(context)
component = components.get_component(component_name)
component_context = self.get_component_context_data(component, context)
bound_component = component.get_bound_component(attrs=self.attrs)

if self.isolated_context:
return component.render(context.new(component_context))
return bound_component.render(context.new(component_context))
with context.push(**component_context):
return component.render(context)
return bound_component.render(context)

def get_component_name(self, context: Context) -> str:
try:
Expand All @@ -93,18 +91,10 @@ def get_component_name(self, context: Context) -> str:
def get_component_context_data(
self, component: Component, context: Context
) -> dict[str, Any]:
if app_settings.ENABLE_BIRD_ID_ATTR:
self.attrs.append(Param("data_bird_id", Value(component.id, True)))

params = Params.with_attrs(self.attrs)
props = params.render_props(component.nodelist, context)
attrs = params.render_attrs(context)
slots = Slots.collect(self.nodelist, context).render()
default_slot = slots.get(DEFAULT_SLOT) or context.get("slot")

return {
"attrs": attrs,
"props": props,
"slot": default_slot,
"slots": slots,
}
10 changes: 8 additions & 2 deletions tests/test_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ def test_from_name_basic(self, templates_dir):

assert comp.name == "button"
assert isinstance(comp.template, Template)
assert comp.render(Context({})) == "<button>Click me</button>"

bound = comp.get_bound_component([])

assert bound.render(Context({})) == "<button>Click me</button>"

def test_from_name_with_assets(self, templates_dir):
button = TestComponent(
Expand Down Expand Up @@ -104,7 +107,10 @@ def test_from_name_custom_component_dir(self, templates_dir, override_app_settin

assert comp.name == "button"
assert isinstance(comp.template, Template)
assert comp.render(Context({})) == "<button>Click me</button>"

bound = comp.get_bound_component([])

assert bound.render(Context({})) == "<button>Click me</button>"

def test_id_is_consistent(self, templates_dir):
button = TestComponent(
Expand Down

0 comments on commit 3f3fddb

Please sign in to comment.