phlex-slotable 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ed2552d628d6851db0da87a5105818d55b5a28bda04d7b5dd9dc0f6fe7357a5
4
- data.tar.gz: dd6b66cb419fae64ee1ea963670735a3773a6d1c26bf18c2cc1be01ad43e559b
3
+ metadata.gz: 663b35110ae8bfad98b0a05e7a5a30afc296f16fc088b4271499ef02b03b9ba2
4
+ data.tar.gz: eb7153fef5861841d353e5404498dbbaadb8ad1bc7e7f83d6ae82f9bebc0551e
5
5
  SHA512:
6
- metadata.gz: 0aafc0d75f3907534255f8ffdc8800e8541373884c5e149afc12dfd5f1e811644c6f037ec32640501f8faec83dace3ea6368c6a17c51fe88b2e27557813f4b70
7
- data.tar.gz: a5d11b19a175d1228c0ec7561986dbec0f0a976829b0b0300d599c15d32e801d9afcd4fd2dabdd3a180efccf2f12bde9673ee84744c712fa9aca7b77f3a8e412
6
+ metadata.gz: 7e7fcf450ddbd8bbab8b6eeb22fa54f0de68d9e5611da5c8c1f71e87bd27d2b7497eecdff21b9d61d40006ea7d1f0f782dfa1b4213284a46e8ae8988b8357394
7
+ data.tar.gz: ad39d88df8841cd7e7599b50b64d48e9b7faefc9abb44b3856749e728bad37c672dbd09fff420324d6b59257f1aaf65d5aef00af614a9a2f509b71e32fb923b3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2025-09-08
4
+ - Prepare for Phlex 2.0 💪
5
+ - Drop Ruby 2.7, 3.0 and 3.1 support
6
+ - Add Phlex::Kit compatibility tests
7
+ - Make `Phlex::Slotable::VERSION` available by default
8
+
9
+ ## [0.4.0] - 2024-02-14
10
+ - [BREAKING CHANGE] Rename `many` option to `collection`.
11
+
12
+ *stephannv*
13
+
14
+ - Improve generic slot performance
15
+
16
+ *stephannv*
17
+
3
18
  ## [0.3.1] - 2024-02-14
4
19
  - Support Ruby 2.7
5
20
 
data/README.md CHANGED
@@ -6,7 +6,22 @@
6
6
 
