phlex-slotable 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.standard.yml +3 -1
- data/CHANGELOG.md +46 -0
- data/README.md +77 -2
- data/benchmark/main.rb +106 -0
- data/lib/phlex/slotable/version.rb +1 -1
- data/lib/phlex/slotable.rb +77 -43
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ed2552d628d6851db0da87a5105818d55b5a28bda04d7b5dd9dc0f6fe7357a5
|
4
|
+
data.tar.gz: dd6b66cb419fae64ee1ea963670735a3773a6d1c26bf18c2cc1be01ad43e559b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0aafc0d75f3907534255f8ffdc8800e8541373884c5e149afc12dfd5f1e811644c6f037ec32640501f8faec83dace3ea6368c6a17c51fe88b2e27557813f4b70
|
7
|
+
data.tar.gz: a5d11b19a175d1228c0ec7561986dbec0f0a976829b0b0300d599c15d32e801d9afcd4fd2dabdd3a180efccf2f12bde9673ee84744c712fa9aca7b77f3a8e412
|
data/.standard.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,51 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.1] - 2024-02-14
|
4
|
+
- Support Ruby 2.7
|
5
|
+
|
6
|
+
*stephannv*
|
7
|
+
|
8
|
+
## [0.3.0] - 2024-02-14
|
9
|
+
|
10
|
+
- Match Slotable peformance with DeferredRender
|
11
|
+
```
|
12
|
+
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
|
13
|
+
Warming up --------------------------------------
|
14
|
+
Deferred 26.779k i/100ms
|
15
|
+
Slotable 0.2.0 21.636k i/100ms
|
16
|
+
Slotable 0.3.0 27.013k i/100ms
|
17
|
+
Calculating -------------------------------------
|
18
|
+
Deferred 267.884k (± 0.6%) i/s - 1.366M in 5.098391s
|
19
|
+
Slotable 0.2.0 216.193k (± 0.4%) i/s - 1.082M in 5.003961s
|
20
|
+
Slotable 0.3.0 270.082k (± 0.5%) i/s - 1.351M in 5.001001s
|
21
|
+
```
|
22
|
+
*stephannv*
|
23
|
+
|
24
|
+
- Allow polymorphic slots
|
25
|
+
```ruby
|
26
|
+
class CardComponent < Phlex::HTML
|
27
|
+
include Phlex::Slotable
|
28
|
+
|
29
|
+
slot :avatar, types: { icon: IconComponent, image: ImageComponent }
|
30
|
+
|
31
|
+
def template
|
32
|
+
if avatar_slot?
|
33
|
+
render avatar_slot
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
render CardComponent.new do |card|
|
39
|
+
if user
|
40
|
+
card.with_image_avatar(src: user.image_url)
|
41
|
+
else
|
42
|
+
card.with_icon_avatar(name: :user)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
*stephannv*
|
48
|
+
|
3
49
|
## [0.2.0] - 2024-02-13
|
4
50
|
|
5
51
|
- Allow view slots using string as class name
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
> Please note that Phlex::Slotable is currently under development and may undergo changes to its API before reaching the stable release (1.0.0). As a result, there may be breaking changes that affect its usage.
|
3
3
|
|
4
4
|
# Phlex::Slotable
|
5
|
+
[](https://github.com/stephannv/phlex-slotable/actions/workflows/main.yml)
|
5
6
|
|
6
7
|
Phlex::Slotable enables slots feature to [Phlex](https://www.phlex.fun/) views. Inspired by ViewComponent.
|
7
8
|
|
@@ -252,7 +253,7 @@ class MyPage < Phlex::HTML
|
|
252
253
|
end
|
253
254
|
```
|
254
255
|
|
255
|
-
You can access the internal view state within lambda slots.For example:
|
256
|
+
You can access the internal view state within lambda slots. For example:
|
256
257
|
```ruby
|
257
258
|
class BlogComponent < Phlex::HTML
|
258
259
|
include Phlex::Slotable
|
@@ -273,10 +274,84 @@ class MyPage < Phlex::HTML
|
|
273
274
|
end
|
274
275
|
```
|
275
276
|
|
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.
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
class CardComponent < Phlex::HTML
|
282
|
+
include Phlex::Slotable
|
283
|
+
|
284
|
+
slot :avatar, types: { icon: IconComponent, image: ImageComponent }
|
285
|
+
|
286
|
+
def template
|
287
|
+
if avatar_slot?
|
288
|
+
figure id: "avatar" do
|
289
|
+
render avatar_slot
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
```
|
295
|
+
|
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
|
302
|
+
|
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
|
311
|
+
end
|
312
|
+
end
|
313
|
+
```
|
314
|
+
|
315
|
+
Please note that you can still utilize the other slot definition APIs:
|
316
|
+
```ruby
|
317
|
+
class CardComponent < Phlex::HTML
|
318
|
+
include Phlex::Slotable
|
319
|
+
|
320
|
+
slot :avatar, types: {
|
321
|
+
icon: IconComponent,
|
322
|
+
image: "ImageComponent",
|
323
|
+
text: ->(size:, &content) { span(class: "text-#{size}", &content) }
|
324
|
+
}, many: true
|
325
|
+
|
326
|
+
def template
|
327
|
+
if avatar_slots?
|
328
|
+
avatar_slots.each do |slot|
|
329
|
+
render slot
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
span { "Count: #{avatar_slots.size}" }
|
334
|
+
end
|
335
|
+
|
336
|
+
...
|
337
|
+
end
|
338
|
+
|
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
|
348
|
+
```
|
349
|
+
|
350
|
+
|
276
351
|
## Roadmap
|
277
352
|
- ✅ ~~Accept Strings as view class name~~
|
278
353
|
- ✅ ~~Allow lambda slots~~
|
279
|
-
-
|
354
|
+
- ✅ ~~Allow polymorphic slots~~
|
280
355
|
|
281
356
|
## Development
|
282
357
|
|
data/benchmark/main.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
require "benchmark/ips"
|
3
|
+
require_relative "../lib/phlex/slotable"
|
4
|
+
|
5
|
+
class DeferredList < Phlex::HTML
|
6
|
+
include Phlex::DeferredRender
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@items = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def template
|
13
|
+
if @header
|
14
|
+
h1(class: "header", &@header)
|
15
|
+
end
|
16
|
+
|
17
|
+
ul do
|
18
|
+
@items.each do |item|
|
19
|
+
li { render(item) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def header(&block)
|
25
|
+
@header = block
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_item(&content)
|
29
|
+
@items << content
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class SlotableList < Phlex::HTML
|
34
|
+
include Phlex::Slotable
|
35
|
+
|
36
|
+
slot :header
|
37
|
+
slot :item, many: true
|
38
|
+
|
39
|
+
def template
|
40
|
+
if header_slot
|
41
|
+
h1(class: "header", &header_slot)
|
42
|
+
end
|
43
|
+
|
44
|
+
ul do
|
45
|
+
item_slots.each do |slot|
|
46
|
+
li { render(slot) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class DeferredListExample < Phlex::HTML
|
53
|
+
def template
|
54
|
+
render DeferredList.new do |list|
|
55
|
+
list.header do
|
56
|
+
"Header"
|
57
|
+
end
|
58
|
+
|
59
|
+
list.with_item do
|
60
|
+
"One"
|
61
|
+
end
|
62
|
+
|
63
|
+
list.with_item do
|
64
|
+
"two"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class SlotableListExample < Phlex::HTML
|
71
|
+
def template
|
72
|
+
render SlotableList.new do |list|
|
73
|
+
list.with_header do
|
74
|
+
"Header"
|
75
|
+
end
|
76
|
+
|
77
|
+
list.with_item do
|
78
|
+
"One"
|
79
|
+
end
|
80
|
+
|
81
|
+
list.with_item do
|
82
|
+
"two"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
puts RUBY_DESCRIPTION
|
89
|
+
|
90
|
+
deferred_list = DeferredListExample.new.call
|
91
|
+
slotable_list = SlotableListExample.new.call
|
92
|
+
|
93
|
+
raise unless deferred_list == slotable_list
|
94
|
+
|
95
|
+
Benchmark.bmbm do |x|
|
96
|
+
x.report("Deferred") { 1_000_000.times { DeferredListExample.new.call } }
|
97
|
+
x.report("Slotable") { 1_000_000.times { SlotableListExample.new.call } }
|
98
|
+
end
|
99
|
+
|
100
|
+
puts
|
101
|
+
|
102
|
+
Benchmark.ips do |x|
|
103
|
+
x.report("Deferred") { DeferredListExample.new.call }
|
104
|
+
x.report("Slotable") { SlotableListExample.new.call }
|
105
|
+
x.compare!
|
106
|
+
end
|
data/lib/phlex/slotable.rb
CHANGED
@@ -9,63 +9,97 @@ module Phlex
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
-
def slot(slot_name, callable = nil, many: false)
|
12
|
+
def slot(slot_name, callable = nil, types: nil, many: false)
|
13
13
|
include Phlex::DeferredRender
|
14
14
|
|
15
|
-
if
|
16
|
-
|
17
|
-
|
15
|
+
if types
|
16
|
+
types.each do |type, callable|
|
17
|
+
define_setter_method(slot_name, callable, many: many, type: type)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
define_setter_method(slot_name, callable, many: many)
|
18
21
|
end
|
22
|
+
define_predicate_method(slot_name, many: many)
|
23
|
+
define_getter_method(slot_name, many: many)
|
24
|
+
end
|
19
25
|
|
20
|
-
|
21
|
-
define_method :"with_#{slot_name}" do |*args, **kwargs, &block|
|
22
|
-
instance_variable_set(:"@#{slot_name}_slots", []) unless instance_variable_defined?(:"@#{slot_name}_slots")
|
26
|
+
private
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
def define_setter_method(slot_name, callable, many:, type: nil)
|
29
|
+
slot_name_with_type = type ? "#{type}_#{slot_name}" : slot_name
|
30
|
+
|
31
|
+
setter_method = if many
|
32
|
+
<<-RUBY
|
33
|
+
def with_#{slot_name_with_type}(*args, **kwargs, &block)
|
34
|
+
@#{slot_name}_slots ||= []
|
35
|
+
@#{slot_name}_slots << #{callable_value(slot_name_with_type, callable)}
|
36
|
+
end
|
37
|
+
RUBY
|
38
|
+
else
|
39
|
+
<<-RUBY
|
40
|
+
def with_#{slot_name_with_type}(*args, **kwargs, &block)
|
41
|
+
@#{slot_name}_slot = #{callable_value(slot_name_with_type, callable)}
|
33
42
|
end
|
43
|
+
RUBY
|
44
|
+
end
|
34
45
|
|
35
|
-
|
36
|
-
|
46
|
+
class_eval(setter_method, __FILE__, __LINE__ + 1)
|
47
|
+
define_lambda_method(slot_name_with_type, callable) if callable.is_a?(Proc)
|
48
|
+
end
|
37
49
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
50
|
+
def define_lambda_method(slot_name, callable)
|
51
|
+
define_method :"__call_#{slot_name}__", &callable
|
52
|
+
private :"__call_#{slot_name}__"
|
53
|
+
end
|
42
54
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
55
|
+
def define_getter_method(slot_name, many:)
|
56
|
+
getter_method = if many
|
57
|
+
<<-RUBY
|
58
|
+
def #{slot_name}_slots
|
59
|
+
@#{slot_name}_slots ||= []
|
60
|
+
end
|
61
|
+
private :#{slot_name}_slots
|
62
|
+
RUBY
|
47
63
|
else
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
block
|
52
|
-
when String
|
53
|
-
self.class.const_get(callable).new(*args, **kwargs, &block)
|
54
|
-
when Proc
|
55
|
-
-> { self.class.instance_method(:"__call_#{slot_name}__").bind_call(self, *args, **kwargs, &block) }
|
56
|
-
else
|
57
|
-
callable.new(*args, **kwargs, &block)
|
64
|
+
<<-RUBY
|
65
|
+
def #{slot_name}_slot
|
66
|
+
@#{slot_name}_slot
|
58
67
|
end
|
68
|
+
private :#{slot_name}_slot
|
69
|
+
RUBY
|
70
|
+
end
|
59
71
|
|
60
|
-
|
61
|
-
|
72
|
+
class_eval(getter_method, __FILE__, __LINE__ + 1)
|
73
|
+
end
|
62
74
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
75
|
+
def define_predicate_method(slot_name, many:)
|
76
|
+
predicate_method = if many
|
77
|
+
<<-RUBY
|
78
|
+
def #{slot_name}_slots?
|
79
|
+
#{slot_name}_slots.any?
|
80
|
+
end
|
81
|
+
private :#{slot_name}_slots?
|
82
|
+
RUBY
|
83
|
+
else
|
84
|
+
<<-RUBY
|
85
|
+
def #{slot_name}_slot?
|
86
|
+
!#{slot_name}_slot.nil?
|
87
|
+
end
|
88
|
+
private :#{slot_name}_slot?
|
89
|
+
RUBY
|
90
|
+
end
|
67
91
|
|
68
|
-
|
92
|
+
class_eval(predicate_method, __FILE__, __LINE__ + 1)
|
93
|
+
end
|
94
|
+
|
95
|
+
def callable_value(slot_name, callable)
|
96
|
+
case callable
|
97
|
+
when nil
|
98
|
+
%(block)
|
99
|
+
when Proc
|
100
|
+
%(-> { self.class.instance_method(:"__call_#{slot_name}__").bind_call(self, *args, **kwargs, &block) })
|
101
|
+
else
|
102
|
+
%(#{callable}.new(*args, **kwargs, &block))
|
69
103
|
end
|
70
104
|
end
|
71
105
|
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.
|
4
|
+
version: 0.3.1
|
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-
|
11
|
+
date: 2024-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- LICENSE.txt
|
38
38
|
- README.md
|
39
39
|
- Rakefile
|
40
|
+
- benchmark/main.rb
|
40
41
|
- lib/phlex/slotable.rb
|
41
42
|
- lib/phlex/slotable/version.rb
|
42
43
|
homepage: https://github.com/stephannv/phlex-slotable
|