slotify 0.0.4 → 0.1.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: 3528c82a3e01b2acc454f5df4c8081f19157e975a2e2ddaa46f56dadc565426f
4
- data.tar.gz: e9660ecb767e9ec12156eb3e878f10cdf9fe74c8b67b9fbc574fa64cae1f5f1a
3
+ metadata.gz: 7901ad71acf99111a450667e96a494becbfc55d78c62ab2314b7c3b8dae7e588
4
+ data.tar.gz: 73ef165ade4d766b69ef68cbba336bf488991e452038f751d3e5fb2f82c34bf5
5
5
  SHA512:
6
- metadata.gz: fe68d4ff07cbc1a12cc69937bb0da7af4080082504226f7dd9c63a8f4b35461460671265ca09745a9899e6e49ac5aab0c377a83d795a2547eb423255907aa84d
7
- data.tar.gz: a189cd09e36a1eab05de32a1c62f953acbe72dcca1ed64c0dd058002ded2f8da2d828e95e0ba0b7865a29d3cac4c58a1cc9c7ad45679ce87684a8edfe03e1032
6
+ metadata.gz: 906ce11515dcb698273445333d273233e96b9006454714b8d17d8a034f38fd333ef649c4d196e21d403e93ed1bca3c4016849aa4c1ac22c3803870d851dce1ba
7
+ data.tar.gz: d45ebbafe579ff34b4727ab14edae97898a87fd87452b841b3989f438248d927cdb16593b4ab05321bf4dce8a7f1e99f39639065798a91a7436f72f442d2b227
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,33 +178,6 @@ 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
- ```
196
-
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
181
  ### Setting slot values
208
182
 
209
183
  Content is passed into slots using dynamically generated `partial#with_<slot_name>` writer methods.
@@ -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,38 +214,44 @@ 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
- * **Multiple-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
 
276
- Single-value slots are defined using a **singlular** slot name:
254
+ Single-value slots are defined using a **singular** slot name:
277
255
 
278
256
  ```erb
279
257
  <%# slots: (item: nil) -%>
@@ -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,35 +281,37 @@ 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
 
308
- #### Multiple-value slots
288
+ #### Multi-value slots
309
289
 
310
- Multiple-value slots are defined using a **plural** slot name:
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
- Multiple-value slots can be called as many times as needed
296
+ Multi-value slots can be called as many times as needed
317
297
  and their corresponding template variable represents an array of values.
318
298
 
319
- The slot writer methods for multiple-value slots use the **singluar form** of the slot name (e.g. `#with_item` for the `items` slot).
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
311
+
312
+ <%# slots: (items: []) -%>
331
313
 
332
- <%= items %> <!-- ["Item one", "Item two", "Item three"] -->
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 multiple-value slots use the **singluar form** of th
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
@@ -371,9 +444,9 @@ The slot writer methods for multiple-value slots use the **singluar form** of th
371
444
  **Singlular slot value variables** in partial templates are actually instances of `Slotity::Value`.
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
-
375
-
376
447
  ```erb
448
+ # views/examples/show.html.erb
449
+
377
450
  <%= render "example" do |partial| %>
378
451
  <% partial.with_title class: "color-hotpink" do %>
379
452
  The title
@@ -382,19 +455,24 @@ These value objects are automatically stringified so in most cases you will not
382
455
  ```
383
456
 
384
457
  ```erb
385
- <% title.is_a?(Slotify::Value) %> <!-- true -->
386
- <% items.is_a?(Slotify::ValueCollection) %> <!-- true -->
458
+ # views/examples/_example.html.erb
387
459
 
388
- <%= title %> <!-- "The title" -->
389
- <% title.content %> <!-- "The title" -->
460
+ <%# slots: (title: nil) -%>
390
461
 
391
- <% title.options %> <!-- { class: "color-hotpink" } (hash of any options provided when calling the `.with_title` slot value writer method) -->
392
- <%= title.options %> <!-- "class='color-hotpink'" (string generated by passing the options hash through the Rails `tag.attributes` helper) -->
462
+ <% title.is_a?(Slotify::Value) %> ➡️ true
463
+
464
+ <%= title %> ➡️ "The title"
465
+ <% title.content %> ➡️ "The title"
466
+
467
+ <% title.options %> ➡️ { class: "color-hotpink" }
468
+ <%= title.options %> ➡️ "class='color-hotpink'"
393
469
  ```
