Skip to content

Commit

Permalink
Add ADR for the new feature
Browse files Browse the repository at this point in the history
  • Loading branch information
fsateler committed Sep 27, 2022
1 parent c8cb3d3 commit 79cdef0
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions docs/adrs/0005-multiple-templates-per-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 1. Allow multiple templates

Date: 2022/09/27

## Status

Proposed.

## Context

As components become larger (for example, because you are implementing a whole page), it becomes
useful to be able to extract sections of the view to a different file. ActionView has
partials, and ViewComponent lacks a similar mechanism.

ActionView partials have the problem that their interface isn't introspectable. Data
may be passed into the partial via ivars or locals, and it is impossible to know
which without actually opening up the file. Additionally, partials are globally
invocable, thus making it difficult to detect if a given partial is in use or not,
and who are its users.

## Considered Options

* Introduce component partials to components
* Keep components as-is

### Component partials

Introduce an API to have multiple ERB templates available within the component,
and make it possible to invoke them from the main view. This API should be as
explicit as possible, in particular it should explicitly list the arguments it
receives (if any). This allows a single method to be compiled, and invoked
directly, without having to dance around with `binding` as ActionView partials
do.

**Pros:**

* Better performance due to lack of GC pressure and object creation
* Reduces the number of components needed to express a more complex view.
* Extracted sections aren't exposed outside the component, thus reducing component library API surface.

**Cons:**

* Another concept for users of ViewComponent to learn and understand.
* Components are no longer the only way to encapsulate behavior.

### Keeping components as-is

**Pros:**

* The API remains simple and components are the only way to encapsulate behavior.
* Encourages creating reusable sub-components.

**Cons:**

* Extracting a component results in more GC and intermediate objects.
* Extracting a component may result in tightly coupled but split components.
* Creates new public components thus expanding component library API surface.

## Decision

We will allow having multiple templates in the sidecar asset. Each asset will be compiled to
it's own method `render_template_<template_name>`. The main entry point will continue to be called
`call`. To allow the compiled method to receive arguments,
the template must define them using the same syntax as
[Rails Strict Locals](https://edgeguides.rubyonrails.org/action_view_overview.html#strict-locals).
Because we don't have backwards compatibility issues, and we want to avoid the `binding` dance
to add arbitrary locals, a difference is made: a missing strict locals tag means the template
takes no arguments (that is, is equivalent to `locals: ()`).
This will create required keyword arguments to the `call_<template_name>` method.

## Consequences

This implementation has better performance characteristics over both an extracted component
and ActionView partials, because it avoids creating intermediate objects, and the overhead of
creating bindings and `instance_exec`.
Having explicit arguments makes the interface explicit.

There is no provision at the moment to allow `render(*)` to render a partial. This could be
added later if necessary and it becomes desirable.

The generated methods are only invokable via keyword arguments, inheriting the limitation
from ActionView.

The generated methods are public, and thus could be invoked by a third party. There is
no pressing need to make the methods private, and we avoid introducing new concepts
into ViewComponent.

0 comments on commit 79cdef0

Please sign in to comment.