7
7
  Phlex::Slotable enables slots feature to [Phlex](https://www.phlex.fun/) views. Inspired by ViewComponent.
8
8
 
9
- ## Installation
9
+ - [What is a slot?](#what-is-a-slot)
10
+ - [Getting started](#getting-started)
11
+ - [Generic slot](#generic-slot)
12
+ - [Slot collection](#slot-collection)
13
+ - [Component slot](#component-slot)
14
+ - [Lambda slot](#lambda-slot)
15
+ - [Polymorphic slot](#polymorphic-slot)
16
+ - [Performance](#performance)
17
+ - [Development](#development)
18
+ - [Contributing](#contributing)
19
+
20
+ ## What is a slot?
21
+
22
+ In the context of view components, a **slot** serves as a placeholder inside a component that can be filled with custom content. Essentially, slots enable a component to accept external content and autonomously organize it within its structure. This abstraction allows developers to work with components without needing to understand their internals, thereby ensuring visual consistency and improving developer experience.
23
+
24
+ ## Getting started
10
25
 
11
26
  Install the gem and add to the application's Gemfile by executing:
12
27
 
@@ -16,268 +31,272 @@ If bundler is not being used to manage dependencies, install the gem by executin
16
31
 
17
32
  $ gem install phlex-slotable
18
33
 
19
- ## Usage
20
-
21
- #### Basic
34
+ > [!TIP]
35
+ > If you prefer not to add another dependency to your project, you can simply copy the [Phlex::Slotable](https://github.com/stephannv/phlex-slotable/blob/main/lib/phlex/slotable.rb) file into your project.
22
36
 
23
- To incorportate slots into your Phlex views, include `Phlex::Slotable` and utilize `slot` class method to define them.
24
-
25
- - `slot :slot_name` declaration establishes a single slot intended for rendering once within a view
26
- - `slot :slot_name, many: true` denotes a slot capable of being rendered multiple times within a view
37
+ Afterward, simply include `Phlex::Slotable` into your Phlex component and utilize `slot` macro to define the component's slots. For example:
27
38
 
28
39
  ```ruby
29
- class BlogComponent < Phlex::HTML
40
+ class MyComponent < Phlex::HTML
30
41
  include Phlex::Slotable
31
42
 
32
- slot :header
33
- slot :post, many: true
34
-
35
- # ...
43
+ slot :my_slot
36
44
  end
37
45
  ```
38
46
 
39
- To render a single slot, utilize the `{slot_name}_slot` method. For example, you can render the `header_slot` using `render header_slot`.
47
+ Below, you will find a more detailed explanation of how to use the `slot` API.
40
48
 
41
- For multi-slot rendering, iterate over the `{slot_name}_slots` collection and and render each slot individually, eg. `post_slots.each { |s| render s }`.
49
+ ## Generic slot
50
+
51
+ Any content can be passed to components through generic slots, also known as passthrough slots. To define a generic slot, use `slot :{slot_name}`. For example:
42
52
 
43
53
  ```ruby
44
- class BlogComponent < Phlex::HTML
54
+ class PageComponent < Phlex::HTML
45
55
  include Phlex::Slotable
46
56
 
47
- slot :header
48
- slot :post, many: true
57
+ slot :title
58
+ end
59
+ ```
49
60
 
50
- def template
51
- div id: "header" do
52
- render header_slot
53
- end
61
+ To render a slot, render the `{slot_name}_slot`:
54
62
 
55
- div id: "main" do
56
- post_slots.each do |slot|
57
- p { render slot }
58
- end
63
+ ```ruby
64
+ class PageComponent < Phlex::HTML
65
+ include Phlex::Slotable
59
66
 
60
- span { "Count: #{post_slots.count}" }
61
- end
67
+ slot :title
68
+
69
+ def template
70
+ header { render title_slot }
62
71
  end
63
72
  end
64
73
  ```
65
74
 
66
- When setting slot content, ensure to utilize the `with_{slot_name}` method while rendering the view:
75
+ To pass content to the component's slot, you should use `with_{slot_name}`:
67
76
 
68
77
  ```ruby
69
- class MyPage < Phlex::HTML
70
- def template
71
- render BlogComponent.new do |blog|
72
- blog.with_header do
73
- h1 { "Hello World!" }
74
- end
75
-
76
- blog.with_post { "Post A" }
77
- blog.with_post { "Post B" }
78
- blog.with_post { "Post C" }
79
- end
78
+ PageComponent.new.call do |page|
79
+ page.with_title do
80
+ h1 { "Hello World!" }
80
81
  end
81
82
  end
82
-
83
- MyPage.new.call
84
83
  ```
85
84
 
86
- This will output:
85
+ Returning:
87
86
 
88
87
  ```html
89
- <div id="header">
90
- <h1>Hello World</h1>
91
- </div>
92
- <div id="main">
93
- <p>Post A</p>
94
- <p>Post B</p>
95
- <p>Post C</p>
96
-
97
- <span>Count: 3</span>
98
- </div>
88
+ <header>
89
+ <h1>Hello World!</h1>
90
+ </header>
99
91
  ```
100
92
 
101
- #### Predicate methods
102
-
103
- You can verify whether a slot has been provided to the view using `{slot_name}_slot?` for single slots or `{slot_name}_slots?` when for multi-slots.
93
+ You can test if a slot has been passed to the component with `{slot_name}_slot?` method. For example:
104
94
 
105
95
  ```ruby
106
- class BlogComponent < Phlex::HTML
96
+ class PageComponent < Phlex::HTML
107
97
  include Phlex::Slotable
108
98
 
109
- slot :header
110
- slot :post, many: true
99
+ slot :title
111
100
 
112
101
  def template
113
102
  if header_slot?
114
- div id: "header" do
115
- render header_slot
116
- end
117
- end
118
-
119
- div id: "main" do
120
- if post_slots?
121
- post_slots.each do |slot|
122
- p { render slot }
123
- end
124
-
125
- span { "Count: #{post_slots.count}" }
126
- else
127
- span { "No post yet" }
128
- end
103
+ header { render title_slot }
104
+ else
105
+ plain "No title"
129
106
  end
130
107
  end
131
108
  end
132
109
  ```
133
110
 
134
- #### View slot
111
+ ## Slot collection
135
112
 
136
- Slots have the capability to render other views, Simply pass the view class name to the `slot` method.
113
+ A slot collection denotes a slot capable of being rendered multiple times within a component. It has some minor differences compared to a single slot seen previously. First, you should pass `collection: true` when defining the slot:
137
114
 
138
115
  ```ruby
139
- class HeaderComponent < Phlex::HTML
140
- def initialize(size:)
141
- @size = size
142
- end
116
+ class ListComponent < Phlex::HTML
117
+ include Phlex::Slotable
143
118
 
144
- def template(&content)
145
- h1(class: "text-#{@size}", &content)
146
- end
119
+ slot :item, collection: true
147
120
  end
121
+ ```
148
122
 
149
- class PostComponent < Phlex::HTML
150
- def initialize(featured:)
151
- @featured = featured
152
- end
153
-
154
- def template(&content)
155
- p(class: @featured ? "featured" : nil, &content)
156
- end
157
- end
123
+ To render a collection of slots, iterate over the `{slot_name}_slots` collection and render each slot individually:
158
124
 
159
- class BlogComponent < Phlex::HTML
125
+ ```ruby
126
+ class ListComponent < Phlex::HTML
160
127
  include Phlex::Slotable
161
128
 
162
- slot :header, HeaderComponent
163
- slot :post, PostComponent, many: true
129
+ slot :item, collection: true
164
130
 
165
131
  def template
166
- if header_slot?
167
- div id: "header" do
168
- render header_slot
132
+ if item_slots?
133
+ ul do
134
+ item_slots.each do |item_slot|
135
+ li { render item_slot }
136
+ end
169
137
  end
170
138
  end
171
139
 
172
- div id: "main" do
173
- if post_slots?
174
- post_slots.each { render slot }
175
-
176
- span { "Count: #{post_slots.count}" }
177
- else
178
- span { "No post yet" }
179
- end
180
- end
140
+ span { "Total: #{item_slots.size}" }
181
141
  end
182
142
  end
143
+ ```
183
144
 
184
- class MyPage < Phlex::HTML
185
- def template
186
- render BlogComponent.new do |blog|
187
- blog.with_header(size: :lg) { "Hello World!" }
145
+ To set slot content, use the `with_{slot_name}` method when rendering the component. Unlike the single slot, `with_{slot_name}` can be called multiple times:
188
146
 
189
- blog.with_post(featured: true) { "Post A" }
190
- blog.with_post { "Post B" }
191
- blog.with_post { "Post C" }
192
- end
193
- end
147
+ ```ruby
148
+ ListComponent.new.call do |list|
149
+ list.with_item { "Item A" }
150
+ list.with_item { "Item B" }
151
+ list.with_item { "Item C" }
194
152
  end
195
-
196
- MyPage.new.call
197
153
  ```
198
154
 
199
- The output:
155
+ Returning:
200
156
 
201
157
  ```html
202
- <div id="header">
203
- <h1 class="text-lg">Hello World</h1>
204
- </div>
205
- <div id="main">
206
- <p class="featured">Post A</p>
207
- <p>Post B</p>
208
- <p>Post C</p>
158
+ <ul>
159
+ <li>Item A</li>
160
+ <li>Item B</li>
161
+ <li>Item C</li>
162
+ </ul>
209
163
 
210
- <span>Count: 3</span>
211
- </div>
164
+ <span>Total: 3</span>
212
165
  ```
213
166
 
214
- You can pass the class name as a string for cases where the class isn't evaluated yet, such as with inner classes. For example:
215
- ```ruby
216
- class BlogComponent < Phlex::HTML
217
- include Phlex::Slotable
218
-
219
- # This will not work
220
- slot :header, HeaderComponent # uninitialized constant BlogComponent::HeaderComponent
221
- # You should do this
222
- slot :header, "HeaderComponent"
167
+ ## Component slot
223
168
 
224
- private
169
+ Slots have the capability to render other components. When defining a slot, provide the name of a component class as the second argument to define a component slot
225
170
 
226
- class HeaderComponent < Phlex::HTML
227
- # ...
228
- end
171
+ ```ruby
172
+ class ListHeaderComponent < Phlex::HTML
173
+ # omitted code
229
174
  end
230
- ```
231
175
 
232
- #### Lambda slots
233
- Lambda slots are valuable when you prefer not to create another component for straightforward structures or when you need to render another view with specific parameters
234
- ```ruby
176
+ class ListItemComponent < Phlex::HTML
177
+ # omitted code
178
+ end
235
179
 
236
- class BlogComponent < Phlex::HTML
180
+ class ListComponent < Phlex::HTML
237
181
  include Phlex::Slotable
238
182
 
239
- slot :header, ->(size:, &content) { render HeaderComponent.new(size: size, color: "blue"), &content }
240
- slot :post, ->(featured:, &content) { span(class: featured ? "featured" : nil, &content) }, many: true
241
- end
183
+ slot :header, ListHeaderComponent
184
+ slot :item, ListItemComponent, collection: true
242
185
 
243
- class MyPage < Phlex::HTML
244
186
  def template
245
- render BlogComponent.new do |blog|
246
- blog.with_header(size: :lg) { "Hello World!" }
187
+ div id: "header" do
188
+ render header_slot if header_slot?
189
+ end
247
190
 
248
- blog.with_post(featured: true) { "Post A" }
249
- blog.with_post { "Post B" }
250
- blog.with_post { "Post C" }
191
+ ul do
192
+ item_slots.each { |slot| render slot }
251
193
  end
252
194
  end
253
195
  end
196
+
197
+ ListComponent.new.call do |list|
198
+ list.with_header(size: "lg") { "Hello World!" }
199
+
200
+ list.with_item(active: true) { "Item A" }
201
+ list.with_item { "Item B" }
202
+ list.with_item { "Item C" }
203
+ end
254
204
  ```
255
205
 
256
- You can access the internal view state within lambda slots. For example:
206
+ Returning:
207
+
208
+ ```html
209
+ <div id="header">
210
+ <h1 class="text-lg">Hello World!</h1>
211
+
212
+ <ul>
213
+ <li class="active">Item A</li>
214
+ <li>Item B</li>
215
+ <li>Item C</li>
216
+ </ul>
217
+ </div>
218
+ ```
219
+
220
+ > [!TIP]
221
+ > You can also pass the component class as a string if your component class hasn't been defined yet. For example:
222
+ >
223
+ > ```ruby
224
+ > slot :header, "HeaderComponent"
225
+ > slot :item, "ItemComponent", collection: true
226
+ >```
227
+
228
+
229
+ ## Lambda slot
230
+
231
+ Lambda slots are valuable when you prefer not to create another component for straightforward structures or when you need to render another component with specific parameters.
232
+
257
233
  ```ruby
258
- class BlogComponent < Phlex::HTML
234
+ class ListComponent < Phlex::HTML
259
235
  include Phlex::Slotable
260
236
 
261
- slot :header, ->(size:, &content) { render HeaderComponent.new(size: size, color: @header_color), &content }
262
-
263
- def initialize(header_color:)
264
- @header_color = header_color
237
+ slot :header, ->(size:, &content) do
238
+ render HeaderComponent.new(size: size, color: "primary")
265
239
  end
266
- end
240
+ slot :item, ->(href:, &content) { li { a(href: href, &content) } }, collection: true
267
241
 
268
- class MyPage < Phlex::HTML
269
242
  def template
270
- render BlogComponent.new(header_color: "red") do |blog|
271
- blog.with_header(size: :lg) { "Hello World!" }
243
+ div id: "header" do
244
+ render header_slot if header_slot?
245
+ end
246
+
247
+ ul do
248
+ item_slots.each { |slot| render slot }
272
249
  end
273
250
  end
274
251
  end
252
+
253
+ ListComponent.new.call do |list|
254
+ list.with_header(size: "lg") { "Hello World!" }
255
+
256
+ list.with_item(href: "/a") { "Item A" }
257
+ list.with_item(href: "/b") { "Item B" }
258
+ list.with_item(href: "/c") { "Item C" }
259
+ end
275
260
  ```
276
261
 
277
- #### Polymorphic slots
278
- Polymorphic slots can render one of several possible slots, allowing for flexibility in component content. This feature is particularly useful when you require a fixed structure but need to accommodate different types of content. To implement this, simply pass a types hash containing the types along with corresponding slot definitions.
262
+ Returning:
263
+
264
+ ```html
265
+ <div id="header">
266
+ <h1 class="text-lg text-primary">Hello World!</h1>
267
+
268
+ <ul>
269
+ <li><a href="/a">Item A</a></li>
270
+ <li><a href="/b">Item B</a></li>
271
+ <li><a href="/c">Item C</a></li>
272
+ </ul>
273
+ </div>
274
+ ```
275
+
276
+ > [!TIP]
277
+ > You can access the internal component state within lambda slots. For example
278
+ >
279
+ > ```ruby
280
+ > slot :header, ->(&content) { render HeaderComponent.new(featured: @featured), &content }
281
+ >
282
+ > def initialize(featured:)
283
+ > @featured = feature
284
+ > end
285
+ > ```
286
+
287
+ ## Polymorphic slot
288
+
289
+ Polymorphic slots can render one of several possible slots, allowing for flexibility in component content. This feature is particularly useful when you require a fixed structure but need to accommodate different types of content. To implement this, simply pass a types hash containing the types along with corresponding slot definitions.
279
290
 
280
291
  ```ruby
292
+ class IconComponent < Phlex::HTML
293
+ # omitted code
294
+ end
295
+
296
+ class ImageComponent < Phlex::HTML
297
+ # omitted code
298
+ end
299
+
281
300
  class CardComponent < Phlex::HTML
282
301
  include Phlex::Slotable
283
302
 
@@ -285,74 +304,69 @@ class CardComponent < Phlex::HTML
285
304
 
286
305
  def template
287
306
  if avatar_slot?
288
- figure id: "avatar" do
307
+ div id: "avatar" do
289
308
  render avatar_slot
290
309
  end
291
310
  end
292
311
  end
293
312
  end
294
- ```
295
313
 
296
- This allows you to set the icon slot using `with_icon_avatar` or the image slot using `with_image_avatar`:
297
- ```ruby
298
- class UserCardComponent < Phlex::HTML
299
- def initialize(user:)
300
- @user = user
301
- end
314
+ User = Data.define(:image_url)
315
+ user = User.new(image_url: "user.png")
302
316
 
303
- def template
304
- render CardComponent.new do |card|
305
- if @user.image?
306
- card.with_image_avatar(src: @user.image)
307
- else
308
- card.with_icon_avatar(name: :user)
309
- end
310
- end
317
+ CardComponent.new.call do |card|
318
+ if user.image_url
319
+ card.with_image_avatar(src: user.image_url)
320
+ else
321
+ card.with_icon_avatar(name: :user)
311
322
  end
312
323
  end
313
324
  ```
314
325
 
315
- Please note that you can still utilize the other slot definition APIs:
316
- ```ruby
317
- class CardComponent < Phlex::HTML
318
- include Phlex::Slotable
326
+ Returning:
319
327
 
320
- slot :avatar, types: {
321
- icon: IconComponent,
322
- image: "ImageComponent",
323
- text: ->(size:, &content) { span(class: "text-#{size}", &content) }
324
- }, many: true
328
+ ```html
329
+ <div id="avatar">
330
+ <img src="user.png"/>
331
+ </div>
332
+ ```
325
333
 
326
- def template
327
- if avatar_slots?
328
- avatar_slots.each do |slot|
329
- render slot
330
- end
331
- end
334
+ Note that you need to use `with_{type}_{slot_name}` to set slot content. In the example above, it was used `with_image_avatar` and `with_icon_avatar`.
332
335
 
333
- span { "Count: #{avatar_slots.size}" }
334
- end
336
+ > [!TIP]
337
+ > You can take advantage of all the previously introduced features, such as lambda slot and slot collection:
338
+ >
339
+ > ```ruby
340
+ > slot :avatar, collection: true, types: {
341
+ > icon: IconComponent,
342
+ > image: "ImageComponent",
343
+ > text: ->(&content) { span(class: "avatar", &content) }
344
+ > }
345
+ > ```
335
346
 
336
- ...
337
- end
347
+ ## Performance
348
+ Using Phlex::Slotable you don't suffer a performance penalty compared to using Phlex::DeferredRender, sometimes it can even be a little faster.
338
349
 
339
- class UsersCardComponent < Phlex::HTML
340
- def template
341
- render CardComponent.new do |card|
342
- card.with_image_avatar(src: @user.image)
343
- card.with_icon_avatar(name: :user)
344
- card.with_text_avatar(size: :lg) { "SV" }
345
- end
346
- end
347
- end
350
+ ```
351
+ Generated using `ruby benchmark/main.rb`
352
+
353
+ Phlex 1.11.0
354
+ Phlex::Slotable 0.5.0
355
+
356
+ ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin23]
357
+ Warming up --------------------------------------
358
+ Deferred 22.176k i/100ms
359
+ Slotable 23.516k i/100ms
360
+ Calculating -------------------------------------
361
+ Deferred 222.727k (± 0.8%) i/s (4.49 μs/i) - 1.131M in 5.078157s
362
+ Slotable 237.405k (± 0.6%) i/s (4.21 μs/i) - 1.199M in 5.051936s
363
+
364
+ Comparison:
365
+ Slotable: 237405.0 i/s
366
+ Deferred: 222726.8 i/s - 1.07x slower
348
367
  ```
349
368
 
350
369
 
351
- ## Roadmap
352
- - ✅ ~~Accept Strings as view class name~~
353
- - ✅ ~~Allow lambda slots~~
354
- - ✅ ~~Allow polymorphic slots~~
355
-
356
370
  ## Development
357
371
 
358
372
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -361,7 +375,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
361
375
 
362
376
  ## Contributing
363
377
 
364
- Bug reports and pull requests are welcome on GitHub at https://github.com/stephannv/phlex-slot. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/stephannv/phlex-slotable/blob/master/CODE_OF_CONDUCT.md).
378
+ Bug reports and pull requests are welcome on GitHub at https://github.com/stephannv/phlex-slotable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/stephannv/phlex-slotable/blob/master/CODE_OF_CONDUCT.md).
365
379
 
366
380
  ## License
367
381
 
data/benchmark/main.rb CHANGED
@@ -2,6 +2,11 @@ require "benchmark"
2
2
  require "benchmark/ips"
3
3
  require_relative "../lib/phlex/slotable"
4
4
 
5
+ require "phlex/version"
6
+
7
+ puts "Phlex #{Phlex::VERSION}"
8
+ puts "Phlex::Slotable #{Phlex::Slotable::VERSION}"
9
+
5
10
  class DeferredList < Phlex::HTML
6
11
  include Phlex::DeferredRender
7
12
 
@@ -9,7 +14,7 @@ class DeferredList < Phlex::HTML
9
14
  @items = []
10
15
  end
11
16
 
12
- def template
17
+ def view_template
13
18
  if @header
14
19
  h1(class: "header", &@header)
15
20
  end
@@ -34,9 +39,9 @@ class SlotableList < Phlex::HTML
34
39
  include Phlex::Slotable
35
40
 
36
41
  slot :header
37
- slot :item, many: true
42
+ slot :item, collection: true
38
43
 
39
- def template
44
+ def view_template
40
45
  if header_slot
41
46
  h1(class: "header", &header_slot)
42
47
  end
@@ -50,7 +55,7 @@ class SlotableList < Phlex::HTML
50
55
  end
51
56
 
52
57
  class DeferredListExample < Phlex::HTML
53
- def template
58
+ def view_template
54
59
  render DeferredList.new do |list|
55
60
  list.header do
56
61
  "Header"
@@ -68,7 +73,7 @@ class DeferredListExample < Phlex::HTML
68
73
  end
69
74
 
70
75
  class SlotableListExample < Phlex::HTML
71
- def template
76
+ def view_template
72
77
  render SlotableList.new do |list|
73
78
  list.with_header do
74
79
  "Header"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Phlex
4
4
  module Slotable
5
- VERSION = "0.3.1"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "phlex"
4
+ require_relative "slotable/version"
4
5
 
5
6
  module Phlex
6
7
  module Slotable
@@ -9,41 +10,42 @@ module Phlex
9
10
  end
10
11
 
11
12
  module ClassMethods
12
- def slot(slot_name, callable = nil, types: nil, many: false)
13
+ def slot(slot_name, callable = nil, types: nil, collection: false)
13
14
  include Phlex::DeferredRender
14
15
 
15
16
  if types
16
17
  types.each do |type, callable|
17
- define_setter_method(slot_name, callable, many: many, type: type)
18
+ define_setter_method(slot_name, callable, collection: collection, type: type)
18
19
  end
19
20
  else
20
- define_setter_method(slot_name, callable, many: many)
21
+ define_setter_method(slot_name, callable, collection: collection)
21
22
  end
22
- define_predicate_method(slot_name, many: many)
23
- define_getter_method(slot_name, many: many)
23
+ define_predicate_method(slot_name, collection: collection)
24
+ define_getter_method(slot_name, collection: collection)
24
25
  end
25
26
 
26
27
  private
27
28
 
28
- def define_setter_method(slot_name, callable, many:, type: nil)
29
+ def define_setter_method(slot_name, callable, collection:, type: nil)
29
30
  slot_name_with_type = type ? "#{type}_#{slot_name}" : slot_name
31
+ signature = callable.nil? ? "(&block)" : "(*args, **kwargs, &block)"
30
32
 
31
- setter_method = if many
33
+ setter_method = if collection
32
34
  <<-RUBY
33
- def with_#{slot_name_with_type}(*args, **kwargs, &block)
35
+ def with_#{slot_name_with_type}#{signature}
34
36
  @#{slot_name}_slots ||= []
35
37
  @#{slot_name}_slots << #{callable_value(slot_name_with_type, callable)}
36
38
  end
37
39
  RUBY
38
40
  else
39
41
  <<-RUBY
40
- def with_#{slot_name_with_type}(*args, **kwargs, &block)
42
+ def with_#{slot_name_with_type}#{signature}
41
43
  @#{slot_name}_slot = #{callable_value(slot_name_with_type, callable)}
42
44
  end
43
45
  RUBY
44
46
  end
45
47
 
46
- class_eval(setter_method, __FILE__, __LINE__ + 1)
48
+ class_eval(setter_method, __FILE__, __LINE__)
47
49
  define_lambda_method(slot_name_with_type, callable) if callable.is_a?(Proc)
48
50
  end
49
51
 
@@ -52,44 +54,40 @@ module Phlex
52
54
  private :"__call_#{slot_name}__"
53
55
  end
54
56
 
55
- def define_getter_method(slot_name, many:)
56
- getter_method = if many
57
+ def define_getter_method(slot_name, collection:)
58
+ getter_method = if collection
57
59
  <<-RUBY
58
- def #{slot_name}_slots
59
- @#{slot_name}_slots ||= []
60
- end
60
+ def #{slot_name}_slots = @#{slot_name}_slots ||= []
61
+
61
62
  private :#{slot_name}_slots
62
63
  RUBY
63
64
  else
64
65
  <<-RUBY
65
- def #{slot_name}_slot
66
- @#{slot_name}_slot
67
- end
66
+ def #{slot_name}_slot = @#{slot_name}_slot
67
+
68
68
  private :#{slot_name}_slot
69
69
  RUBY
70
70
  end
71
71
 
72
- class_eval(getter_method, __FILE__, __LINE__ + 1)
72
+ class_eval(getter_method, __FILE__, __LINE__)
73
73
  end
74
74
 
75
- def define_predicate_method(slot_name, many:)
76
- predicate_method = if many
75
+ def define_predicate_method(slot_name, collection:)
76
+ predicate_method = if collection
77
77
  <<-RUBY
78
- def #{slot_name}_slots?
79
- #{slot_name}_slots.any?
80
- end
78
+ def #{slot_name}_slots? = #{slot_name}_slots.any?
79
+
81
80
  private :#{slot_name}_slots?
82
81
  RUBY
83
82
  else
84
83
  <<-RUBY
85
- def #{slot_name}_slot?
86
- !#{slot_name}_slot.nil?
87
- end
84
+ def #{slot_name}_slot? = !#{slot_name}_slot.nil?
85
+
88
86
  private :#{slot_name}_slot?
89
87
  RUBY
90
88
  end
91
89
 
92
- class_eval(predicate_method, __FILE__, __LINE__ + 1)
90
+ class_eval(predicate_method, __FILE__, __LINE__)
93
91
  end
94
92
 
95
93
  def callable_value(slot_name, callable)
@@ -97,7 +95,7 @@ module Phlex
97
95
  when nil
98
96
  %(block)
99
97
  when Proc
100
- %(-> { self.class.instance_method(:"__call_#{slot_name}__").bind_call(self, *args, **kwargs, &block) })
98
+ %(-> { __call_#{slot_name}__(*args, **kwargs, &block) })
101
99
  else
102
100
  %(#{callable}.new(*args, **kwargs, &block))
103
101
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlex-slotable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - stephann
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-14 00:00:00.000000000 Z
11
+ date: 2024-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex
@@ -17,6 +17,9 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.9'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -24,6 +27,9 @@ dependencies:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: '1.9'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
27
33
  description:
28
34
  email:
29
35
  - 3025661+stephannv@users.noreply.github.com
@@ -53,14 +59,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
53
59
  requirements:
54
60
  - - ">="
55
61
  - !ruby/object:Gem::Version
56
- version: 2.7.0
62
+ version: 3.2.0
57
63
  required_rubygems_version: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - ">="
60
66
  - !ruby/object:Gem::Version
61
67
  version: '0'
62
68
  requirements: []
63
- rubygems_version: 3.5.6
69
+ rubygems_version: 3.5.18
64
70
  signing_key:
65
71
  specification_version: 4
66
72
  summary: Enable Slot API for Phlex views