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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 311b3b7001fb194c8518283b714ed8f5ed5990dc5b7fa51af34a25348f00d8cd
4
- data.tar.gz: d22c0b5cea41b792e837f387b6c444013ff64013b84bcf781aa67f46e91a9e6c
3
+ metadata.gz: 1ab98a9cc284edb88505a7e87f06b10f57d33e9e531387b3bf2eb184c2f543f5
4
+ data.tar.gz: 96635ec015c16ebca358ad031b8f8bf5e4c3c253102f94d8730cb894a252d3f5
5
5
  SHA512:
6
- metadata.gz: 8cb049942e918fb670d775d9e7e6fe42a4db1b799d6df34a4ed7ea6dbb6f325824d52a56f629d379597e8273b3c02d6151749e6de01d7019def944b627bbc828
7
- data.tar.gz: 70658ea93b6b19b14a0a608e6d3afc9e815a9f3c70fdfdd7a9d5ec20a9743b4fcb3603e6c2973381e7219cf4b39dd65e42448be5874446780a8c020055923061
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 adds an unobtrusive (but powerful!) **content slot API** to ActionView partials.
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
- > [!CAUTION]
15
- > Slotify is still in a early stage of development.
16
- The documentation is still quite sparse and the API could change at any point prior to a `v1.0` release.
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 `article` partial template might look something like this:
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
- <!-- _article.html.erb -->
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
- <%= render "article" do |partial| %>
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
- <!-- views/_example.html.erb -->
72
+ # views/examples/_example.html.erb
72
73
 
73
74
  <%# locals: (id:) -%>
74
- <%# slots: (title: "Example title", lists: nil, quotes: nil, website_link:) -%>
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
- <!-- views/_list.html.erb -->
99
+ # views/examples/_list.html.erb
99
100
 
100
101
  <%# locals: (title:) -%>
101
- <%# slots: (items: nil) -%>
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
- <!-- views/slotify.html.erb -->
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
- <!-- _required.html.erb -->
160
+ # views/examples/_required.html.erb
160
161
 
161
162
  <%# slots: (title:) -%>
162
163
  <h1><%= title %></h1>
163
- ```
164
164
 
165
- ```erb
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
- ### Using alongside strict locals
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
- Locals are provided when rendering the partial in the usual way.
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
- ```erb
251
- <%# slots: (title:) -%>
223
+ # views/examples/_example.html.erb
252
224
 
253
- <%= title.options.keys %> <!-- [:class, :data] -->
254
- <%= title %> <!-- The 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
- <!-- <h1 class="color-hotpink" data-controller="fancy-title">The title</h1> -->
242
+ ➡️ <h1 class="color-hotpink" data-controller="fancy-title">The title</h1>
265
243
  ```
266
244
 
267
- ### Slot types
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** and return **a single value**.
272
- * **Multi-value** slots can be called **many times** and return **an array of values**.
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
- ```erb
270
+ # views/examples/_example.html.erb
271
+
292
272
  <%# slots: (item: nil) -%>
293
273
  <div>
294
- <%= item %> <!-- "Item one" -->
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" %> # ❌ raises an error!
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: nil) -%>
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
- ```erb
330
- <%# slots: (items: nil) -%>
310
+ # views/examples/_example.html.erb
331
311
 
332
- <%= items %> <!-- ["Item one", "Item two", "Item three"] -->
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 slots with helpers
327
+ ### Using with view helpers
344
328
 