394
470
 
395
471
  **Plural slot value variables** in partial templates are instances of the enumerable `Slotify::ValueCollection` class, with all items instances of `Slotity::Value`.
396
472
 
397
473
  ```erb
474
+ # views/examples/show.html.erb
475
+
398
476
  <%= render "example" do |partial| %>
399
477
  <% partial.with_item "Item one" %>
400
478
  <% partial.with_item "Item two", class: "current" %>
@@ -402,14 +480,18 @@ These value objects are automatically stringified so in most cases you will not
402
480
  ```
403
481
 
404
482
  ```erb
405
- <% items.is_a?(Slotify::ValueCollection) %> <!-- true -->
483
+ # views/examples/_example.html.erb
484
+
485
+ <%# slots: (items: []) -%>
486
+
487
+ <% items.is_a?(Slotify::ValueCollection) %> ➡️ true
406
488
 
407
489
  <% items.each do |item| %>
408
490
  <li <%= item.options %>><%= item %></li>
409
491
  <% end %>
410
- <!-- <li>Item one</li> <li class="current">Item two</li> -->
492
+ ➡️ <li>Item one</li> <li class="current">Item two</li>
411
493
 
412
- <%= items %> <!-- "Item one Item two" -->
494
+ <%= items %> ➡️ "Item one Item two"
413
495
  ```
414
496
 
415
497
  #### `Slotity::Value`
@@ -426,15 +508,41 @@ Returns a `Slotify::ValueOptions` instance that can be treated like a `Hash`. Ca
426
508
 
427
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.
428
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
+
429
535
  **`.with_default_options(default_options)`**
430
536
 
431
- 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.
432
540
 
433
541
  ```erb
434
- <% 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}) %>
435
543
 
436
- <% title_with_default_opts.options %> <!-- { class: "size-lg color-hotpink", aria: {level: 1} } -->
437
- <%= 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'"
438
546
  ```
439
547
 
440
548
  ## Slotify vs alternatives
@@ -447,10 +555,10 @@ However there are a number of key differences:
447
555
  * Slotify requires the explicit definition of slots using 'strict locals'-style comments;
448
556
  Nice partials slots are implicitly defined when rendering the partial.
449
557
  * Slotify slot values are available as local variables;
450
- with Nice partials slot values are accessed via methods on the `partial` variable.
451
- * Slotify has the concept (and enforces the use) of single-value vs. multiple-value slots.
558
+ with Nice partials slot values are accessed via methods a single `partial` variable.
559
+ * Slotify has the concept (and enforces the use) of single- vs. multi-value slots.
452
560
  * Slotify slot content and options are transparently expanded and merged into defaults when using with helpers like `content_tag` and `link_to`.
453
- * Slotify slot values are `renderable` objects
561
+ * Slotify slot values are `renderable` objects.
454
562
 
455
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
456
564
  separation of 'nice partial' functionality from ActionView-provided locals etc.
@@ -458,8 +566,7 @@ separation of 'nice partial' functionality from ActionView-provided locals etc.
458
566
  #### `view_component`
459
567
 
460
568
  Both [ViewComponent](https://viewcomponent.org/) and Slotify provide a 'slots' API for content blocks.
461
- Slotify's slot writer syntax (i.e. `.with_<slot_name>` methods) and the concept of single-value (`renders_one`) vs multiple-value (`renders_many`) slots
462
- 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.
463
570
 
464
571
  However apart from that they are quite different. Slotify adds functionality to regular ActionView partials whereas ViewComponent provides a complete standalone component system.
465
572
 
@@ -484,78 +591,181 @@ And then run `bundle install`. You are good to go!
484
591
  * `Rails 7.1+`
485
592
  * `Ruby 3.1+`
486
593
 
487
- ## Credits
594
+ ## Testing
488
595
 
489
- 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).
596
+ Slotify uses MiniTest for its test suite.
490
597
 
