From 79cdef01d078e8c20db5ab2d8cf501718047420d Mon Sep 17 00:00:00 2001 From: Felipe Sateler Date: Thu, 9 Sep 2021 10:53:58 -0300 Subject: [PATCH] Add ADR for the new feature --- .../0005-multiple-templates-per-component.md | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 docs/adrs/0005-multiple-templates-per-component.md diff --git a/docs/adrs/0005-multiple-templates-per-component.md b/docs/adrs/0005-multiple-templates-per-component.md new file mode 100644 index 0000000000..bc1b229701 --- /dev/null +++ b/docs/adrs/0005-multiple-templates-per-component.md @@ -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_`. 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_` 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.