345
- > _Docs coming soon..._
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
- <% partial.with_title "The title", class: "color-hotpink" %>
349
- <% partial.with_website_link "Example website", "https://example.com", data: {controller: "external-link"} %>
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
- <% partial.with_item "Item one" %>
352
- <% partial.with_item "Item two", class: "highlight" %>
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
- <%= content_tag :h1, title %> <!-- <h1 class="color-hotpink">The title</h1> -->
357
- <%= content_tag :h1, title, class: "example-title" %> <!-- <h1 class="example-title color-hotpink">The title</h1> -->
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
- <%= link_to website_link %> <!-- <a href="https://example.com" data-controller="external-link">Example website</a> -->
428
+ ```erb
429
+ # views/examples/show.html.erb
360
430
 
361
- <%= content_tag :li, items %> <!-- <li>Item one</li><li class="highlight">Item two</li> -->
362
- <%= content_tag :li, items, class: "item" %> <!-- <li class="item">Item one</li><li class="item highlight">Item two</li> -->
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
- <% title.is_a?(Slotify::Value) %> <!-- true -->
384
- <% items.is_a?(Slotify::ValueCollection) %> <!-- true -->
458
+ # views/examples/_example.html.erb
459
+
460
+ <%# slots: (title: nil) -%>
385
461
 
386
- <%= title %> <!-- "The title" -->
387
- <% title.content %> <!-- "The title" -->
462
+ <% title.is_a?(Slotify::Value) %> ➡️ true
388
463
 
389
- <% title.options %> <!-- { class: "color-hotpink" } (hash of any options provided when calling the `.with_title` slot value writer method) -->
390
- <%= title.options %> <!-- "class='color-hotpink'" (string generated by passing the options hash through the Rails `tag.attributes` helper) -->
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
- <% items.is_a?(Slotify::ValueCollection) %> <!-- true -->
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
- <!-- <li>Item one</li> <li class="current">Item two</li> -->
492
+ ➡️ <li>Item one</li> <li class="current">Item two</li>
409
493
 
410
- <%= items %> <!-- "Item one Item two" -->
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 the options set when calling the slot value writer method with the `default_options` hash provided and returns a new `Slotity::Value` instance with the merged options set.
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
- <% title_with_default_opts = title.with_default_options(class: "size-lg", aria: {level: 1}) %> <!-- apply default options -->
542
+ <% title_with_defaults = title.with_default_options(class: "size-lg", aria: {level: 1}) %>
433
543
 
434
- <% title_with_default_opts.options %> <!-- { class: "size-lg color-hotpink", aria: {level: 1} } -->
435
- <%= title_with_default_opts.options %> <!-- "class='size-lg color-hotpink' aria-level='1'" -->
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 on the `partial` variable.
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
- Rendering performance benchmark tests for Slotify and a few alternatives (`action_view`, `view_component` & `nice_partials`) can be found in the `/performance` directory.
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
- **Run benchmarks:**
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 Slotify benchmarks only
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
- 🏁🏁 ACTION_VIEW 🏁🏁
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
- no slots 13.120k i/100ms
519
- slots 11.038k i/100ms
646
+ baseline 11.836k i/100ms
520
647
  Calculating -------------------------------------
521
- no slots 127.047k5.2%) i/s (7.87 μs/i) - 1.273M in 10.051203s
522
- slots 106.400k (± 5.0%) i/s (9.40 μs/i) - 1.071M in 10.095061s
648
+ baseline 118.334k2.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
- no slots: 127047.3 i/s
526
- slots: 106400.3 i/s - 1.19x slower
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
- no slots 11.451k i/100ms
534
- slots 3.889k i/100ms
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
- no slots 117.258k3.3%) i/s (8.53 μs/i) - 1.179M in 10.070693s
537
- slots 40.737k (± 4.5%) i/s (24.55 μs/i) - 408.345k in 10.051584s
673
+ slots 35.971k6.1%) i/s (27.80 μs/i) - 181.300k in 5.061126s
538
674
 
539
675
  Comparison:
540
- no slots: 117257.7 i/s
541
- slots: 40736.8 i/s - 2.88x slower
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
- no slots 20.270k i/100ms
549
- slots 7.445k i/100ms
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
- no slots 211.571k2.6%) i/s (4.73 μs/i) - 2.128M in 10.067334s
552
- slots 72.508k (± 5.2%) i/s (13.79 μs/i) - 729.610k in 10.096809s
692
+ slots 72.281k6.3%) i/s (13.83 μs/i) - 359.664k in 5.002508s
553
693
 
554
694
  Comparison:
555
- no slots: 211570.7 i/s
556
- slots: 72508.0 i/s - 2.92x slower
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
- no slots 12.051k i/100ms
564
- slots 2.710k i/100ms
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 116.156k5.4%) i/s (8.61 μs/i) - 1.169M in 10.102387s
567
- slots 26.454k (± 5.4%) i/s (37.80 μs/i) - 265.580k in 10.077285s
735
+ no slots 44.888k3.7%) i/s (22.28 μs/i) - 227.800k in 5.082635s
568
736
 
569
737
  Comparison:
570
- no slots: 116155.7 i/s
571
- slots: 26454.0 i/s - 4.39x slower
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 UnknownSlotError < NameError
3
- end
4
-
5
- class SlotsDefinedError < RuntimeError
2
+ class Error < StandardError
6
3
  end
7
4
 
8
- class UndefinedSlotError < StandardError
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 :partial
6
+ attr_accessor :slotify
7
7
 
8
- def capture_with_outer_partial_access(*args, &block)
9
- inner_partial, @partial = partial, partial.outer_partial
10
- inner_partial.capture(*args, &block)
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
- @partial = inner_partial
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.partial = Slotify::Partial.new(view, template.strict_slots_keys)
7
+ view.slotify = Slotify::Slots.new(view, template.strict_slots_keys)
8
8
 
9
- view.capture_with_outer_partial_access(&block) if block
9
+ view.capture_with_outer_slotify_access(&block) if block
10
10
 
11
- locals = locals.merge(view.partial.slot_locals)
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.partial = view.partial.outer_partial if view.partial
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 && (defined?(ActionView::StrictLocalsError) && error.cause.is_a?(ActionView::StrictLocalsError)) ||
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 << "partial.set_slot_default(:#{key}, binding.local_variable_get(:#{key})); #{key} = partial.content_for(:#{key});"
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 Partial
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 :outer_partial
10
+ attr_reader :outer_slotify
11
11
 
12
12
  def initialize(view_context, slots = [])
13
13
  @view_context = view_context
14
- @outer_partial = view_context.partial
14
+ @outer_slotify = view_context.slotify
15
15
  @values = ValueStore.new(@view_context)
16
16
  @defined_slots = nil
17
17
 
@@ -1,3 +1,3 @@
1
1
  module Slotify
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.1"
3
3
  end
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.0.5
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-14 00:00:00.000000000 Z
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.5.10
82
+ rubygems_version: 3.3.3
83
83
  signing_key:
84
84
  specification_version: 4
85
85
  summary: Superpowered slots for your Rails partials.