491
- `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)!
598
+ [Appraisal](https://github.com/thoughtbot/appraisal) is used in CI to test against a matrix of Ruby/Rails versions.
492
599
 
493
- ## Benchmarks
600
+ #### Run tests
494
601
 
495
- Slotify is still in the early stages of development and no attempt has yet been made to optimise rendering performance.
602
+ ```shell
603
+ bundle exec bin/test
604
+ ```
496
605
 
497
- Below are some initial (crude) benchmarking comparisons with other similar gems.
606
+ ### Benchmarks
498
607
 
499
- > [!TIP]
500
- > Benchmarks can be run using the `bin/benchmarks` command from the repository root.
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).
622
+
623
+
624
+ #### Running benchmarks
625
+
626
+ You can run the benchmark tests locally using the `bin/benchmark` command from the root of the repository.
627
+
628
+ ```shell
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
632
+ ```
633
+
634
+ <details>
635
+ <summary><h4>Recent benchmark results</h4></summary>
636
+
637
+ #### With slots:
501
638
 
502
639
  ```
503
- ✨🦄 ACTION_VIEW 🦄✨
640
+ bin/benchmark
641
+
642
+ 🏁🏁 SLOTIFY 🏁🏁
643
+
644
+ ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
645
+ Warming up --------------------------------------
646
+ baseline 11.836k i/100ms
647
+ Calculating -------------------------------------
648
+ baseline 118.334k (± 2.8%) i/s (8.45 μs/i) - 591.800k in 5.005403s
504
649
 
505
650
  ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
506
651
  Warming up --------------------------------------
507
- no slots 12.997k i/100ms
508
- slots 10.891k i/100ms
652
+ slots 2.229k i/100ms
509
653
  Calculating -------------------------------------
510
- no slots 125.622k5.4%) i/s (7.96 μs/i) - 1.261M in 10.072521s
511
- slots 108.468k (± 3.3%) i/s (9.22 μs/i) - 1.089M in 10.053026s
654
+ slots 26.066k7.8%) i/s (38.36 μs/i) - 131.511k in 5.087467s
512
655
 
513
656
  Comparison:
514
- no slots: 125621.9 i/s
515
- slots: 108467.5 i/s - 1.16x slower
657
+ baseline: 118333.8 i/s
658
+ slots: 26066.0 i/s - 4.54x slower
516
659
 
517
660
 
518
- ✨🦄 NICE_PARTIALS 🦄✨
661
+ 🏁🏁 NICE_PARTIALS 🏁🏁
519
662
 
520
663
  ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
521
664
  Warming up --------------------------------------
522
- no slots 11.822k i/100ms
523
- slots 4.204k i/100ms
665
+ baseline 12.072k i/100ms
524
666
  Calculating -------------------------------------
525
- no slots 114.190k (± 4.7%) i/s (8.76 μs/i) - 1.147M in 10.069870s
526
- slots 41.138k (± 4.3%) i/s (24.31 μs/i) - 411.992k in 10.039730s
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
672
+ Calculating -------------------------------------
673
+ slots 35.971k (± 6.1%) i/s (27.80 μs/i) - 181.300k in 5.061126s
527
674
 
528
675
  Comparison:
529
- no slots: 114190.2 i/s
530
- slots: 41137.9 i/s - 2.78x slower
676
+ baseline: 114740.4 i/s
677
+ slots: 35971.0 i/s - 3.19x slower
531
678
 
532
679
 
533
- ✨🦄 VIEW_COMPONENT 🦄✨
680
+ 🏁🏁 VIEW_COMPONENT 🏁🏁
534
681
 
535
682
  ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
536
683
  Warming up --------------------------------------
537
- no slots 20.329k i/100ms
538
- slots 7.409k i/100ms
684
+ baseline 11.991k i/100ms
539
685
  Calculating -------------------------------------
