phlex-slotable 0.2.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +76 -2
- data/benchmark/main.rb +105 -0
- data/lib/phlex/slotable/version.rb +1 -1
- data/lib/phlex/slotable.rb +72 -44
- 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: 6daa3c251793367884c7260e217d1df50891db3e5e5d07d3510dce8dc9dab868
|
4
|
+
data.tar.gz: 95a6c59d0c7571933821a8df89b76542789d16fe222677b7be48af06730bd7f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c04f7d85f3068f064d8b45566de267975cd73d86d5fe7b34114865836b8cdf064cc218f01c86ddc5ffce9b942d39b1ca63f4188af23222e86f4cd6570ad516b
|
7
|
+
data.tar.gz: 14ac74deabd99d4af0b9370279bb0bf592d9116426c7037f40cee94c198f2d92a3dd034577218b6aaa0fa118cc7e54869966177ada7564ee96b00ca2d0e78687
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,46 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.0] - 2024-02-14
|
4
|
+
|
5
|
+
- Match Slotable peformance with DeferredRender
|
6
|
+
```
|
7
|
+
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
|
8
|
+
Warming up --------------------------------------
|
9
|
+
Deferred 26.779k i/100ms
|
10
|
+
Slotable 0.2.0 21.636k i/100ms
|
11
|
+
Slotable 0.3.0 27.013k i/100ms
|
12
|
+
Calculating -------------------------------------
|
13
|
+
Deferred 267.884k (± 0.6%) i/s - 1.366M in 5.098391s
|
14
|
+
Slotable 0.2.0 216.193k (± 0.4%) i/s - 1.082M in 5.003961s
|
15
|
+
Slotable 0.3.0 270.082k (± 0.5%) i/s - 1.351M in 5.001001s
|
16
|
+
```
|
17
|
+
*stephannv*
|
18
|
+
|
19
|
+
- Allow polymorphic slots
|
20
|
+
```ruby
|
21
|
+
class CardComponent < Phlex::HTML
|
22
|
+
include Phlex::Slotable
|
23
|
+
|
24
|
+
slot :avatar, types: { icon: IconComponent, image: ImageComponent }
|
25
|
+
|
26
|
+
def template
|
27
|
+
if avatar_slot?
|
28
|
+
render avatar_slot
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
render CardComponent.new do |card|
|
34
|
+
if user
|
35
|
+
card.with_image_avatar(src: user.image_url)
|
36
|
+
else
|
37
|
+
card.with_icon_avatar(name: :user)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
*stephannv*
|
43
|
+
|
3
44
|
## [0.2.0] - 2024-02-13
|
4
45
|
|
5
46
|
- Allow view slots using string as class name
|
data/README.md
CHANGED
@@ -252,7 +252,7 @@ class MyPage < Phlex::HTML
|
|
252
252
|
end
|
253
253
|
```
|
254
254
|
|
255
|
-
You can access the internal view state within lambda slots.For example:
|
255
|
+
You can access the internal view state within lambda slots. For example:
|
256
256
|
```ruby
|
257
257
|
class BlogComponent < Phlex::HTML
|
258
258
|
include Phlex::Slotable
|
@@ -273,10 +273,84 @@ class MyPage < Phlex::HTML
|
|
273
273
|
end
|
274
274
|
```
|
275
275
|
|
276
|
+
#### Polymorphic slots
|
277
|
+
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.
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
class CardComponent < Phlex::HTML
|
281
|
+
include Phlex::Slotable
|
282
|
+
|
283
|
+
slot :avatar, types: { icon: IconComponent, image: ImageComponent }
|
284
|
+
|
285
|
+
def template
|
286
|
+
if avatar_slot?
|
287
|
+
figure id: "avatar" do
|
288
|
+
render avatar_slot
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
```
|
294
|
+
|
295
|
+
This allows you to set the icon slot using `with_icon_avatar` or the image slot using `with_image_avatar`:
|
296
|
+
```ruby
|
297
|
+
class UserCardComponent < Phlex::HTML
|
298
|
+
def initialize(user:)
|
299
|
+
@user = user
|
300
|
+
end
|
301
|
+
|
302
|
+
def template
|
303
|
+
render CardComponent.new do |card|
|
304
|
+
if @user.image?
|
305
|
+
card.with_image_avatar(src: @user.image)
|
306
|
+
else
|
307
|
+
card.with_icon_avatar(name: :user)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
Please note that you can still utilize the other slot definition APIs:
|
315
|
+
```ruby
|
316
|
+
class CardComponent < Phlex::HTML
|
317
|
+
include Phlex::Slotable
|
318
|
+
|
319
|
+
slot :avatar, types: {
|
320
|
+
icon: IconComponent,
|
321
|
+
image: "ImageComponent",
|
322
|
+
text: ->(size:, &content) { span(class: "text-#{size}", &content) }
|
323
|
+
}, many: true
|
324
|
+
|
325
|
+
def template
|
326
|
+
if avatar_slots?
|
327
|
+
avatar_slots.each do |slot|
|
328
|
+
render slot
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
span { "Count: #{avatar_slots.size}" }
|
333
|
+
end
|
334
|
+
|
335
|
+
...
|
336
|
+
end
|
337
|
+
|
338
|
+
class UsersCardComponent < Phlex::HTML
|
339
|
+
def template
|
340
|
+
render CardComponent.new do |card|
|
341
|
+
card.with_image_avatar(src: @user.image)
|
342
|
+
card.with_icon_avatar(name: :user)
|
343
|
+
card.with_text_avatar(size: :lg) { "SV" }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
```
|
348
|
+
|
349
|
+
|
276
350
|
## Roadmap
|
277
351
|
- ✅ ~~Accept Strings as view class name~~
|
278
352
|
- ✅ ~~Allow lambda slots~~
|
279
|
-
-
|
353
|
+
- ✅ ~~Allow polymorphic slots~~
|
280
354
|
|
281
355
|
## Development
|
282
356
|
|
data/benchmark/main.rb
ADDED
@@ -0,0 +1,105 @@
|
|
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
|
+
end
|
data/lib/phlex/slotable.rb
CHANGED
@@ -9,63 +9,91 @@ 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
|
-
|
47
|
-
|
48
|
-
define_method :"with_#{slot_name}" do |*args, **kwargs, &block|
|
49
|
-
value = case callable
|
50
|
-
when nil
|
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)
|
55
|
+
def define_getter_method(slot_name, many:)
|
56
|
+
getter_method = if many
|
57
|
+
<<-RUBY
|
58
|
+
def #{slot_name}_slots
|
59
|
+
@#{slot_name}_slots ||= []
|
58
60
|
end
|
61
|
+
private :#{slot_name}_slots
|
62
|
+
RUBY
|
63
|
+
else
|
64
|
+
<<-RUBY
|
65
|
+
def #{slot_name}_slot = @#{slot_name}_slot
|
66
|
+
private :#{slot_name}_slot
|
67
|
+
RUBY
|
68
|
+
end
|
59
69
|
|
60
|
-
|
61
|
-
|
70
|
+
class_eval(getter_method, __FILE__, __LINE__ + 1)
|
71
|
+
end
|
62
72
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
73
|
+
def define_predicate_method(slot_name, many:)
|
74
|
+
predicate_method = if many
|
75
|
+
<<-RUBY
|
76
|
+
def #{slot_name}_slots? = #{slot_name}_slots.any?
|
77
|
+
private :#{slot_name}_slots?
|
78
|
+
RUBY
|
79
|
+
else
|
80
|
+
<<-RUBY
|
81
|
+
def #{slot_name}_slot? = !#{slot_name}_slot.nil?
|
82
|
+
private :#{slot_name}_slot?
|
83
|
+
RUBY
|
84
|
+
end
|
67
85
|
|
68
|
-
|
86
|
+
class_eval(predicate_method, __FILE__, __LINE__ + 1)
|
87
|
+
end
|
88
|
+
|
89
|
+
def callable_value(slot_name, callable)
|
90
|
+
case callable
|
91
|
+
when nil
|
92
|
+
%(block)
|
93
|
+
when Proc
|
94
|
+
%(-> { self.class.instance_method(:"__call_#{slot_name}__").bind_call(self, *args, **kwargs, &block) })
|
95
|
+
else
|
96
|
+
%(#{callable}.new(*args, **kwargs, &block))
|
69
97
|
end
|
70
98
|
end
|
71
99
|
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.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-
|
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
|