view_component_subtemplates 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ceb9e9e6a47b06706f7464baf5f4a2f5754b10b670ae3bf28dfed61ca3068027
4
- data.tar.gz: 887ad1b1f30395592da4a3a90c78f22691715864f8e821e985c32cd4f0e1cf9a
3
+ metadata.gz: acf9c5ce1fdb01c2939e9e5737cfaf2b66a38a9540f8f24eafadf3cb473aaba5
4
+ data.tar.gz: 78bfcfbc62662ab2d5ac93a7f56a1bf4fcdd410132bf23e356c58b9a503336f9
5
5
  SHA512:
6
- metadata.gz: d00849719d594843981c36d0bf85a44fa933b31fe1e49034434e92d18a6af377d7e89391e105f623bcff3a1727620f799827775bcf6ace4aa1afb76fe76ee003
7
- data.tar.gz: fe2554a34486b1c27e3a452d5b5bc3b9830bba3677f284c5d1a5ae8cef43be5359b44f0cf4f7e01b756fa26d73f7ce9008ac22bfb2e063d623cacff30a6eada5
6
+ metadata.gz: 85de07486ae8017548b8bcef1168967cecb340bfe3060564f33fc9779b8fef323703b364b38d2bde01814d7f4eecb9295f73fc620531ebf14a529108d6a8c7dd
7
+ data.tar.gz: 34d0b0f65cac64023cb71eecb06e6001dccb9f1b2144510ea71c21acbaf19cdf13cfaff17a19840ae7be55c09ce52ac3a157810cd11726ce836ab83e0f7800c3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-06-12
4
+
5
+ ### Added
6
+ - `render_subtemplate_in(view_context, name)` to render a single subtemplate on its own, from outside the component (e.g. a controller responding with just a fragment), without rendering the main template. It is the subtemplate-level counterpart of ViewComponent's `render_in`. The subtemplate must not declare locals (per-render data is passed through the component's constructor); rendering a subtemplate with locals, or one that is not defined, raises a clear error.
7
+
3
8
  ## [0.2.0] - 2026-01-21
4
9
 
5
10
  ### Added
data/README.md CHANGED
@@ -107,6 +107,47 @@ app/components/
107
107
  <%= render TableComponent.new(users: @users, title: "User List") %>
108
108
  ```
109
109
 
110
+ ## Rendering a subtemplate on its own
111
+
112
+ Sometimes you need to render just one subtemplate, outside the component's main
113
+ template. A common case is a controller action that responds to an AJAX request
114
+ with only an updated fragment.
115
+
116
+ Call `render_subtemplate_in` on a component instance, passing the current view
117
+ context. The subtemplate **must not declare locals**: any per-render data is
118
+ passed through the component's constructor, so the call site stays free of an
119
+ untyped `**locals` boundary.
120
+
121
+ ```ruby
122
+ class UsersController < ApplicationController
123
+ def row
124
+ component = RowComponent.new(user: User.find(params[:id]), highlight: false)
125
+
126
+ render html: component.render_subtemplate_in(view_context, :row), layout: false
127
+ end
128
+ end
129
+ ```
130
+
131
+ ```erb
132
+ <%# app/components/row_component/row.html.erb -- no `locals:` line %>
133
+ <tr class="<%= 'highlighted' if @highlight %>"><td><%= @user.name %></td></tr>
134
+ ```
135
+
136
+ `render_subtemplate_in(view_context, name)` renders the named subtemplate and
137
+ returns its HTML as an html_safe string, without rendering the component's main
138
+ template. It is the subtemplate-level counterpart of ViewComponent's `render_in`:
139
+ `render_in(view_context)` is the external entry point for the main template
140
+ (internally `call`), and `render_subtemplate_in` is the external entry point for
141
+ a single subtemplate (internally `call_<name>`).
142
+
143
+ `render_subtemplate_in` raises a clear error if the named subtemplate does not
144
+ exist, or if it declares locals (pass that data through the component's
145
+ constructor instead).
146
+
147
+ **The no-locals rule applies only to standalone rendering.** Inside a component's
148
+ templates, `call_<name>` keeps taking locals exactly as shown in the Quick Start;
149
+ `render_subtemplate_in` is the only path that requires a no-locals subtemplate.
150
+
110
151
  ## Requirements
111
152
 
112
153
  - Ruby >= 3.1.0
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponentSubtemplates
4
+ # Renders one subtemplate on its own (no main template), e.g. to serve a fragment
5
+ # from a controller. The subtemplate-level counterpart of `render_in`.
6
+ # Requires a no-locals subtemplate: per-render data goes through the constructor.
7
+ module StandaloneRenderer
8
+ # @param view_context [ActionView::Base] inside a controller, this is `view_context`.
9
+ # @param name [Symbol, String] subtemplate name, without the `call_` prefix.
10
+ # @return [String] the subtemplate's HTML (html_safe).
11
+ def render_subtemplate_in(view_context, name)
12
+ self.class.__vc_compile(raise_errors: true)
13
+ ensure_renderable_subtemplate!(name)
14
+
15
+ # Set up the view context the helper guards require. `__vc_original_view_context`
16
+ # makes `helpers` reuse it instead of building a new one (keep both).
17
+ @view_context = view_context
18
+ self.__vc_original_view_context = view_context
19
+
20
+ public_send("call_#{name}")
21
+ end
22
+
23
+ private
24
+
25
+ def ensure_renderable_subtemplate!(name)
26
+ call_method = "call_#{name}"
27
+ unless respond_to?(call_method)
28
+ raise ViewComponentSubtemplates::Error,
29
+ "Subtemplate `#{name}` is not defined on #{self.class}. " \
30
+ "Available subtemplates: #{available_subtemplates.join(", ").presence || "(none)"}."
31
+ end
32
+
33
+ return if method(call_method).parameters.empty?
34
+
35
+ raise ViewComponentSubtemplates::Error,
36
+ "Subtemplate `#{name}` declares locals; standalone rendering requires a subtemplate " \
37
+ "without locals. Pass per-render data through the component's constructor instead."
38
+ end
39
+
40
+ def available_subtemplates
41
+ methods.grep(/\Acall_(.+)\z/) { Regexp.last_match(1) }
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ViewComponentSubtemplates
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -37,8 +37,10 @@ end
37
37
 
38
38
  # Load SubTemplate after the module is fully configured
39
39
  require_relative "view_component_subtemplates/sub_template"
40
+ require_relative "view_component_subtemplates/standalone_renderer"
40
41
 
41
42
  # Hook into ViewComponent when it loads (lazy loading for faster boot in development)
42
43
  ActiveSupport.on_load(:view_component) do
43
44
  ViewComponent::Base.singleton_class.prepend(ViewComponentSubtemplates::AfterCompileHook)
45
+ ViewComponent::Base.include(ViewComponentSubtemplates::StandaloneRenderer)
44
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component_subtemplates
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - jsolas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-01-21 00:00:00.000000000 Z
11
+ date: 2026-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -53,6 +53,7 @@ files:
53
53
  - Rakefile
54
54
  - lib/view_component_subtemplates.rb
55
55
  - lib/view_component_subtemplates/compiler_extension.rb
56
+ - lib/view_component_subtemplates/standalone_renderer.rb
56
57
  - lib/view_component_subtemplates/sub_template.rb
57
58
  - lib/view_component_subtemplates/version.rb
58
59
  homepage: https://github.com/bukhr/view_component_subtemplates