540
- no slots 196.288k4.7%) i/s (5.09 μs/i) - 1.972M in 10.073103s
541
- slots 71.311k (± 5.0%) i/s (14.02 μs/i) - 718.673k in 10.108426s
686
+ baseline 118.532k2.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
691
+ Calculating -------------------------------------
692
+ slots 72.281k (± 6.3%) i/s (13.83 μs/i) - 359.664k in 5.002508s
542
693
 
543
694
  Comparison:
544
- no slots: 196287.6 i/s
545
- slots: 71310.5 i/s - 2.75x slower
695
+ baseline: 118532.2 i/s
696
+ slots: 72281.3 i/s - 1.64x slower
697
+ ```
698
+
699
+ #### Without slots:
700
+
701
+ ```
702
+ ➜ bin/benchmark --no-slots
546
703
 
704
+ 🏁🏁 SLOTIFY 🏁🏁
547
705
 
548
- ✨🦄 SLOTIFY 🦄✨
706
+ ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
707
+ Warming up --------------------------------------
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
549
711
 
550
712
  ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
551
713
  Warming up --------------------------------------
552
- no slots 10.883k i/100ms
553
- slots 77.000 i/100ms
714
+ no slots 11.029k i/100ms
554
715
  Calculating -------------------------------------
555
- no slots 110.153k4.8%) i/s (9.08 μs/i) - 1.099M in 10.009002s
556
- slots 789.118 (± 4.1%) i/s (1.27 ms/i) - 7.931k in 10.071749s
716
+ no slots 110.253k2.0%) i/s (9.07 μs/i) - 551.450k in 5.003625s
557
717
 
558
718
  Comparison:
559
- no slots: 110152.8 i/s
560
- slots: 789.1 i/s - 139.59x slower
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
734
+ Calculating -------------------------------------
735
+ no slots 44.888k (± 3.7%) i/s (22.28 μs/i) - 227.800k in 5.082635s
736
+
737
+ Comparison:
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
561
759
  ```
760
+
761
+ </details>
762
+
763
+ ## Credits
764
+
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).
766
+
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.
@@ -0,0 +1,19 @@
1
+ module Slotify
2
+ module SymbolInflectionHelper
3
+ extend ActiveSupport::Concern
4
+
5
+ mattr_accessor :singularizations, default: {}
6
+
7
+ def singular?(sym)
8
+ singularize(sym.to_sym) == sym.to_sym
9
+ end
10
+
11
+ def plural?(sym)
12
+ !singular?(sym)
13
+ end
14
+
15
+ def singularize(sym)
16
+ singularizations[sym.to_sym] ||= sym.to_s.singularize.to_sym
17
+ end
18
+ end
19
+ end
data/lib/slotify/error.rb CHANGED
@@ -2,18 +2,9 @@ module Slotify
2
2
  class UnknownSlotError < NameError
3
3
  end
4
4
 
5
- class SlotsDefinedError < RuntimeError
6
- end
7
-
8
- class UndefinedSlotError < StandardError
9
- end
10
-
11
5
  class MultipleSlotEntriesError < ArgumentError
12
6
  end
13
7
 
14
- class SlotArgumentError < ArgumentError
15
- end
16
-
17
8
  class StrictSlotsError < ArgumentError
18
9
  end
19
10
 
@@ -3,20 +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 render(options = {}, locals = {}, &block)
9
- @partial = Slotify::Partial.new(self)
10
- super
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 = partial.outer_partial
13
- end
14
-
15
- def capture_with_outer_partial_access(*args, &block)
16
- inner_partial, @partial = partial, partial.outer_partial
17
- inner_partial.capture(*args, &block)
18
- ensure
19
- @partial = inner_partial
12
+ @slotify = inner_slotify
20
13
  end
21
14
 
22
15
  make_compatible_with_slots :url_for, :link_to, :button_to, :link_to_unless_current,
@@ -4,17 +4,20 @@ 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.define_slots!(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
10
- locals = locals.merge(view.partial.slot_locals)
9
+ view.capture_with_outer_slotify_access(&block) if block
10
+
11
+ locals = locals.merge(view.slotify.slot_locals)
11
12
 
12
13
  decorate_strict_slots_errors do
13
14
  super(view, locals, template, layout, block)
14
15
  end
16
+ ensure
17
+ view.slotify = view.slotify.outer_slotify if view.slotify
15
18
  end
16
19
 
17
- def decorate_strict_slots_errors(&block)
20
+ def decorate_strict_slots_errors
18
21
  yield
19
22
  rescue ActionView::Template::Error => error
20
23
  if missing_strict_locals_error?(error)
@@ -31,7 +34,7 @@ module Slotify
31
34
  end
32
35
 
33
36
  def missing_strict_locals_error?(error)
34
- error.template && (defined?(ActionView::StrictLocalsError) && error.cause.is_a?(ActionView::StrictLocalsError)) ||
37
+ error.template && defined?(ActionView::StrictLocalsError) && error.cause.is_a?(ActionView::StrictLocalsError) ||
35
38
  (error.cause.is_a?(ArgumentError) && error.cause.message.match(/missing local/))
36
39
  end
37
40
  end
@@ -31,7 +31,7 @@ module Slotify
31
31
  end
32
32
 
33
33
  def strict_slots_keys
34
- @strict_slots_keys ||= strict_slots!.scan(STRICT_SLOTS_KEYS_REGEX).map(&:first)
34
+ @strict_slots_keys ||= strict_slots!.scan(STRICT_SLOTS_KEYS_REGEX).map(&:first).freeze
35
35
  end
36
36
 
37
37
  def strict_locals!
@@ -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.public_send(:#{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,53 +1,46 @@
1
1
  module Slotify
2
2
  module TagOptionsMerger
3
3
  class << self
4
- include ::ActionView::Helpers::TagHelper
5
-
6
4
  def call(original, target)
7
- original = original.to_h.deep_symbolize_keys
8
- target = target.to_h.deep_symbolize_keys
9
-
10
- target.each do |key, value|
11
- original[key] = case key
12
- when :data
13
- merge_data_options(original[key], value)
14
- when :class
15
- merge_class_options(original[key], value)
16
- else
17
- value
18
- end
19
- end
20
-
21
- original
5
+ mix(original, target)
22
6
  end
23
7
 
24
8
  private
25
9
 
26
- def merge_data_options(original_data, target_data)
27
- original_data = original_data.dup
10
+ # https://www.phlex.fun/sgml/helpers#mix
11
+ def mix(*args)
12
+ args.each_with_object({}) do |object, result|
13
+ result.merge!(object) do |_key, old, new|
14
+ case [old, new].freeze
15
+ in [Array, Array] | [Set, Set]
16
+ old + new
17
+ in [Array, Set]
18
+ old + new.to_a
19
+ in [Array, String]
20
+ old + [new]
21
+ in [Hash, Hash]
22
+ mix(old, new)
23
+ in [Set, Array]
24
+ old.to_a + new
25
+ in [Set, String]
26
+ old.to_a + [new]
27
+ in [String, Array]
28
+ [old] + new
29
+ in [String, Set]
30
+ [old] + new.to_a
31
+ in [String, String]
32
+ "#{old} #{new}"
33
+ in [_, nil]
34
+ old
35
+ else
36
+ new
37
+ end
38
+ end
28
39
 
29
- target_data.each do |key, value|
30
- values = [original_data[key], value]
31
- original_data[key] = if key.in?([:controller, :action]) && all_kind_of?(String, values)
32
- merge_strings(values)
33
- else
34
- value
40
+ result.transform_keys! do |key|
41
+ key.end_with?("!") ? key.name.chop.to_sym : key
35
42
  end
36
43
  end
37
-
38
- original_data
39
- end
40
-
41
- def merge_class_options(original_classes, target_classes)
42
- class_names(original_classes, target_classes)
43
- end
44
-
45
- def merge_strings(*args)
46
- args.map(&:presence).compact.join(" ")
47
- end
48
-
49
- def all_kind_of?(kind, values)
50
- values.none? { !_1.is_a?(kind) }
51
44
  end
52
45
  end
53
46
  end
@@ -1,19 +1,21 @@
1
1
  module Slotify
2
- class Partial
3
- include InflectionHelper
2
+ class Slots
3
+ include SymbolInflectionHelper
4
4
 
5
5
  RESERVED_SLOT_NAMES = [
6
6
  :content, :slot, :value, :content_for,
7
7
  :capture, :yield, :partial
8
8
  ]
9
9
 
10
- attr_reader :outer_partial
10
+ attr_reader :outer_slotify
11
11
 
12
- def initialize(view_context)
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
+
18
+ define_slots!(slots)
17
19
  end
18
20
 
19
21
  def content_for(slot_name)
@@ -50,8 +52,6 @@ module Slotify
50
52
  end
51
53
 
52
54
  def slot_locals
53
- validate_slots!
54
-
55
55
  pairs = @defined_slots.map do |slot_name|
56
56
  slot_values = values.for(slot_name)
57
57
  slot_values = singular?(slot_name) ? slot_values&.first : slot_values
@@ -65,50 +65,56 @@ module Slotify
65
65
  end.to_h
66
66
  end
67
67
 
68
- def define_slots!(slot_names)
69
- raise SlotsDefinedError, "Slots cannot be redefined" unless @defined_slots.nil?
68
+ private
70
69
 
70
+ attr_reader :values
71
+
72
+ def slot?(slot_name)
73
+ slot_name && @defined_slots.include?(slot_name.to_sym)
74
+ end
75
+
76
+ def define_slots!(slot_names)
71
77
  @defined_slots = slot_names.map(&:to_sym).each do |slot_name|
72
78
  if RESERVED_SLOT_NAMES.include?(singularize(slot_name))
73
- raise ReservedSlotNameError, ":#{slot_name} is a reserved word and cannot be used as a slot name"
79
+ raise ReservedSlotNameError,
80
+ ":#{slot_name} is a reserved word and cannot be used as a slot name"
74
81
  end
75
- end
76
- end
77
82
 
78
- def respond_to_missing?(name, include_private = false)
79
- name.start_with?("with_") || slot?(name)
80
- end
81
-
82
- def method_missing(name, *args, **options, &block)
83
- if name.start_with?("with_")
84
- values.add(name.to_s.delete_prefix("with_"), args, options, block)
85
- elsif slot?(name)
86
- content_for(name)
87
- else
88
- super
83
+ if singular?(slot_name)
84
+ define_single_value_slot_method(slot_name)
85
+ else
86
+ define_multi_value_slot_methods(slot_name)
87
+ end
89
88
  end
90
89
  end
91
90
 
92
- private
91
+ def define_single_value_slot_method(slot_name)
92
+ method_name = :"with_#{slot_name}"
93
93
 
94
- attr_reader :values
94
+ return if respond_to?(method_name)
95
95
 
96
- def slot?(slot_name)
97
- slot_name && @defined_slots.include?(slot_name.to_sym)
96
+ self.class.define_method(method_name) do |*args, **options, &block|
97
+ if values.for(slot_name).any?
98
+ raise MultipleSlotEntriesError,
99
+ "slot :#{slot_name} is defined as a single-value slot but was called multiple times"
100
+ end
101
+
102
+ values.add(slot_name, args, options, block)
103
+ end
98
104
  end
99
105
 
100
- def validate_slots!
101
- return if @defined_slots.nil?
106
+ def define_multi_value_slot_methods(slot_name)
107
+ method_name = :"with_#{slot_name}"
108
+ singular_slot_name = singularize(slot_name)
109
+
110
+ return if respond_to?(method_name)
102
111
 
103
- undefined_slots = values.slot_names - @defined_slots.map { singularize(_1) }
104
- if undefined_slots.any?
105
- raise UndefinedSlotError,
106
- "missing slot #{"definition".pluralize(undefined_slots.size)} for `#{undefined_slots.map { ":#{_1}(s)" }.join(", ")}`"
112
+ self.class.define_method(method_name) do |*args, **options, &block|
113
+ values.add(slot_name, args, options, block)
107
114
  end
