slotify 0.0.5 → 0.1.1
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 +4 -4
- data/README.md +318 -127
- data/lib/slotify/error.rb +2 -8
- data/lib/slotify/extensions/base.rb +5 -5
- data/lib/slotify/extensions/partial_renderer.rb +5 -5
- data/lib/slotify/extensions/template.rb +1 -1
- data/lib/slotify/{partial.rb → slots.rb} +3 -3
- data/lib/slotify/version.rb +1 -1
- data/lib/slotify.rb +1 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ab98a9cc284edb88505a7e87f06b10f57d33e9e531387b3bf2eb184c2f543f5
|
4
|
+
data.tar.gz: 96635ec015c16ebca358ad031b8f8bf5e4c3c253102f94d8730cb894a252d3f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df1dee1e6d19c450efdbfe4cffac050b7d4f13dd76d25ebbfcbfb2ddc187373e4c597fd1902c8e006077167a6f2919eee7e00d064dee75556d20ee0929886d1c
|
7
|
+
data.tar.gz: e05a1dc621cb9bdc7b8e0c269bfd472cb7790edc2d94a8c8f9aee7f714b0a3ff52cfa714cc2c041afdc2cfa33245b0faf036172c3b742399bc19e8df339164f6
|
data/README.md
CHANGED
@@ -5,15 +5,15 @@
|
|
5
5
|
|
6
6
|
## Superpowered slots for ActionView partials
|
7
7
|
|
8
|
-
Slotify
|
8
|
+
Slotify brings a ViewComponent-style **content slot API** to ActionView partials.
|
9
9
|
|
10
|
-
Slots are a convenient way to pass blocks of content in to a partial without having to resort to ugly `<% capture do ... end %>` workarounds or unscoped (global) `<% content_for :foo %>` declarations.
|
10
|
+
Slots are a convenient way to pass blocks of content in to a partial without having to resort to ugly `<% capture do ... end %>` workarounds or unscoped (global) `<% content_for :foo %>` declarations.
|
11
11
|
|
12
12
|
Slotified partials are a great way to build components in a Rails app without the additional overhead and learning curve of libraries like [ViewComponent](https://viewcomponent.org/) or [Phlex](https://www.phlex.fun/).
|
13
13
|
|
14
|
-
> [!
|
15
|
-
> Slotify is
|
16
|
-
The documentation is still
|
14
|
+
> [!WARNING]
|
15
|
+
> Slotify is in an early stage of development and has not been properly battle-tested yet.
|
16
|
+
The documentation is still a work-in-progress and the API may change between point releases until it reaches `v1.0`.
|
17
17
|
|
18
18
|
###
|
19
19
|
|
@@ -25,13 +25,12 @@ Slotify slots are defined using a **[strict locals](https://guides.rubyonrails.o
|
|
25
25
|
<%# slots: (title:, body: nil, theme: "default") -%>
|
26
26
|
```
|
27
27
|
|
28
|
-
Slot content is accessed via **standard local variables** within the partial. So a simple, slot-enabled `
|
28
|
+
Slot content is accessed via **standard local variables** within the partial. So a simple, slot-enabled `example` partial template might look something like this:
|
29
29
|
|
30
30
|
```erb
|
31
|
-
|
31
|
+
# views/examples/_example.html.erb
|
32
32
|
|
33
33
|
<%# slots: (title: "Default title", body: nil) -%>
|
34
|
-
|
35
34
|
<article>
|
36
35
|
<h1><%= title %></h1>
|
37
36
|
<% if body.present? %>
|
@@ -50,7 +49,9 @@ When the partial is rendered, a special `partial` object is yielded as an argume
|
|
50
49
|
For example, here our `article` partial is being rendered with content for the `title` and `body` slots that were defined above:
|
51
50
|
|
52
51
|
```erb
|
53
|
-
|
52
|
+
# views/examples/show.html.erb
|
53
|
+
|
54
|
+
<%= render "example" do |partial| %>
|
54
55
|
<% partial.with_title "This is a title" %>
|
55
56
|
<% partial.with_body do %>
|
56
57
|
<p>You can use <%= tag.strong "markup" %> within slot content blocks without
|
@@ -68,10 +69,10 @@ But this example just scratches the surface of what Slotify slots can do. Have a
|
|
68
69
|
<summary><h4>More full-featured example</h4></summary>
|
69
70
|
|
70
71
|
```erb
|
71
|
-
|
72
|
+
# views/examples/_example.html.erb
|
72
73
|
|
73
74
|
<%# locals: (id:) -%>
|
74
|
-
<%# slots: (title: "Example title", lists:
|
75
|
+
<%# slots: (title: "Example title", lists: [], quotes: [], website_link:) -%>
|
75
76
|
|
76
77
|
<%= tag.section id: do %>
|
77
78
|
<h1 class="example-title">
|
@@ -95,10 +96,10 @@ But this example just scratches the surface of what Slotify slots can do. Have a
|
|
95
96
|
```
|
96
97
|
|
97
98
|
```erb
|
98
|
-
|
99
|
+
# views/examples/_list.html.erb
|
99
100
|
|
100
101
|
<%# locals: (title:) -%>
|
101
|
-
<%# slots: (items:
|
102
|
+
<%# slots: (items: []) -%>
|
102
103
|
|
103
104
|
<h3><%= title %></h3>
|
104
105
|
|
@@ -110,7 +111,7 @@ But this example just scratches the surface of what Slotify slots can do. Have a
|
|
110
111
|
```
|
111
112
|
|
112
113
|
```erb
|
113
|
-
|
114
|
+
# views/examples/show.html.erb
|
114
115
|
|
115
116
|
<%= render "example", id: "slotify-example" do |partial| %>
|
116
117
|
<% partial.with_subtitle do %>
|
@@ -156,13 +157,13 @@ Required slots are defined without a default value.
|
|
156
157
|
If no content is provided for a required slot then a `StrictSlotsError` exception will be raised.
|
157
158
|
|
158
159
|
```erb
|
159
|
-
|
160
|
+
# views/examples/_required.html.erb
|
160
161
|
|
161
162
|
<%# slots: (title:) -%>
|
162
163
|
<h1><%= title %></h1>
|
163
|
-
```
|
164
164
|
|
165
|
-
|
165
|
+
# views/examples/show.html.erb
|
166
|
+
|
166
167
|
<%= render "required" do |partial| %>
|
167
168
|
<!-- ❌ raises an error, no content set for the `title` slot -->
|
168
169
|
<% end %>
|
@@ -177,36 +178,9 @@ the default value will be used instead.
|
|
177
178
|
<%# slots: (title: "Default title", author: nil) -%>
|
178
179
|
```
|
179
180
|
|
180
|
-
###
|
181
|
-
|
182
|
-
Strict locals can be defined in 'slotified' partial templates in the same way as usual,
|
183
|
-
either above or below the `slots` definition.
|
184
|
-
|
185
|
-
```erb
|
186
|
-
<!-- _article.html.erb -->
|
187
|
-
|
188
|
-
<%# locals: (title:) -%>
|
189
|
-
<%# slots: (body: "No content available") -%>
|
190
|
-
|
191
|
-
<article>
|
192
|
-
<h1><%= title %></h1>
|
193
|
-
<div><%= body %></div>
|
194
|
-
</article>
|
195
|
-
```
|
181
|
+
### Passing content to slots
|
196
182
|
|
197
|
-
|
198
|
-
|
199
|
-
```erb
|
200
|
-
<%= render "article", title: "Article title here" do |partial| %>
|
201
|
-
<% partial.with_body do %>
|
202
|
-
<p>Body content here...</p>
|
203
|
-
<% end %>
|
204
|
-
<% end %>
|
205
|
-
```
|
206
|
-
|
207
|
-
### Setting slot values
|
208
|
-
|
209
|
-
Content is passed into slots using dynamically generated `partial#with_<slot_name>` writer methods.
|
183
|
+
Content is passed to slots by calling the appropriate `.with_<slot_name>` writer method on the argument yielded to the block when rendering the partial.
|
210
184
|
|
211
185
|
Content can be provided as either the **first argument** or **as a block** when calling these methods at render time.
|
212
186
|
The following two examples are equivalent:
|
@@ -215,9 +189,7 @@ The following two examples are equivalent:
|
|
215
189
|
<%= render "example" do |partial| %>
|
216
190
|
<% partial.with_title "Title passed as argument" %>
|
217
191
|
<% end %>
|
218
|
-
```
|
219
192
|
|
220
|
-
```erb
|
221
193
|
<%= render "example" do |partial| %>
|
222
194
|
<% partial.with_title do %>
|
223
195
|
Title passed as block content
|
@@ -242,34 +214,40 @@ The slot value writer methods also accept optional arbitrary keyword arguments.
|
|
242
214
|
These can then be accessed in the partial template via the `.options` method on the slot variable.
|
243
215
|
|
244
216
|
```erb
|
217
|
+
# views/examples/show.html.erb
|
218
|
+
|
245
219
|
<%= render "example" do |partial| %>
|
246
220
|
<% partial.with_title "The title", class: "color-hotpink", data: {controller: "fancy-title"} %>
|
247
221
|
<% end %>
|
248
|
-
```
|
249
222
|
|
250
|
-
|
251
|
-
<%# slots: (title:) -%>
|
223
|
+
# views/examples/_example.html.erb
|
252
224
|
|
253
|
-
|
254
|
-
<%= title %>
|
225
|
+
<%# slots: (title:) -%>
|
226
|
+
<%= title.options.keys %> ➡️ [:class, :data]
|
227
|
+
<%= title %> ➡️ "The title"
|
255
228
|
```
|
256
229
|
|
257
230
|
Slot options can be useful for providing tag attributes when rendering slot content or rendering variants
|
258
231
|
of a slot based on an option value.
|
259
232
|
|
233
|
+
```erb
|
234
|
+
<%= tag.h1 **title.options %><%= title %><% end %>
|
235
|
+
➡️ <h1 class="color-hotpink" data-controller="fancy-title">The title</h1>
|
236
|
+
```
|
237
|
+
|
260
238
|
When rendered as a string the options are passed through the Rails `tag.attributes` helper to generate an HTML tag attributes string:
|
261
239
|
|
262
240
|
```erb
|
263
241
|
<h1 <%= title.options %>><%= title %></h1>
|
264
|
-
|
242
|
+
➡️ <h1 class="color-hotpink" data-controller="fancy-title">The title</h1>
|
265
243
|
```
|
266
244
|
|
267
|
-
###
|
245
|
+
### Single- vs multi-value slots
|
268
246
|
|
269
247
|
There are two types of slots.
|
270
248
|
|
271
|
-
* **Single-value** slots can only be called **once**
|
272
|
-
* **Multi-value** slots can be called **many times**
|
249
|
+
* **Single-value** slots can only be called **once** when rendering the partial. The corresponding variable in the template represents a single slot value.
|
250
|
+
* **Multi-value** slots can be called **many times** when rendering the partial. The corresponding variable in the template represents **a collection of slot values**.
|
273
251
|
|
274
252
|
#### Single-value slots
|
275
253
|
|
@@ -283,15 +261,17 @@ Single-value slots can be called once (at most)
|
|
283
261
|
and their corresponding template variable represents a single value:
|
284
262
|
|
285
263
|
```erb
|
264
|
+
# views/examples/show.html.erb
|
265
|
+
|
286
266
|
<%= render "example" do |partial| %>
|
287
267
|
<% partial.with_item "Item one" %>
|
288
268
|
<% end %>
|
289
|
-
```
|
290
269
|
|
291
|
-
|
270
|
+
# views/examples/_example.html.erb
|
271
|
+
|
292
272
|
<%# slots: (item: nil) -%>
|
293
273
|
<div>
|
294
|
-
<%= item %>
|
274
|
+
<%= item %> ➡️ "Item one"
|
295
275
|
</div>
|
296
276
|
```
|
297
277
|
|
@@ -301,7 +281,7 @@ and their corresponding template variable represents a single value:
|
|
301
281
|
> ```erb
|
302
282
|
> <%= render "example" do |partial| %>
|
303
283
|
> <% partial.with_item "Item one" %>
|
304
|
-
> <% partial.with_item "Item two" %>
|
284
|
+
> <% partial.with_item "Item two" %> ❌ raises an error!
|
305
285
|
> <% end %>
|
306
286
|
> ```
|
307
287
|
|
@@ -310,7 +290,7 @@ and their corresponding template variable represents a single value:
|
|
310
290
|
Multi-value slots are defined using a **plural** slot name:
|
311
291
|
|
312
292
|
```erb
|
313
|
-
<%# slots: (items:
|
293
|
+
<%# slots: (items: []) -%>
|
314
294
|
```
|
315
295
|
|
316
296
|
Multi-value slots can be called as many times as needed
|
@@ -319,17 +299,19 @@ and their corresponding template variable represents an array of values.
|
|
319
299
|
The slot writer methods for multi-value slots use the **singluar form** of the slot name (e.g. `#with_item` for the `items` slot).
|
320
300
|
|
321
301
|
```erb
|
302
|
+
# views/examples/show.html.erb
|
303
|
+
|
322
304
|
<%= render "example" do |partial| %>
|
323
305
|
<% partial.with_item "Item one" %>
|
324
306
|
<% partial.with_item "Item two" %>
|
325
307
|
<% partial.with_item "Item three" %>
|
326
308
|
<% end %>
|
327
|
-
```
|
328
309
|
|
329
|
-
|
330
|
-
<%# slots: (items: nil) -%>
|
310
|
+
# views/examples/_example.html.erb
|
331
311
|
|
332
|
-
|
312
|
+
<%# slots: (items: []) -%>
|
313
|
+
|
314
|
+
<%= items %> ➡️ ["Item one", "Item two", "Item three"]
|
333
315
|
|
334
316
|
<ul>
|
335
317
|
<% items.each do |item| %>
|
@@ -338,28 +320,119 @@ The slot writer methods for multi-value slots use the **singluar form** of the s
|
|
338
320
|
</li>
|
339
321
|
<% end %>
|
340
322
|
</ul>
|
323
|
+
|
324
|
+
➡️ <ul><li>Item one</li><li>Item two</li><li>Item three</li></ul>
|
341
325
|
```
|
342
326
|
|
343
|
-
### Using
|
327
|
+
### Using with view helpers
|
344
328
|
|
345
|
-
|
329
|
+
Slot values can be used with Rails view helpers (such as tag helpers) in the partial templates in the usual way:
|
330
|
+
|
331
|
+
```erb
|
332
|
+
<%= tag.h1 title %>
|
333
|
+
```
|
334
|
+
|
335
|
+
[Slot options](#slot-options) can be passed to helpers alongside the content by splatting slot value `.options`:
|
336
|
+
|
337
|
+
```erb
|
338
|
+
<%= tag.h1 title, **title.options %>
|
339
|
+
```
|
340
|
+
|
341
|
+
#### Slotified helpers
|
342
|
+
|
343
|
+
Slotify patches a number of the most commonly used view helpers (such as `content_tag`, `link_to`) so that slot value arguments and options are transparently expanded and passed to the underlying helper. This means that manual args/options splatting (as described above) is not required.
|
344
|
+
|
345
|
+
```erb
|
346
|
+
# views/examples/show.html.erb
|
347
|
+
|
348
|
+
<%= render "example" do |partial| %>
|
349
|
+
<% partial.with_title "The title", class: "color-hotpink" %>
|
350
|
+
<% partial.with_website_link "Example website", "https://example.com", data: {controller: "clicker"} %>
|
351
|
+
<% end %>
|
352
|
+
|
353
|
+
# views/examples/_example.html.erb
|
354
|
+
|
355
|
+
<%# slots: (title: nil, website_link: nil) -%>
|
356
|
+
|
357
|
+
<%= content_tag :h1, title %>
|
358
|
+
➡️ <h1 class="color-hotpink">The title</h1>
|
359
|
+
|
360
|
+
<%= link_to website_link %>
|
361
|
+
➡️ <a href="https://example.com" data-controller="clicker">Example website</a>
|
362
|
+
```
|
363
|
+
|
364
|
+
Any options provided to the helper are 'smart-merged' with slot value options using the [Phlex `mix` helper](https://www.phlex.fun/sgml/helpers#mix) to ensure token list options (such as class names) are properly combined instead of being overwritten.
|
346
365
|
|
347
366
|
```erb
|
348
|
-
|
349
|
-
|
367
|
+
<%= content_tag :h1, title, class: "text-xl", id: "headline" %> <!-- options here are merged with slot value options -->
|
368
|
+
➡️ <h1 class="text-xl color-hotpink" id="headline">The title</h1>
|
350
369
|
|
351
|
-
|
352
|
-
|
370
|
+
<%= link_to website_link, target: "_blank" %>
|
371
|
+
➡️ <a href="https://example.com" data-controller="clicker" target="_blank">Example website</a>
|
353
372
|
```
|
354
373
|
|
374
|
+
If a slotified helper is provided with a slot value collection (i.e. from a [multi-value slot](#multi-value-slots)) then the helper will be run once for each value in the collection:
|
375
|
+
|
355
376
|
```erb
|
356
|
-
|
357
|
-
|
377
|
+
# views/examples/show.html.erb
|
378
|
+
|
379
|
+
<%= render "example" do |partial| %>
|
380
|
+
<% partial.with_item "Item one" %>
|
381
|
+
<% partial.with_item "Item two", class: "highlight" %>
|
382
|
+
<% end %>
|
383
|
+
|
384
|
+
# views/examples/_example.html.erb
|
385
|
+
|
386
|
+
<%# slots: (items: []) -%>
|
387
|
+
|
388
|
+
<%= content_tag :li, items %>
|
389
|
+
➡️ <li>Item one</li><li class="highlight">Item two</li>
|
390
|
+
|
391
|
+
<%= content_tag :li, items, class: "item" %>
|
392
|
+
➡️ <li class="item">Item one</li><li class="item highlight">Item two</li>
|
393
|
+
```
|
394
|
+
|
395
|
+
#### List of 'slotified' helpers
|
396
|
+
|
397
|
+
* `tag` _(top-level `tag` helper only, not the `tag.<tag_name>` shorthands)_
|
398
|
+
* `content_tag`
|
399
|
+
* `link_to`
|
400
|
+
* `link_to_if`
|
401
|
+
* `link_to_unless`
|
402
|
+
* `link_to_unless_current`
|
403
|
+
* `button_to`
|
404
|
+
* `mail_to`
|
405
|
+
* `sms_to`
|
406
|
+
* `phone_to`
|
407
|
+
* `url_for`
|
408
|
+
|
409
|
+
### Using alongside strict locals
|
410
|
+
|
411
|
+
Strict locals can be defined in 'slotified' partial templates in the same way as usual,
|
412
|
+
either above or below the `slots` definition.
|
413
|
+
|
414
|
+
```erb
|
415
|
+
# views/examples/_example.html.erb
|
416
|
+
|
417
|
+
<%# locals: (title:) -%>
|
418
|
+
<%# slots: (body: "No content available") -%>
|
419
|
+
|
420
|
+
<article>
|
421
|
+
<h1><%= title %></h1>
|
422
|
+
<div><%= body %></div>
|
423
|
+
</article>
|
424
|
+
```
|
425
|
+
|
426
|
+
Locals are provided when rendering the partial in the usual way.
|
358
427
|
|
359
|
-
|
428
|
+
```erb
|
429
|
+
# views/examples/show.html.erb
|
360
430
|
|
361
|
-
<%=
|
362
|
-
|
431
|
+
<%= render "article", title: "Article title here" do |partial| %>
|
432
|
+
<% partial.with_body do %>
|
433
|
+
<p>Body content here...</p>
|
434
|
+
<% end %>
|
435
|
+
<% end %>
|
363
436
|
```
|
364
437
|
|
365
438
|
### Rendering slots
|
@@ -372,6 +445,8 @@ The slot writer methods for multi-value slots use the **singluar form** of the s
|
|
372
445
|
These value objects are automatically stringified so in most cases you will not even be aware of this and they can just be treated as regular string variables.
|
373
446
|
|
374
447
|
```erb
|
448
|
+
# views/examples/show.html.erb
|
449
|
+
|
375
450
|
<%= render "example" do |partial| %>
|
376
451
|
<% partial.with_title class: "color-hotpink" do %>
|
377
452
|
The title
|
@@ -380,19 +455,24 @@ These value objects are automatically stringified so in most cases you will not
|
|
380
455
|
```
|
381
456
|
|
382
457
|
```erb
|
383
|
-
|
384
|
-
|
458
|
+
# views/examples/_example.html.erb
|
459
|
+
|
460
|
+
<%# slots: (title: nil) -%>
|
385
461
|
|
386
|
-
|
387
|
-
<% title.content %> <!-- "The title" -->
|
462
|
+
<% title.is_a?(Slotify::Value) %> ➡️ true
|
388
463
|
|
389
|
-
|
390
|
-
|
464
|
+
<%= title %> ➡️ "The title"
|
465
|
+
<% title.content %> ➡️ "The title"
|
466
|
+
|
467
|
+
<% title.options %> ➡️ { class: "color-hotpink" }
|
468
|
+
<%= title.options %> ➡️ "class='color-hotpink'"
|
391
469
|
```
|
392
470
|
|
393
471
|
**Plural slot value variables** in partial templates are instances of the enumerable `Slotify::ValueCollection` class, with all items instances of `Slotity::Value`.
|
394
472
|
|
395
473
|
```erb
|
474
|
+
# views/examples/show.html.erb
|
475
|
+
|
396
476
|
<%= render "example" do |partial| %>
|
397
477
|
<% partial.with_item "Item one" %>
|
398
478
|
<% partial.with_item "Item two", class: "current" %>
|
@@ -400,14 +480,18 @@ These value objects are automatically stringified so in most cases you will not
|
|
400
480
|
```
|
401
481
|
|
402
482
|
```erb
|
403
|
-
|
483
|
+
# views/examples/_example.html.erb
|
484
|
+
|
485
|
+
<%# slots: (items: []) -%>
|
486
|
+
|
487
|
+
<% items.is_a?(Slotify::ValueCollection) %> ➡️ true
|
404
488
|
|
405
489
|
<% items.each do |item| %>
|
406
490
|
<li <%= item.options %>><%= item %></li>
|
407
491
|
<% end %>
|
408
|
-
|
492
|
+
➡️ <li>Item one</li> <li class="current">Item two</li>
|
409
493
|
|
410
|
-
<%= items %>
|
494
|
+
<%= items %> ➡️ "Item one Item two"
|
411
495
|
```
|
412
496
|
|
413
497
|
#### `Slotity::Value`
|
@@ -424,15 +508,41 @@ Returns a `Slotify::ValueOptions` instance that can be treated like a `Hash`. Ca
|
|
424
508
|
|
425
509
|
When converted to a string either explicitly (via `.to_s`) or implicitly (by outputting the value template using ERB `<%= %>` expression tags) the stringified value is generated by passing the options hash through the Rails `tag.attributes` helper.
|
426
510
|
|
511
|
+
**`.args`**
|
512
|
+
|
513
|
+
Returns an array of the arguments that were passed into the slot writer method (if any) when rendering the partial.
|
514
|
+
|
515
|
+
Slot arguments can also be accessed using hash access notation.
|
516
|
+
|
517
|
+
```erb
|
518
|
+
# views/examples/show.html.erb
|
519
|
+
|
520
|
+
<%= render "example" do |partial| %>
|
521
|
+
<% partial.with_link "Example link", "https://example.com", class: "external-link" %>
|
522
|
+
<% end %>
|
523
|
+
```
|
524
|
+
|
525
|
+
```erb
|
526
|
+
# views/examples/_example.html.erb
|
527
|
+
|
528
|
+
<%# slots: (link: nil) -%>
|
529
|
+
|
530
|
+
<% link.args %> ➡️ ["Example link", "https://example.com"]
|
531
|
+
<% link[0] %> ➡️ "Example link"
|
532
|
+
<% link[1] %> ➡️ "https://example.com"
|
533
|
+
```
|
534
|
+
|
427
535
|
**`.with_default_options(default_options)`**
|
428
536
|
|
429
|
-
Merges
|
537
|
+
Merges slot options with the `default_options` hash provided. Returns a new `Slotity::Value` instance with the merged options set.
|
538
|
+
|
539
|
+
Options are 'smart-merged' using the [Phlex `mix` helper](https://www.phlex.fun/sgml/helpers#mix) to ensure token list options (such as class names) are properly combined instead of being overwritten.
|
430
540
|
|
431
541
|
```erb
|
432
|
-
<%
|
542
|
+
<% title_with_defaults = title.with_default_options(class: "size-lg", aria: {level: 1}) %>
|
433
543
|
|
434
|
-
<%
|
435
|
-
<%=
|
544
|
+
<% title_with_defaults.options %> ➡️ { class: "size-lg color-hotpink", aria: {level: 1} }
|
545
|
+
<%= title_with_defaults.options %> ➡️ "class='size-lg color-hotpink' aria-level='1'"
|
436
546
|
```
|
437
547
|
|
438
548
|
## Slotify vs alternatives
|
@@ -445,10 +555,10 @@ However there are a number of key differences:
|
|
445
555
|
* Slotify requires the explicit definition of slots using 'strict locals'-style comments;
|
446
556
|
Nice partials slots are implicitly defined when rendering the partial.
|
447
557
|
* Slotify slot values are available as local variables;
|
448
|
-
with Nice partials slot values are accessed via methods
|
558
|
+
with Nice partials slot values are accessed via methods a single `partial` variable.
|
449
559
|
* Slotify has the concept (and enforces the use) of single- vs. multi-value slots.
|
450
560
|
* Slotify slot content and options are transparently expanded and merged into defaults when using with helpers like `content_tag` and `link_to`.
|
451
|
-
* Slotify slot values are `renderable` objects
|
561
|
+
* Slotify slot values are `renderable` objects.
|
452
562
|
|
453
563
|
You might choose slotify if you prefer a stricter, 'Rails-native'-feeling slots implementation, and Nice Partials if you want more render-time flexibility and a clearer
|
454
564
|
separation of 'nice partial' functionality from ActionView-provided locals etc.
|
@@ -456,8 +566,7 @@ separation of 'nice partial' functionality from ActionView-provided locals etc.
|
|
456
566
|
#### `view_component`
|
457
567
|
|
458
568
|
Both [ViewComponent](https://viewcomponent.org/) and Slotify provide a 'slots' API for content blocks.
|
459
|
-
Slotify's slot writer syntax (i.e. `.with_<slot_name>` methods) and the concept of single-value (`renders_one`) vs multi-value (`renders_many`) slots
|
460
|
-
are both modelled on ViewComponent's slots implementation.
|
569
|
+
Slotify's slot writer syntax (i.e. `.with_<slot_name>` methods) and the concept of single-value (`renders_one`) vs multi-value (`renders_many`) slots are both modelled on ViewComponent's slots implementation.
|
461
570
|
|
462
571
|
However apart from that they are quite different. Slotify adds functionality to regular ActionView partials whereas ViewComponent provides a complete standalone component system.
|
463
572
|
|
@@ -491,84 +600,162 @@ Slotify uses MiniTest for its test suite.
|
|
491
600
|
#### Run tests
|
492
601
|
|
493
602
|
```shell
|
494
|
-
bin/test
|
603
|
+
bundle exec bin/test
|
495
604
|
```
|
496
605
|
|
497
606
|
### Benchmarks
|
498
607
|
|
499
|
-
|
608
|
+
Some crude render performance benchmark tests for `slotify`, `view_component` and `nice_partials` can be found in the `/performance` directory.
|
609
|
+
|
610
|
+
All benchmarks use a vanilla ActionView template rendering performance measurement as the baseline for comparison against.
|
611
|
+
|
612
|
+
* The **slots** benchmarks compare the performance of rendering a partial/component that uses slots against the baseline.
|
613
|
+
* The **no slots** benchmarks compare the performance of rendering a partial/component without slots (i.e. values provided as keyword arguments) against the baseline. These results are useful for determining how much the gem being benchmarked affects rendering performance even when slots are not used.
|
614
|
+
|
615
|
+
The benchmark tests are a work in progress right now so any suggestions for improvements would be much appreciated!
|
616
|
+
|
617
|
+
#### Benchmark results summary
|
618
|
+
|
619
|
+
* `slotify`, `nice_partials` and `view_component` all result in slighly slower partial/component rendering speeds compared to the 'vanilla ActionView' baseline (as expected).
|
620
|
+
* `slotify` is currently the closest to the baseline when rendering partials/components without any slots (~1.2x slower).
|
621
|
+
* `slotify` is currently the furthest from the baseline when rendering partials/components using slots (~3x slower).
|
500
622
|
|
501
|
-
These benchmarks are a little crude right now!
|
502
623
|
|
503
|
-
|
624
|
+
#### Running benchmarks
|
625
|
+
|
626
|
+
You can run the benchmark tests locally using the `bin/benchmark` command from the root of the repository.
|
504
627
|
|
505
628
|
```shell
|
506
|
-
bin/benchmarks # run all benchmarks
|
507
|
-
bin/benchmarks slotify # run
|
629
|
+
bundle exec bin/benchmarks # run all benchmarks
|
630
|
+
bundle exec bin/benchmarks slotify # run specified benchmarks only (slotify / view_component / nice_partials)
|
631
|
+
bundle exec bin/benchmarks --no-slots # run 'no-slots' benchmarks
|
508
632
|
```
|
509
633
|
|
510
634
|
<details>
|
511
|
-
<summary>Recent benchmark results</summary>
|
635
|
+
<summary><h4>Recent benchmark results</h4></summary>
|
636
|
+
|
637
|
+
#### With slots:
|
512
638
|
|
513
639
|
```
|
514
|
-
|
640
|
+
➜ bin/benchmark
|
641
|
+
|
642
|
+
🏁🏁 SLOTIFY 🏁🏁
|
515
643
|
|
516
644
|
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
517
645
|
Warming up --------------------------------------
|
518
|
-
|
519
|
-
slots 11.038k i/100ms
|
646
|
+
baseline 11.836k i/100ms
|
520
647
|
Calculating -------------------------------------
|
521
|
-
|
522
|
-
|
648
|
+
baseline 118.334k (± 2.8%) i/s (8.45 μs/i) - 591.800k in 5.005403s
|
649
|
+
|
650
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
651
|
+
Warming up --------------------------------------
|
652
|
+
slots 2.229k i/100ms
|
653
|
+
Calculating -------------------------------------
|
654
|
+
slots 26.066k (± 7.8%) i/s (38.36 μs/i) - 131.511k in 5.087467s
|
523
655
|
|
524
656
|
Comparison:
|
525
|
-
|
526
|
-
slots:
|
657
|
+
baseline: 118333.8 i/s
|
658
|
+
slots: 26066.0 i/s - 4.54x slower
|
527
659
|
|
528
660
|
|
529
661
|
🏁🏁 NICE_PARTIALS 🏁🏁
|
530
662
|
|
531
663
|
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
532
664
|
Warming up --------------------------------------
|
533
|
-
|
534
|
-
|
665
|
+
baseline 12.072k i/100ms
|
666
|
+
Calculating -------------------------------------
|
667
|
+
baseline 114.740k (± 4.4%) i/s (8.72 μs/i) - 579.456k in 5.060487s
|
668
|
+
|
669
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
670
|
+
Warming up --------------------------------------
|
671
|
+
slots 3.626k i/100ms
|
535
672
|
Calculating -------------------------------------
|
536
|
-
|
537
|
-
slots 40.737k (± 4.5%) i/s (24.55 μs/i) - 408.345k in 10.051584s
|
673
|
+
slots 35.971k (± 6.1%) i/s (27.80 μs/i) - 181.300k in 5.061126s
|
538
674
|
|
539
675
|
Comparison:
|
540
|
-
|
541
|
-
slots:
|
676
|
+
baseline: 114740.4 i/s
|
677
|
+
slots: 35971.0 i/s - 3.19x slower
|
542
678
|
|
543
679
|
|
544
680
|
🏁🏁 VIEW_COMPONENT 🏁🏁
|
545
681
|
|
546
682
|
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
547
683
|
Warming up --------------------------------------
|
548
|
-
|
549
|
-
|
684
|
+
baseline 11.991k i/100ms
|
685
|
+
Calculating -------------------------------------
|
686
|
+
baseline 118.532k (± 2.3%) i/s (8.44 μs/i) - 599.550k in 5.060901s
|
687
|
+
|
688
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
689
|
+
Warming up --------------------------------------
|
690
|
+
slots 7.493k i/100ms
|
550
691
|
Calculating -------------------------------------
|
551
|
-
|
552
|
-
slots 72.508k (± 5.2%) i/s (13.79 μs/i) - 729.610k in 10.096809s
|
692
|
+
slots 72.281k (± 6.3%) i/s (13.83 μs/i) - 359.664k in 5.002508s
|
553
693
|
|
554
694
|
Comparison:
|
555
|
-
|
556
|
-
slots:
|
695
|
+
baseline: 118532.2 i/s
|
696
|
+
slots: 72281.3 i/s - 1.64x slower
|
697
|
+
```
|
557
698
|
|
699
|
+
#### Without slots:
|
700
|
+
|
701
|
+
```
|
702
|
+
➜ bin/benchmark --no-slots
|
558
703
|
|
559
704
|
🏁🏁 SLOTIFY 🏁🏁
|
560
705
|
|
561
706
|
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
562
707
|
Warming up --------------------------------------
|
563
|
-
|
564
|
-
|
708
|
+
baseline 13.071k i/100ms
|
709
|
+
Calculating -------------------------------------
|
710
|
+
baseline 127.673k (± 3.6%) i/s (7.83 μs/i) - 640.479k in 5.023506s
|
711
|
+
|
712
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
713
|
+
Warming up --------------------------------------
|
714
|
+
no slots 11.029k i/100ms
|
715
|
+
Calculating -------------------------------------
|
716
|
+
no slots 110.253k (± 2.0%) i/s (9.07 μs/i) - 551.450k in 5.003625s
|
717
|
+
|
718
|
+
Comparison:
|
719
|
+
baseline: 127673.0 i/s
|
720
|
+
no slots: 110252.6 i/s - 1.16x slower
|
721
|
+
|
722
|
+
|
723
|
+
🏁🏁 NICE_PARTIALS 🏁🏁
|
724
|
+
|
725
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
726
|
+
Warming up --------------------------------------
|
727
|
+
baseline 13.016k i/100ms
|
728
|
+
Calculating -------------------------------------
|
729
|
+
baseline 131.103k (± 1.8%) i/s (7.63 μs/i) - 663.816k in 5.065054s
|
730
|
+
|
731
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
732
|
+
Warming up --------------------------------------
|
733
|
+
no slots 4.556k i/100ms
|
565
734
|
Calculating -------------------------------------
|
566
|
-
no slots
|
567
|
-
slots 26.454k (± 5.4%) i/s (37.80 μs/i) - 265.580k in 10.077285s
|
735
|
+
no slots 44.888k (± 3.7%) i/s (22.28 μs/i) - 227.800k in 5.082635s
|
568
736
|
|
569
737
|
Comparison:
|
570
|
-
|
571
|
-
|
738
|
+
baseline: 131103.4 i/s
|
739
|
+
no slots: 44888.2 i/s - 2.92x slower
|
740
|
+
|
741
|
+
|
742
|
+
🏁🏁 VIEW_COMPONENT 🏁🏁
|
743
|
+
|
744
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
745
|
+
Warming up --------------------------------------
|
746
|
+
baseline 13.454k i/100ms
|
747
|
+
Calculating -------------------------------------
|
748
|
+
baseline 128.817k (± 5.7%) i/s (7.76 μs/i) - 645.792k in 5.038036s
|
749
|
+
|
750
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
751
|
+
Warming up --------------------------------------
|
752
|
+
no slots 17.335k i/100ms
|
753
|
+
Calculating -------------------------------------
|
754
|
+
no slots 203.191k (± 2.2%) i/s (4.92 μs/i) - 1.023M in 5.036040s
|
755
|
+
|
756
|
+
Comparison:
|
757
|
+
no slots: 203190.7 i/s
|
758
|
+
baseline: 128817.3 i/s - 1.58x slower
|
572
759
|
```
|
573
760
|
|
574
761
|
</details>
|
@@ -577,4 +764,8 @@ Comparison:
|
|
577
764
|
|
578
765
|
Slotify was inspired by the excellent [nice_partials gem](https://github.com/bullet-train-co/nice_partials) as well as ViewComponent's [slots implementation](https://viewcomponent.org/guide/slots.html).
|
579
766
|
|
580
|
-
`nice_partials` provides very similar functionality to Slotify but takes a slightly different approach/style. So if you are not convinced by Slotify then definitely [check it out](https://github.com/bullet-train-co/nice_partials)!
|
767
|
+
`nice_partials` provides very similar functionality to Slotify but takes a slightly different approach/style. So if you are not convinced by Slotify then definitely [check it out](https://github.com/bullet-train-co/nice_partials)!
|
768
|
+
|
769
|
+
## License
|
770
|
+
|
771
|
+
The `slotify` gem is available as open source under the terms of the MIT License.
|
data/lib/slotify/error.rb
CHANGED
@@ -1,19 +1,13 @@
|
|
1
1
|
module Slotify
|
2
|
-
class
|
3
|
-
end
|
4
|
-
|
5
|
-
class SlotsDefinedError < RuntimeError
|
2
|
+
class Error < StandardError
|
6
3
|
end
|
7
4
|
|
8
|
-
class
|
5
|
+
class UnknownSlotError < NameError
|
9
6
|
end
|
10
7
|
|
11
8
|
class MultipleSlotEntriesError < ArgumentError
|
12
9
|
end
|
13
10
|
|
14
|
-
class SlotArgumentError < ArgumentError
|
15
|
-
end
|
16
|
-
|
17
11
|
class StrictSlotsError < ArgumentError
|
18
12
|
end
|
19
13
|
|
@@ -3,13 +3,13 @@ module Slotify
|
|
3
3
|
module Base
|
4
4
|
include SlotCompatability
|
5
5
|
|
6
|
-
attr_accessor :
|
6
|
+
attr_accessor :slotify
|
7
7
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
8
|
+
def capture_with_outer_slotify_access(*args, &block)
|
9
|
+
inner_slotify, @slotify = slotify, slotify.outer_slotify
|
10
|
+
inner_slotify.capture(*args, &block)
|
11
11
|
ensure
|
12
|
-
@
|
12
|
+
@slotify = inner_slotify
|
13
13
|
end
|
14
14
|
|
15
15
|
make_compatible_with_slots :url_for, :link_to, :button_to, :link_to_unless_current,
|
@@ -4,17 +4,17 @@ module Slotify
|
|
4
4
|
def render_partial_template(view, locals, template, layout, block)
|
5
5
|
return super unless template.strict_slots?
|
6
6
|
|
7
|
-
view.
|
7
|
+
view.slotify = Slotify::Slots.new(view, template.strict_slots_keys)
|
8
8
|
|
9
|
-
view.
|
9
|
+
view.capture_with_outer_slotify_access(&block) if block
|
10
10
|
|
11
|
-
locals = locals.merge(view.
|
11
|
+
locals = locals.merge(view.slotify.slot_locals)
|
12
12
|
|
13
13
|
decorate_strict_slots_errors do
|
14
14
|
super(view, locals, template, layout, block)
|
15
15
|
end
|
16
16
|
ensure
|
17
|
-
view.
|
17
|
+
view.slotify = view.slotify.outer_slotify if view.slotify
|
18
18
|
end
|
19
19
|
|
20
20
|
def decorate_strict_slots_errors
|
@@ -34,7 +34,7 @@ module Slotify
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def missing_strict_locals_error?(error)
|
37
|
-
error.template &&
|
37
|
+
error.template && defined?(ActionView::StrictLocalsError) && error.cause.is_a?(ActionView::StrictLocalsError) ||
|
38
38
|
(error.cause.is_a?(ArgumentError) && error.cause.message.match(/missing local/))
|
39
39
|
end
|
40
40
|
end
|
@@ -44,7 +44,7 @@ module Slotify
|
|
44
44
|
return super unless strict_slots?
|
45
45
|
|
46
46
|
strict_slots_keys.each_with_object(+super) do |key, code|
|
47
|
-
code << "
|
47
|
+
code << "slotify.set_slot_default(:#{key}, binding.local_variable_get(:#{key})); #{key} = slotify.content_for(:#{key});"
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Slotify
|
2
|
-
class
|
2
|
+
class Slots
|
3
3
|
include SymbolInflectionHelper
|
4
4
|
|
5
5
|
RESERVED_SLOT_NAMES = [
|
@@ -7,11 +7,11 @@ module Slotify
|
|
7
7
|
:capture, :yield, :partial
|
8
8
|
]
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :outer_slotify
|
11
11
|
|
12
12
|
def initialize(view_context, slots = [])
|
13
13
|
@view_context = view_context
|
14
|
-
@
|
14
|
+
@outer_slotify = view_context.slotify
|
15
15
|
@values = ValueStore.new(@view_context)
|
16
16
|
@defined_slots = nil
|
17
17
|
|
data/lib/slotify/version.rb
CHANGED
data/lib/slotify.rb
CHANGED
@@ -6,6 +6,7 @@ require_relative "slotify/error"
|
|
6
6
|
loader = Zeitwerk::Loader.for_gem
|
7
7
|
loader.tag = "slotify"
|
8
8
|
loader.push_dir("#{__dir__}/slotify", namespace: Slotify)
|
9
|
+
loader.ignore("#{__dir__}/slotify/error")
|
9
10
|
loader.collapse("#{__dir__}/slotify/concerns")
|
10
11
|
loader.collapse("#{__dir__}/slotify/services")
|
11
12
|
loader.enable_reloading if ENV["RAILS_ENV"] == "development"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slotify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Perkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -52,9 +52,9 @@ files:
|
|
52
52
|
- lib/slotify/extensions/base.rb
|
53
53
|
- lib/slotify/extensions/partial_renderer.rb
|
54
54
|
- lib/slotify/extensions/template.rb
|
55
|
-
- lib/slotify/partial.rb
|
56
55
|
- lib/slotify/services/method_args_resolver.rb
|
57
56
|
- lib/slotify/services/tag_options_merger.rb
|
57
|
+
- lib/slotify/slots.rb
|
58
58
|
- lib/slotify/value.rb
|
59
59
|
- lib/slotify/value_collection.rb
|
60
60
|
- lib/slotify/value_options.rb
|
@@ -79,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: '0'
|
81
81
|
requirements: []
|
82
|
-
rubygems_version: 3.
|
82
|
+
rubygems_version: 3.3.3
|
83
83
|
signing_key:
|
84
84
|
specification_version: 4
|
85
85
|
summary: Superpowered slots for your Rails partials.
|