slotify 0.0.1 → 0.0.4
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 +316 -83
- data/lib/slotify/concerns/slot_compatability.rb +20 -0
- data/lib/slotify/error.rb +4 -1
- data/lib/slotify/extensions/base.rb +3 -11
- data/lib/slotify/extensions/partial_renderer.rb +3 -3
- data/lib/slotify/extensions/template.rb +6 -4
- data/lib/slotify/partial.rb +56 -65
- data/lib/slotify/value.rb +20 -19
- data/lib/slotify/value_store.rb +28 -0
- data/lib/slotify/version.rb +1 -1
- metadata +5 -4
- data/lib/slotify/concerns/helpers_concern.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3528c82a3e01b2acc454f5df4c8081f19157e975a2e2ddaa46f56dadc565426f
|
4
|
+
data.tar.gz: e9660ecb767e9ec12156eb3e878f10cdf9fe74c8b67b9fbc574fa64cae1f5f1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe68d4ff07cbc1a12cc69937bb0da7af4080082504226f7dd9c63a8f4b35461460671265ca09745a9899e6e49ac5aab0c377a83d795a2547eb423255907aa84d
|
7
|
+
data.tar.gz: a189cd09e36a1eab05de32a1c62f953acbe72dcca1ed64c0dd058002ded2f8da2d828e95e0ba0b7865a29d3cac4c58a1cc9c7ad45679ce87684a8edfe03e1032
|
data/README.md
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
<img src=".github/assets/slotify_wordmark.svg" width="200">
|
2
2
|
|
3
|
+
<p><a href="https://rubygems.org/gems/slotify"><img src="https://img.shields.io/gem/v/slotify" alt="Gem version"></a>
|
4
|
+
<a href="https://github.com/allmarkedup/slotify/actions/workflows/ci.yml"><img src="https://github.com/allmarkedup/slotify/actions/workflows/ci.yml/badge.svg" alt="CI status"></a></p>
|
3
5
|
|
6
|
+
## Superpowered slots for ActionView partials
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
---
|
8
|
-
|
9
|
-
## Overview
|
10
|
-
|
11
|
-
Slotify adds an unobtrusive (but powerful!) **content slot API** to ActionView partials.
|
8
|
+
Slotify adds an unobtrusive (but powerful!) **content slot API** to ActionView partials.
|
12
9
|
|
13
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.
|
14
11
|
|
15
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/).
|
16
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.
|
17
|
+
|
17
18
|
###
|
18
19
|
|
19
20
|
## Slotify basics
|
20
21
|
|
21
|
-
Slotify slots are defined using a **[strict locals](https://guides.rubyonrails.org/action_view_overview.html#strict-locals)-style magic comment** at the top of partial templates ([more details here](#defining-slots)).
|
22
|
+
Slotify slots are defined using a **[strict locals](https://guides.rubyonrails.org/action_view_overview.html#strict-locals)-style magic comment** at the top of **partial templates** ([more details here](#defining-slots)).
|
22
23
|
|
23
24
|
```erb
|
24
|
-
<%# slots: (
|
25
|
+
<%# slots: (title:, body: nil, theme: "default") -%>
|
25
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:
|
@@ -29,10 +30,10 @@ Slot content is accessed via **standard local variables** within the partial. So
|
|
29
30
|
```erb
|
30
31
|
<!-- _article.html.erb -->
|
31
32
|
|
32
|
-
<%# slots: (
|
33
|
+
<%# slots: (title: "Default title", body: nil) -%>
|
33
34
|
|
34
35
|
<article>
|
35
|
-
<h1><%=
|
36
|
+
<h1><%= title %></h1>
|
36
37
|
<% if body.present? %>
|
37
38
|
<div>
|
38
39
|
<%= body %>
|
@@ -46,11 +47,11 @@ Slot content is accessed via **standard local variables** within the partial. So
|
|
46
47
|
|
47
48
|
When the partial is rendered, a special `partial` object is yielded as an argument to the block. Slot content is set by calling the appropriate `#with_<slot_name>` methods on this partial object.
|
48
49
|
|
49
|
-
For example, here our `article` partial is being rendered with content for the `
|
50
|
+
For example, here our `article` partial is being rendered with content for the `title` and `body` slots that were defined above:
|
50
51
|
|
51
52
|
```erb
|
52
53
|
<%= render "article" do |partial| %>
|
53
|
-
<% partial.
|
54
|
+
<% partial.with_title "This is a title" %>
|
54
55
|
<% partial.with_body do %>
|
55
56
|
<p>You can use <%= tag.strong "markup" %> within slot content blocks without
|
56
57
|
having to worry about marking the output as <code>html_safe</code> later.</p>
|
@@ -61,7 +62,82 @@ For example, here our `article` partial is being rendered with content for the `
|
|
61
62
|
> [!NOTE]
|
62
63
|
> _If you've ever used [ViewComponent](https://viewcomponent.org) then the above code should also feel quite familiar to you - it's pretty much the same syntax used to provide content to [component slots](https://viewcomponent.org/guide/slots.html)._
|
63
64
|
|
64
|
-
But this example just scratches the surface of what Slotify slots can do
|
65
|
+
But this example just scratches the surface of what Slotify slots can do. Have a look at the more full-featured example below or jump to [the usage information](#usage).
|
66
|
+
|
67
|
+
<details>
|
68
|
+
<summary><h4>More full-featured example</h4></summary>
|
69
|
+
|
70
|
+
```erb
|
71
|
+
<!-- views/_example.html.erb -->
|
72
|
+
|
73
|
+
<%# locals: (id:) -%>
|
74
|
+
<%# slots: (title: "Example title", lists: nil, quotes: nil, website_link:) -%>
|
75
|
+
|
76
|
+
<%= tag.section id: do %>
|
77
|
+
<h1 class="example-title">
|
78
|
+
<%= title %>
|
79
|
+
</h1>
|
80
|
+
|
81
|
+
<p>Example link: <%= link_to website_link, data: {controller: "external-link"} %></p>
|
82
|
+
|
83
|
+
<%= render lists, title: "Default title" %>
|
84
|
+
|
85
|
+
<% if quotes.any? %>
|
86
|
+
<h3>Quotes</h3>
|
87
|
+
<% quotes.each do |quote| %>
|
88
|
+
<blockquote <%= quote.options.except(:citation) %>>
|
89
|
+
<%= quote %>
|
90
|
+
<%== "— #{tag.cite(quote.options.citation)}" if quote.options.citation.present? %>
|
91
|
+
</blockquote>
|
92
|
+
<% end %>
|
93
|
+
<% end %>
|
94
|
+
<% end %>
|
95
|
+
```
|
96
|
+
|
97
|
+
```erb
|
98
|
+
<!-- views/_list.html.erb -->
|
99
|
+
|
100
|
+
<%# locals: (title:) -%>
|
101
|
+
<%# slots: (items: nil) -%>
|
102
|
+
|
103
|
+
<h3><%= title %></h3>
|
104
|
+
|
105
|
+
<% if items.any? %>
|
106
|
+
<%= tag.ul class: "list" do %>
|
107
|
+
<%= content_tag :li, items, class: "list-item" %>
|
108
|
+
<% end %>
|
109
|
+
<% end %>
|
110
|
+
```
|
111
|
+
|
112
|
+
```erb
|
113
|
+
<!-- views/slotify.html.erb -->
|
114
|
+
|
115
|
+
<%= render "example", id: "slotify-example" do |partial| %>
|
116
|
+
<% partial.with_subtitle do %>
|
117
|
+
This is the <%= tag.em "subtitle" %>
|
118
|
+
<% end %>
|
119
|
+
|
120
|
+
<% partial.with_website_link "example.com", "https://example.com", target: "_blank", data: {controller: "preview-link"} %>
|
121
|
+
|
122
|
+
<% partial.with_list do |list| %>
|
123
|
+
<% list.with_item "first thing" %>
|
124
|
+
<% list.with_item "second thing", class: "text-green-700" %>
|
125
|
+
<% list.with_item "third thing" %>
|
126
|
+
<% end %>
|
127
|
+
|
128
|
+
<% partial.with_quote citation: "A. Person", class: "text-lg" do %>
|
129
|
+
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
|
130
|
+
<% end %>
|
131
|
+
|
132
|
+
<% partial.with_quote do %>
|
133
|
+
<p>Non quos explicabo eius hic quaerat laboriosam incidunt numquam.</p>
|
134
|
+
<% end %>
|
135
|
+
<% end %>
|
136
|
+
```
|
137
|
+
|
138
|
+
</details>
|
139
|
+
|
140
|
+
---
|
65
141
|
|
66
142
|
## Usage
|
67
143
|
|
@@ -71,7 +147,7 @@ But this example just scratches the surface of what Slotify slots can do! Read o
|
|
71
147
|
Slots are defined using a [strict locals](https://guides.rubyonrails.org/action_view_overview.html#strict-locals)-style magic comment at the top of the partial template. The `slots:` signature uses the same syntax as standard Ruby method signatures:
|
72
148
|
|
73
149
|
```erb
|
74
|
-
<%# slots: (title:,
|
150
|
+
<%# slots: (title:, body: "No content available", author: nil) -%>
|
75
151
|
```
|
76
152
|
|
77
153
|
#### Required slots
|
@@ -110,11 +186,11 @@ either above or below the `slots` definition.
|
|
110
186
|
<!-- _article.html.erb -->
|
111
187
|
|
112
188
|
<%# locals: (title:) -%>
|
113
|
-
<%# slots: (
|
189
|
+
<%# slots: (body: "No content available") -%>
|
114
190
|
|
115
191
|
<article>
|
116
192
|
<h1><%= title %></h1>
|
117
|
-
<div><%=
|
193
|
+
<div><%= body %></div>
|
118
194
|
</article>
|
119
195
|
```
|
120
196
|
|
@@ -122,28 +198,72 @@ Locals are provided when rendering the partial in the usual way.
|
|
122
198
|
|
123
199
|
```erb
|
124
200
|
<%= render "article", title: "Article title here" do |partial| %>
|
125
|
-
<% partial.
|
126
|
-
<p>
|
201
|
+
<% partial.with_body do %>
|
202
|
+
<p>Body content here...</p>
|
127
203
|
<% end %>
|
128
204
|
<% end %>
|
129
205
|
```
|
130
206
|
|
131
|
-
###
|
207
|
+
### Setting slot values
|
132
208
|
|
133
|
-
Content is passed into slots using dynamically generated `partial#with_<slot_name>` methods.
|
209
|
+
Content is passed into slots using dynamically generated `partial#with_<slot_name>` writer methods.
|
134
210
|
|
135
211
|
Content can be provided as either the **first argument** or **as a block** when calling these methods at render time.
|
136
|
-
The following
|
212
|
+
The following two examples are equivalent:
|
137
213
|
|
138
214
|
```erb
|
139
215
|
<%= render "example" do |partial| %>
|
140
216
|
<% partial.with_title "Title passed as argument" %>
|
217
|
+
<% end %>
|
218
|
+
```
|
219
|
+
|
220
|
+
```erb
|
221
|
+
<%= render "example" do |partial| %>
|
141
222
|
<% partial.with_title do %>
|
142
223
|
Title passed as block content
|
143
224
|
<% end %>
|
144
225
|
<% end %>
|
145
226
|
```
|
146
227
|
|
228
|
+
> [!TIP]
|
229
|
+
> Block content is generally better suited for longer-form content containing HTML tags because it will not need to be marked
|
230
|
+
as `html_safe` when used in the partial template.
|
231
|
+
|
232
|
+
The content will be available as a local variable in the partial template whichever way it is provided.
|
233
|
+
|
234
|
+
```erb
|
235
|
+
<%# slots: (title:) -%>
|
236
|
+
<h1><%= title %></h1>
|
237
|
+
```
|
238
|
+
|
239
|
+
### Slot options
|
240
|
+
|
241
|
+
The slot value writer methods also accept optional arbitrary keyword arguments.
|
242
|
+
These can then be accessed in the partial template via the `.options` method on the slot variable.
|
243
|
+
|
244
|
+
```erb
|
245
|
+
<%= render "example" do |partial| %>
|
246
|
+
<% partial.with_title "The title", class: "color-hotpink", data: {controller: "fancy-title"} %>
|
247
|
+
<% end %>
|
248
|
+
```
|
249
|
+
|
250
|
+
```erb
|
251
|
+
<%# slots: (title:) -%>
|
252
|
+
|
253
|
+
<%= title.options.keys %> <!-- [:class, :data] -->
|
254
|
+
<%= title %> <!-- The title -->
|
255
|
+
```
|
256
|
+
|
257
|
+
Slot options can be useful for providing tag attributes when rendering slot content or rendering variants
|
258
|
+
of a slot based on an option value.
|
259
|
+
|
260
|
+
When rendered as a string the options are passed through the Rails `tag.attributes` helper to generate an HTML tag attributes string:
|
261
|
+
|
262
|
+
```erb
|
263
|
+
<h1 <%= title.options %>><%= title %></h1>
|
264
|
+
<!-- <h1 class="color-hotpink" data-controller="fancy-title">The title</h1> -->
|
265
|
+
```
|
266
|
+
|
147
267
|
### Slot types
|
148
268
|
|
149
269
|
There are two types of slots.
|
@@ -194,7 +314,9 @@ Multiple-value slots are defined using a **plural** slot name:
|
|
194
314
|
```
|
195
315
|
|
196
316
|
Multiple-value slots can be called as many times as needed
|
197
|
-
and their corresponding template variable represents an array of values
|
317
|
+
and their corresponding template variable represents an array of values.
|
318
|
+
|
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).
|
198
320
|
|
199
321
|
```erb
|
200
322
|
<%= render "example" do |partial| %>
|
@@ -218,17 +340,134 @@ and their corresponding template variable represents an array of values:
|
|
218
340
|
</ul>
|
219
341
|
```
|
220
342
|
|
221
|
-
###
|
343
|
+
### Using slots with helpers
|
222
344
|
|
223
345
|
> _Docs coming soon..._
|
224
346
|
|
347
|
+
```erb
|
348
|
+
<% partial.with_title "The title", class: "color-hotpink" %>
|
349
|
+
<% partial.with_website_link "Example website", "https://example.com", data: {controller: "external-link"} %>
|
350
|
+
|
351
|
+
<% partial.with_item "Item one" %>
|
352
|
+
<% partial.with_item "Item two", class: "highlight" %>
|
353
|
+
```
|
354
|
+
|
355
|
+
```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> -->
|
358
|
+
|
359
|
+
<%= link_to website_link %> <!-- <a href="https://example.com" data-controller="external-link">Example website</a> -->
|
360
|
+
|
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> -->
|
363
|
+
```
|
364
|
+
|
225
365
|
### Rendering slots
|
226
366
|
|
227
367
|
> _Docs coming soon..._
|
228
368
|
|
229
|
-
### Slot
|
369
|
+
### Slot values API
|
230
370
|
|
231
|
-
|
371
|
+
**Singlular slot value variables** in partial templates are actually instances of `Slotity::Value`.
|
372
|
+
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
|
+
|
374
|
+
|
375
|
+
|
376
|
+
```erb
|
377
|
+
<%= render "example" do |partial| %>
|
378
|
+
<% partial.with_title class: "color-hotpink" do %>
|
379
|
+
The title
|
380
|
+
<% end %>
|
381
|
+
<% end %>
|
382
|
+
```
|
383
|
+
|
384
|
+
```erb
|
385
|
+
<% title.is_a?(Slotify::Value) %> <!-- true -->
|
386
|
+
<% items.is_a?(Slotify::ValueCollection) %> <!-- true -->
|
387
|
+
|
388
|
+
<%= title %> <!-- "The title" -->
|
389
|
+
<% title.content %> <!-- "The title" -->
|
390
|
+
|
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) -->
|
393
|
+
```
|
394
|
+
|
395
|
+
**Plural slot value variables** in partial templates are instances of the enumerable `Slotify::ValueCollection` class, with all items instances of `Slotity::Value`.
|
396
|
+
|
397
|
+
```erb
|
398
|
+
<%= render "example" do |partial| %>
|
399
|
+
<% partial.with_item "Item one" %>
|
400
|
+
<% partial.with_item "Item two", class: "current" %>
|
401
|
+
<% end %>
|
402
|
+
```
|
403
|
+
|
404
|
+
```erb
|
405
|
+
<% items.is_a?(Slotify::ValueCollection) %> <!-- true -->
|
406
|
+
|
407
|
+
<% items.each do |item| %>
|
408
|
+
<li <%= item.options %>><%= item %></li>
|
409
|
+
<% end %>
|
410
|
+
<!-- <li>Item one</li> <li class="current">Item two</li> -->
|
411
|
+
|
412
|
+
<%= items %> <!-- "Item one Item two" -->
|
413
|
+
```
|
414
|
+
|
415
|
+
#### `Slotity::Value`
|
416
|
+
|
417
|
+
The following methods are available on `Slotity::Value` instances:
|
418
|
+
|
419
|
+
**`.content`**
|
420
|
+
|
421
|
+
Returns the slot content string that was provided as the first argument or as the block when calling the slot writer method.
|
422
|
+
|
423
|
+
**`.options`**
|
424
|
+
|
425
|
+
Returns a `Slotify::ValueOptions` instance that can be treated like a `Hash`. Calling `.slice` or `.except` on this will return another `Slotify::ValueOptions` instance.
|
426
|
+
|
427
|
+
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
|
+
|
429
|
+
**`.with_default_options(default_options)`**
|
430
|
+
|
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.
|
432
|
+
|
433
|
+
```erb
|
434
|
+
<% title_with_default_opts = title.with_default_options(class: "size-lg", aria: {level: 1}) %> <!-- apply default options -->
|
435
|
+
|
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'" -->
|
438
|
+
```
|
439
|
+
|
440
|
+
## Slotify vs alternatives
|
441
|
+
|
442
|
+
#### `nice_partials`
|
443
|
+
|
444
|
+
Slotify was very much inspired by the [Nice Partials gem](https://github.com/bullet-train-co/nice_partials) and both provide similar functionality.
|
445
|
+
However there are a number of key differences:
|
446
|
+
|
447
|
+
* Slotify requires the explicit definition of slots using 'strict locals'-style comments;
|
448
|
+
Nice partials slots are implicitly defined when rendering the partial.
|
449
|
+
* 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.
|
452
|
+
* 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
|
454
|
+
|
455
|
+
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
|
+
separation of 'nice partial' functionality from ActionView-provided locals etc.
|
457
|
+
|
458
|
+
#### `view_component`
|
459
|
+
|
460
|
+
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.
|
463
|
+
|
464
|
+
However apart from that they are quite different. Slotify adds functionality to regular ActionView partials whereas ViewComponent provides a complete standalone component system.
|
465
|
+
|
466
|
+
Each ViewComponent has an associated class which can be used to extract and encapsulate view logic.
|
467
|
+
Slotify doesn't have an analagous concept, any view-specific logic will by default live in the partial template (as per standard partial rendering patterns).
|
468
|
+
|
469
|
+
You might choose Slotify if you want a more 'component-y' API but you don't want the overhead or learning curve associated with a tool that sits somewhat adjacent to the standard Rails way of doing things.
|
470
|
+
But if you have components with a lot of view logic or want a more formalised component format then ViewComponent is likely a better fit for your project.
|
232
471
|
|
233
472
|
## Installation
|
234
473
|
|
@@ -251,78 +490,72 @@ Slotify was inspired by the excellent [nice_partials gem](https://github.com/bul
|
|
251
490
|
|
252
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)!
|
253
492
|
|
254
|
-
|
493
|
+
## Benchmarks
|
255
494
|
|
256
|
-
|
495
|
+
Slotify is still in the early stages of development and no attempt has yet been made to optimise rendering performance.
|
257
496
|
|
258
|
-
|
497
|
+
Below are some initial (crude) benchmarking comparisons with other similar gems.
|
259
498
|
|
260
|
-
|
261
|
-
|
499
|
+
> [!TIP]
|
500
|
+
> Benchmarks can be run using the `bin/benchmarks` command from the repository root.
|
262
501
|
|
263
|
-
|
264
|
-
|
502
|
+
```
|
503
|
+
✨🦄 ACTION_VIEW 🦄✨
|
265
504
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
505
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
506
|
+
Warming up --------------------------------------
|
507
|
+
no slots 12.997k i/100ms
|
508
|
+
slots 10.891k i/100ms
|
509
|
+
Calculating -------------------------------------
|
510
|
+
no slots 125.622k (± 5.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
|
272
512
|
|
273
|
-
|
513
|
+
Comparison:
|
514
|
+
no slots: 125621.9 i/s
|
515
|
+
slots: 108467.5 i/s - 1.16x slower
|
274
516
|
|
275
|
-
<% if quotes.any? %>
|
276
|
-
<h3>Quotes</h3>
|
277
|
-
<% quotes.each do |quote| %>
|
278
|
-
<blockquote <%= quote.options.except(:citation) %>>
|
279
|
-
<%= quote %>
|
280
|
-
<%== "— #{tag.cite(quote.options.citation)}" if quote.options.citation.present? %>
|
281
|
-
</blockquote>
|
282
|
-
<% end %>
|
283
|
-
<% end %>
|
284
|
-
<% end %>
|
285
|
-
```
|
286
517
|
|
287
|
-
|
288
|
-
<!-- views/_list.html.erb -->
|
518
|
+
✨🦄 NICE_PARTIALS 🦄✨
|
289
519
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
520
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
521
|
+
Warming up --------------------------------------
|
522
|
+
no slots 11.822k i/100ms
|
523
|
+
slots 4.204k i/100ms
|
524
|
+
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
|
294
527
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
<% end %>
|
299
|
-
<% end %>
|
300
|
-
```
|
528
|
+
Comparison:
|
529
|
+
no slots: 114190.2 i/s
|
530
|
+
slots: 41137.9 i/s - 2.78x slower
|
301
531
|
|
302
|
-
```erb
|
303
|
-
<!-- views/slotify.html.erb -->
|
304
532
|
|
305
|
-
|
306
|
-
<% partial.with_subtitle do %>
|
307
|
-
This is the <%= tag.em "subtitle" %>
|
308
|
-
<% end %>
|
533
|
+
✨🦄 VIEW_COMPONENT 🦄✨
|
309
534
|
|
310
|
-
|
535
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
536
|
+
Warming up --------------------------------------
|
537
|
+
no slots 20.329k i/100ms
|
538
|
+
slots 7.409k i/100ms
|
539
|
+
Calculating -------------------------------------
|
540
|
+
no slots 196.288k (± 4.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
|
311
542
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
<% list.with_item "third thing" %>
|
316
|
-
<% end %>
|
543
|
+
Comparison:
|
544
|
+
no slots: 196287.6 i/s
|
545
|
+
slots: 71310.5 i/s - 2.75x slower
|
317
546
|
|
318
|
-
<% partial.with_quote citation: "A. Person", class: "text-lg" do %>
|
319
|
-
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
|
320
|
-
<% end %>
|
321
547
|
|
322
|
-
|
323
|
-
<p>Non quos explicabo eius hic quaerat laboriosam incidunt numquam.</p>
|
324
|
-
<% end %>
|
325
|
-
<% end %>
|
326
|
-
```
|
548
|
+
✨🦄 SLOTIFY 🦄✨
|
327
549
|
|
550
|
+
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
|
551
|
+
Warming up --------------------------------------
|
552
|
+
no slots 10.883k i/100ms
|
553
|
+
slots 77.000 i/100ms
|
554
|
+
Calculating -------------------------------------
|
555
|
+
no slots 110.153k (± 4.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
|
328
557
|
|
558
|
+
Comparison:
|
559
|
+
no slots: 110152.8 i/s
|
560
|
+
slots: 789.1 i/s - 139.59x slower
|
561
|
+
```
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Slotify
|
2
|
+
module SlotCompatability
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def make_compatible_with_slots(*method_names)
|
7
|
+
proxy = Module.new
|
8
|
+
method_names.each do |name|
|
9
|
+
proxy.define_method(name) do |*args, **kwargs, &block|
|
10
|
+
return super(*args, **kwargs, &block) if args.none?
|
11
|
+
|
12
|
+
results = MethodArgsResolver.call(args, kwargs, block) { super(*_1, **_2, &_3) }
|
13
|
+
results.reduce(ActiveSupport::SafeBuffer.new) { _1 << _2 }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
prepend proxy
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/slotify/error.rb
CHANGED
@@ -2,7 +2,7 @@ module Slotify
|
|
2
2
|
class UnknownSlotError < NameError
|
3
3
|
end
|
4
4
|
|
5
|
-
class
|
5
|
+
class SlotsDefinedError < RuntimeError
|
6
6
|
end
|
7
7
|
|
8
8
|
class UndefinedSlotError < StandardError
|
@@ -16,4 +16,7 @@ module Slotify
|
|
16
16
|
|
17
17
|
class StrictSlotsError < ArgumentError
|
18
18
|
end
|
19
|
+
|
20
|
+
class ReservedSlotNameError < ArgumentError
|
21
|
+
end
|
19
22
|
end
|
@@ -1,25 +1,17 @@
|
|
1
1
|
module Slotify
|
2
2
|
module Extensions
|
3
3
|
module Base
|
4
|
-
|
4
|
+
include SlotCompatability
|
5
5
|
|
6
|
-
|
6
|
+
attr_accessor :partial
|
7
7
|
|
8
|
-
def render(
|
8
|
+
def render(options = {}, locals = {}, &block)
|
9
9
|
@partial = Slotify::Partial.new(self)
|
10
10
|
super
|
11
11
|
ensure
|
12
12
|
@partial = partial.outer_partial
|
13
13
|
end
|
14
14
|
|
15
|
-
def _layout_for(*args, &block)
|
16
|
-
if block && args.first.is_a?(Symbol)
|
17
|
-
capture_with_outer_partial_access(*args, &block)
|
18
|
-
else
|
19
|
-
super
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
15
|
def capture_with_outer_partial_access(*args, &block)
|
24
16
|
inner_partial, @partial = partial, partial.outer_partial
|
25
17
|
inner_partial.capture(*args, &block)
|
@@ -4,10 +4,10 @@ module Slotify
|
|
4
4
|
def render_partial_template(view, locals, template, layout, block)
|
5
5
|
return super unless template.strict_slots?
|
6
6
|
|
7
|
-
|
7
|
+
view.partial.define_slots!(template.strict_slots_keys)
|
8
|
+
|
8
9
|
view.capture_with_outer_partial_access(&block) if block
|
9
|
-
|
10
|
-
locals = locals.merge(partial.slot_locals)
|
10
|
+
locals = locals.merge(view.partial.slot_locals)
|
11
11
|
|
12
12
|
decorate_strict_slots_errors do
|
13
13
|
super(view, locals, template, layout, block)
|
@@ -3,16 +3,17 @@ require "action_view/template/error"
|
|
3
3
|
module Slotify
|
4
4
|
module Extensions
|
5
5
|
module Template
|
6
|
+
STRICT_SLOTS_NONE = Object.new
|
6
7
|
STRICT_SLOTS_REGEX = /\#\s+slots:\s+\((.*)\)/
|
7
8
|
STRICT_SLOTS_KEYS_REGEX = /(\w+):(?=(?:[^"\\]*(?:\\.|"(?:[^"\\]*\\.)*[^"\\]*"))*[^"]*$)/
|
8
9
|
|
9
10
|
def initialize(...)
|
10
11
|
super
|
11
|
-
@strict_slots =
|
12
|
+
@strict_slots = STRICT_SLOTS_NONE
|
12
13
|
end
|
13
14
|
|
14
15
|
def strict_slots!
|
15
|
-
if @strict_slots ==
|
16
|
+
if @strict_slots == STRICT_SLOTS_NONE
|
16
17
|
source.sub!(STRICT_SLOTS_REGEX, "")
|
17
18
|
strict_slots = $1
|
18
19
|
@strict_slots = if strict_slots.nil?
|
@@ -30,7 +31,7 @@ module Slotify
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def strict_slots_keys
|
33
|
-
strict_slots!.scan(STRICT_SLOTS_KEYS_REGEX).map(&:first)
|
34
|
+
@strict_slots_keys ||= strict_slots!.scan(STRICT_SLOTS_KEYS_REGEX).map(&:first)
|
34
35
|
end
|
35
36
|
|
36
37
|
def strict_locals!
|
@@ -41,8 +42,9 @@ module Slotify
|
|
41
42
|
|
42
43
|
def locals_code
|
43
44
|
return super unless strict_slots?
|
45
|
+
|
44
46
|
strict_slots_keys.each_with_object(+super) do |key, code|
|
45
|
-
code << "
|
47
|
+
code << "partial.set_slot_default(:#{key}, binding.local_variable_get(:#{key})); #{key} = partial.public_send(:#{key});"
|
46
48
|
end
|
47
49
|
end
|
48
50
|
end
|
data/lib/slotify/partial.rb
CHANGED
@@ -2,32 +2,31 @@ module Slotify
|
|
2
2
|
class Partial
|
3
3
|
include InflectionHelper
|
4
4
|
|
5
|
+
RESERVED_SLOT_NAMES = [
|
6
|
+
:content, :slot, :value, :content_for,
|
7
|
+
:capture, :yield, :partial
|
8
|
+
]
|
9
|
+
|
5
10
|
attr_reader :outer_partial
|
6
11
|
|
7
12
|
def initialize(view_context)
|
8
13
|
@view_context = view_context
|
9
14
|
@outer_partial = view_context.partial
|
10
|
-
@values =
|
11
|
-
@
|
15
|
+
@values = ValueStore.new(@view_context)
|
16
|
+
@defined_slots = nil
|
12
17
|
end
|
13
18
|
|
14
|
-
def content_for(slot_name
|
15
|
-
raise
|
16
|
-
raise UnknownSlotError, "unknown slot :#{slot_name}" unless slot_defined?(slot_name)
|
17
|
-
|
18
|
-
values = slot_values(slot_name)
|
19
|
-
if values.none? && !fallback_value.nil?
|
20
|
-
values = add_values(slot_name, Array(fallback_value))
|
21
|
-
end
|
19
|
+
def content_for(slot_name)
|
20
|
+
raise UnknownSlotError, "unknown slot :#{slot_name}" unless slot?(slot_name)
|
22
21
|
|
23
|
-
|
22
|
+
slot_values = values.for(slot_name)
|
23
|
+
singular?(slot_name) ? slot_values.first : ValueCollection.new(slot_values)
|
24
24
|
end
|
25
25
|
|
26
26
|
def content_for?(slot_name)
|
27
|
-
raise
|
28
|
-
raise UnknownSlotError, "unknown slot :#{slot_name}" unless slot_defined?(slot_name)
|
27
|
+
raise UnknownSlotError, "unknown slot :#{slot_name}" unless slot?(slot_name)
|
29
28
|
|
30
|
-
|
29
|
+
values.for(slot_name).any?
|
31
30
|
end
|
32
31
|
|
33
32
|
def capture(*args, &block)
|
@@ -35,19 +34,30 @@ module Slotify
|
|
35
34
|
end
|
36
35
|
|
37
36
|
def yield(*args)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
args.empty? ? @captured_buffer : content_for(args.first)
|
38
|
+
end
|
39
|
+
|
40
|
+
def content
|
41
|
+
self.yield
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_slot_default(slot_name, default_value)
|
45
|
+
raise UnknownSlotError, "unknown slot :#{slot_name}" unless slot?(slot_name)
|
46
|
+
|
47
|
+
if values.for(slot_name).none? && !default_value.nil?
|
48
|
+
values.add(slot_name, Array.wrap(default_value))
|
42
49
|
end
|
43
50
|
end
|
44
51
|
|
45
52
|
def slot_locals
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
53
|
+
validate_slots!
|
54
|
+
|
55
|
+
pairs = @defined_slots.map do |slot_name|
|
56
|
+
slot_values = values.for(slot_name)
|
57
|
+
slot_values = singular?(slot_name) ? slot_values&.first : slot_values
|
58
|
+
[slot_name, slot_values]
|
50
59
|
end
|
60
|
+
|
51
61
|
pairs.filter do |key, value|
|
52
62
|
# keep empty strings as local value but filter out empty arrays
|
53
63
|
# and objects so they don't override any default values set via strict slots.
|
@@ -55,69 +65,50 @@ module Slotify
|
|
55
65
|
end.to_h
|
56
66
|
end
|
57
67
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
68
|
+
def define_slots!(slot_names)
|
69
|
+
raise SlotsDefinedError, "Slots cannot be redefined" unless @defined_slots.nil?
|
70
|
+
|
71
|
+
@defined_slots = slot_names.map(&:to_sym).each do |slot_name|
|
72
|
+
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"
|
74
|
+
end
|
75
|
+
end
|
61
76
|
end
|
62
77
|
|
63
78
|
def respond_to_missing?(name, include_private = false)
|
64
|
-
name.start_with?("with_")
|
79
|
+
name.start_with?("with_") || slot?(name)
|
65
80
|
end
|
66
81
|
|
67
82
|
def method_missing(name, *args, **options, &block)
|
68
83
|
if name.start_with?("with_")
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
add_values(slot_name, collection, options, block)
|
75
|
-
end
|
84
|
+
values.add(name.to_s.delete_prefix("with_"), args, options, block)
|
85
|
+
elsif slot?(name)
|
86
|
+
content_for(name)
|
87
|
+
else
|
88
|
+
super
|
76
89
|
end
|
77
90
|
end
|
78
91
|
|
79
92
|
private
|
80
93
|
|
81
|
-
|
82
|
-
!@strict_slots.nil?
|
83
|
-
end
|
84
|
-
|
85
|
-
def slot_defined?(slot_name)
|
86
|
-
slot_name && slots_defined? && @strict_slots.include?(slot_name.to_sym)
|
87
|
-
end
|
88
|
-
|
89
|
-
def slot_values(slot_name)
|
90
|
-
@values.filter { _1.slot_name == singularize(slot_name) }
|
91
|
-
end
|
94
|
+
attr_reader :values
|
92
95
|
|
93
|
-
def
|
94
|
-
|
95
|
-
rescue NoMethodError
|
96
|
-
raise SlotArgumentError, "expected array to be passed to slot :#{slot_name} (received #{collection.class.name})"
|
97
|
-
end
|
98
|
-
|
99
|
-
def add_value(slot_name, args = [], options = {}, block = nil)
|
100
|
-
MethodArgsResolver.call(args, options, block) do
|
101
|
-
@values << Value.new(@view_context, singularize(slot_name), _1, _2, _3)
|
102
|
-
end
|
103
|
-
|
104
|
-
@values.last
|
96
|
+
def slot?(slot_name)
|
97
|
+
slot_name && @defined_slots.include?(slot_name.to_sym)
|
105
98
|
end
|
106
99
|
|
107
100
|
def validate_slots!
|
108
|
-
return if @
|
109
|
-
|
110
|
-
singular_slots = @strict_slots.map { singularize(_1) }
|
111
|
-
slots_called = @values.map(&:slot_name).uniq
|
112
|
-
undefined_slots = slots_called - singular_slots
|
101
|
+
return if @defined_slots.nil?
|
113
102
|
|
103
|
+
undefined_slots = values.slot_names - @defined_slots.map { singularize(_1) }
|
114
104
|
if undefined_slots.any?
|
115
|
-
raise UndefinedSlotError,
|
105
|
+
raise UndefinedSlotError,
|
106
|
+
"missing slot #{"definition".pluralize(undefined_slots.size)} for `#{undefined_slots.map { ":#{_1}(s)" }.join(", ")}`"
|
116
107
|
end
|
117
108
|
|
118
|
-
@
|
119
|
-
|
120
|
-
raise MultipleSlotEntriesError, "slot :#{slot_name} called #{
|
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?
|
121
112
|
end
|
122
113
|
end
|
123
114
|
end
|
data/lib/slotify/value.rb
CHANGED
@@ -4,11 +4,11 @@ module Slotify
|
|
4
4
|
|
5
5
|
attr_reader :slot_name, :args, :block
|
6
6
|
|
7
|
-
delegate :presence, to:
|
7
|
+
delegate :presence, :to_s, :to_str, to: :content
|
8
8
|
|
9
|
-
def initialize(view_context,
|
9
|
+
def initialize(view_context, args = [], options = {}, block = nil, slot_name: nil, partial_path: nil)
|
10
10
|
@view_context = view_context
|
11
|
-
@slot_name = slot_name
|
11
|
+
@slot_name = slot_name&.to_sym
|
12
12
|
@args = args
|
13
13
|
@options = options.to_h
|
14
14
|
@block = block
|
@@ -20,21 +20,16 @@ module Slotify
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def content
|
23
|
-
|
24
|
-
@view_context.capture(&@block)
|
23
|
+
if @block && @block.arity == 0
|
24
|
+
body = @view_context.capture(&@block)
|
25
|
+
ActiveSupport::SafeBuffer.new(body.presence || "")
|
26
|
+
elsif args.first.is_a?(String)
|
27
|
+
ActiveSupport::SafeBuffer.new(args.first)
|
25
28
|
else
|
26
|
-
|
27
|
-
args.first.to_str
|
28
|
-
rescue NoMethodError
|
29
|
-
""
|
30
|
-
end
|
29
|
+
args.first
|
31
30
|
end
|
32
|
-
ActiveSupport::SafeBuffer.new(body.presence || "")
|
33
31
|
end
|
34
32
|
|
35
|
-
alias_method :to_s, :content
|
36
|
-
alias_method :to_str, :content
|
37
|
-
|
38
33
|
def present?
|
39
34
|
@args.present? || @options.present? || @block
|
40
35
|
end
|
@@ -52,24 +47,30 @@ module Slotify
|
|
52
47
|
alias_method :to_hash, :to_h
|
53
48
|
|
54
49
|
def with_partial_path(partial_path)
|
55
|
-
Value.new(@view_context, @
|
50
|
+
Value.new(@view_context, @args, options, @block, slot_name: @slot_name, partial_path:)
|
56
51
|
end
|
57
52
|
|
58
53
|
def with_default_options(default_options)
|
59
54
|
options = TagOptionsMerger.call(default_options, @options)
|
60
|
-
Value.new(@view_context, @
|
55
|
+
Value.new(@view_context, @args, options, @block, slot_name: @slot_name)
|
61
56
|
end
|
62
57
|
|
63
58
|
def respond_to_missing?(name, include_private = false)
|
64
59
|
name.start_with?("to_") || super
|
65
60
|
end
|
66
61
|
|
67
|
-
def method_missing(name,
|
68
|
-
if name.start_with?("to_")
|
69
|
-
@args.first.public_send(name)
|
62
|
+
def method_missing(name, ...)
|
63
|
+
if name.start_with?("to_")
|
64
|
+
@args.first.public_send(name, ...)
|
65
|
+
else
|
66
|
+
super
|
70
67
|
end
|
71
68
|
end
|
72
69
|
|
70
|
+
def [](key)
|
71
|
+
key.is_a?(Integer) ? @args[key] : super
|
72
|
+
end
|
73
|
+
|
73
74
|
def render_in(view_context, &block)
|
74
75
|
view_context.render partial_path, **@options.to_h, &@block || block
|
75
76
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Slotify
|
2
|
+
class ValueStore
|
3
|
+
include InflectionHelper
|
4
|
+
|
5
|
+
def initialize(view_context)
|
6
|
+
@view_context = view_context
|
7
|
+
@values = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def for(slot_name)
|
11
|
+
@values.select { _1.slot_name == singularize(slot_name) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(slot_name, args = [], options = {}, block = nil)
|
15
|
+
if plural?(slot_name)
|
16
|
+
Array.wrap(args.first).map { add(singularize(slot_name), _1, options, block) }
|
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
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def slot_names
|
25
|
+
@values.map(&:slot_name).uniq
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/slotify/version.rb
CHANGED
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
|
+
version: 0.0.4
|
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-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -46,8 +46,8 @@ extra_rdoc_files: []
|
|
46
46
|
files:
|
47
47
|
- README.md
|
48
48
|
- lib/slotify.rb
|
49
|
-
- lib/slotify/concerns/helpers_concern.rb
|
50
49
|
- lib/slotify/concerns/inflection_helper.rb
|
50
|
+
- lib/slotify/concerns/slot_compatability.rb
|
51
51
|
- lib/slotify/error.rb
|
52
52
|
- lib/slotify/extensions/base.rb
|
53
53
|
- lib/slotify/extensions/partial_renderer.rb
|
@@ -58,6 +58,7 @@ files:
|
|
58
58
|
- lib/slotify/value.rb
|
59
59
|
- lib/slotify/value_collection.rb
|
60
60
|
- lib/slotify/value_options.rb
|
61
|
+
- lib/slotify/value_store.rb
|
61
62
|
- lib/slotify/version.rb
|
62
63
|
homepage:
|
63
64
|
licenses:
|
@@ -78,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
79
|
- !ruby/object:Gem::Version
|
79
80
|
version: '0'
|
80
81
|
requirements: []
|
81
|
-
rubygems_version: 3.
|
82
|
+
rubygems_version: 3.3.3
|
82
83
|
signing_key:
|
83
84
|
specification_version: 4
|
84
85
|
summary: Superpowered slots for your Rails partials.
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Slotify
|
2
|
-
module HelpersConcern
|
3
|
-
def make_compatible_with_slots(*method_names)
|
4
|
-
proxy = Module.new
|
5
|
-
method_names.each do |name|
|
6
|
-
proxy.define_method(name) do |*args, **kwargs, &block|
|
7
|
-
return super(*args, **kwargs, &block) if args.none?
|
8
|
-
|
9
|
-
results = MethodArgsResolver.call(args, kwargs, block) { super(*_1, **_2, &_3) }
|
10
|
-
results.reduce(ActiveSupport::SafeBuffer.new) { _1 << _2 }
|
11
|
-
end
|
12
|
-
end
|
13
|
-
prepend proxy
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|