108
115
 
109
- @defined_slots.filter { singular?(_1) }.each do |slot_name|
110
- slot_values = values.for(slot_name)
111
- raise MultipleSlotEntriesError, "slot :#{slot_name} called #{slot_values.size} times (expected 1)" if slot_values.many?
116
+ self.class.define_method(:"with_#{singular_slot_name}") do |*args, **options, &block|
117
+ values.add(singular_slot_name, args, options, block)
112
118
  end
113
119
  end
114
120
  end
data/lib/slotify/value.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Slotify
2
2
  class Value
3
- include InflectionHelper
3
+ include SymbolInflectionHelper
4
4
 
5
5
  attr_reader :slot_name, :args, :block
6
6
 
@@ -47,7 +47,7 @@ module Slotify
47
47
  alias_method :to_hash, :to_h
48
48
 
49
49
  def with_partial_path(partial_path)
50
- Value.new(@view_context, @args, options, @block, slot_name: @slot_name, partial_path:)
50
+ Value.new(@view_context, @args, @options, @block, slot_name: @slot_name, partial_path:)
51
51
  end
52
52
 
53
53
  def with_default_options(default_options)
@@ -61,7 +61,7 @@ module Slotify
61
61
 
62
62
  def method_missing(name, ...)
63
63
  if name.start_with?("to_")
64
- @args.first.public_send(name, ...)
64
+ content.public_send(name, ...)
65
65
  else
66
66
  super
67
67
  end
@@ -72,7 +72,7 @@ module Slotify
72
72
  end
73
73
 
74
74
  def render_in(view_context, &block)
75
- view_context.render partial_path, **@options.to_h, &@block || block
75
+ view_context.render partial_path, **@options.to_h, &block || @block
76
76
  end
77
77
 
78
78
  private
@@ -1,6 +1,6 @@
1
1
  module Slotify
2
2
  class ValueStore
3
- include InflectionHelper
3
+ include SymbolInflectionHelper
4
4
 
5
5
  def initialize(view_context)
6
6
  @view_context = view_context
@@ -13,11 +13,9 @@ module Slotify
13
13
 
14
14
  def add(slot_name, args = [], options = {}, block = nil)
15
15
  if plural?(slot_name)
16
- Array.wrap(args.first).map { add(singularize(slot_name), _1, options, block) }
16
+ Array.wrap(args.shift).map { add(singularize(slot_name), [_1, *args], options, block) }
17
17
  else
18
- MethodArgsResolver.call(args, options, block) do
19
- @values << Value.new(@view_context, _1, _2, _3, slot_name: singularize(slot_name))
20
- end
18
+ @values << Value.new(@view_context, args, options, block, slot_name: singularize(slot_name))
21
19
  end
22
20
  end
23
21
 
@@ -1,3 +1,3 @@
1
1
  module Slotify
2
- VERSION = "0.0.4"
2
+ VERSION = "0.1.0"
3
3
  end
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.4
4
+ version: 0.1.0
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
@@ -46,15 +46,15 @@ extra_rdoc_files: []
46
46
  files:
47
47
  - README.md
48
48
  - lib/slotify.rb
49
- - lib/slotify/concerns/inflection_helper.rb
50
49
  - lib/slotify/concerns/slot_compatability.rb
50
+ - lib/slotify/concerns/symbol_inflection_helper.rb
51
51
  - lib/slotify/error.rb
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.3.3
82
+ rubygems_version: 3.5.10
83
83
  signing_key:
84
84
  specification_version: 4
85
85
  summary: Superpowered slots for your Rails partials.
@@ -1,18 +0,0 @@
1
- module Slotify
2
- module InflectionHelper
3
- extend ActiveSupport::Concern
4
-
5
- def singular?(str)
6
- str = str.to_s
7
- str.singularize == str && str.pluralize != str
8
- end
9
-
10
- def singularize(sym)
11
- sym.to_s.singularize.to_sym
12
- end
13
-
14
- def plural?(str)
15
- !singular?(str)
16
- end
17
- end
18
- end