phlex 2.0.0.rc1 → 2.0.0.rc2
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/lib/phlex/fifo.rb +10 -3
- data/lib/phlex/fifo_cache_store.rb +49 -0
- data/lib/phlex/helpers.rb +1 -1
- data/lib/phlex/html/standard_elements.rb +0 -101
- data/lib/phlex/html/void_elements.rb +0 -11
- data/lib/phlex/html.rb +3 -3
- data/lib/phlex/kit.rb +20 -15
- data/lib/phlex/null_cache_store.rb +9 -0
- data/lib/phlex/sgml/elements.rb +19 -53
- data/lib/phlex/sgml/state.rb +118 -0
- data/lib/phlex/sgml.rb +131 -68
- data/lib/phlex/svg/standard_elements.rb +0 -63
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex.rb +16 -5
- metadata +6 -7
- data/lib/phlex/context.rb +0 -60
@@ -11,7 +11,6 @@ module Phlex::HTML::VoidElements
|
|
11
11
|
id: nil,
|
12
12
|
**attributes
|
13
13
|
) = nil
|
14
|
-
|
15
14
|
# Outputs a `<base>` tag.
|
16
15
|
# See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
17
16
|
__register_void_element__ def base(
|
@@ -21,7 +20,6 @@ module Phlex::HTML::VoidElements
|
|
21
20
|
target: nil,
|
22
21
|
**attributes
|
23
22
|
) = nil
|
24
|
-
|
25
23
|
# Outputs a `<br>` tag.
|
26
24
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/br
|
27
25
|
__register_void_element__ def br(
|
@@ -29,7 +27,6 @@ module Phlex::HTML::VoidElements
|
|
29
27
|
id: nil,
|
30
28
|
**attributes
|
31
29
|
) = nil
|
32
|
-
|
33
30
|
# Outputs a `<col>` tag.
|
34
31
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/col
|
35
32
|
__register_void_element__ def col(
|
@@ -37,7 +34,6 @@ module Phlex::HTML::VoidElements
|
|
37
34
|
id: nil,
|
38
35
|
**attributes
|
39
36
|
) = nil
|
40
|
-
|
41
37
|
# Outputs an `<embed>` tag.
|
42
38
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/embed
|
43
39
|
__register_void_element__ def embed(
|
@@ -45,7 +41,6 @@ module Phlex::HTML::VoidElements
|
|
45
41
|
id: nil,
|
46
42
|
**attributes
|
47
43
|
) = nil
|
48
|
-
|
49
44
|
# Outputs an `<hr>` tag.
|
50
45
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/hr
|
51
46
|
__register_void_element__ def hr(
|
@@ -53,7 +48,6 @@ module Phlex::HTML::VoidElements
|
|
53
48
|
id: nil,
|
54
49
|
**attributes
|
55
50
|
) = nil
|
56
|
-
|
57
51
|
# Outputs an `<img>` tag.
|
58
52
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/img
|
59
53
|
__register_void_element__ def img(
|
@@ -63,7 +57,6 @@ module Phlex::HTML::VoidElements
|
|
63
57
|
src: nil,
|
64
58
|
**attributes
|
65
59
|
) = nil
|
66
|
-
|
67
60
|
# Outputs an `<input>` tag.
|
68
61
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/input
|
69
62
|
__register_void_element__ def input(
|
@@ -73,7 +66,6 @@ module Phlex::HTML::VoidElements
|
|
73
66
|
type: nil,
|
74
67
|
**attributes
|
75
68
|
) = nil
|
76
|
-
|
77
69
|
# Outputs a `<link>` tag.
|
78
70
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/link
|
79
71
|
__register_void_element__ def link(
|
@@ -81,7 +73,6 @@ module Phlex::HTML::VoidElements
|
|
81
73
|
id: nil,
|
82
74
|
**attributes
|
83
75
|
) = nil
|
84
|
-
|
85
76
|
# Outputs a `<meta>` tag.
|
86
77
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/meta
|
87
78
|
__register_void_element__ def meta(
|
@@ -91,7 +82,6 @@ module Phlex::HTML::VoidElements
|
|
91
82
|
name: nil,
|
92
83
|
**attributes
|
93
84
|
) = nil
|
94
|
-
|
95
85
|
# Outputs a `<source>` tag.
|
96
86
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/source
|
97
87
|
__register_void_element__ def source(
|
@@ -99,7 +89,6 @@ module Phlex::HTML::VoidElements
|
|
99
89
|
id: nil,
|
100
90
|
**attributes
|
101
91
|
) = nil
|
102
|
-
|
103
92
|
# Outputs a `<track>` tag.
|
104
93
|
# See https://developer.mozilla.org/docs/Web/HTML/Element/track
|
105
94
|
__register_void_element__ def track(
|
data/lib/phlex/html.rb
CHANGED
@@ -9,10 +9,10 @@ class Phlex::HTML < Phlex::SGML
|
|
9
9
|
|
10
10
|
# Output an HTML doctype.
|
11
11
|
def doctype
|
12
|
-
|
13
|
-
return
|
12
|
+
state = @_state
|
13
|
+
return unless state.should_render?
|
14
14
|
|
15
|
-
|
15
|
+
state.buffer << "<!doctype html>"
|
16
16
|
nil
|
17
17
|
end
|
18
18
|
|
data/lib/phlex/kit.rb
CHANGED
@@ -49,27 +49,32 @@ module Phlex::Kit
|
|
49
49
|
me = self
|
50
50
|
constant = const_get(name)
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
case constant
|
53
|
+
when Class
|
54
|
+
if constant < Phlex::SGML
|
55
|
+
constant.include(self)
|
54
56
|
|
55
|
-
|
57
|
+
constant = nil
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
define_method(name) do |*args, **kwargs, &block|
|
60
|
+
constant = me.const_get(name)
|
61
|
+
render(constant.new(*args, **kwargs), &block)
|
62
|
+
end
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
define_singleton_method(name) do |*args, **kwargs, &block|
|
65
|
+
component, fiber_id = Thread.current[:__phlex_component__]
|
66
|
+
if (component && fiber_id == Fiber.current.object_id)
|
67
|
+
component.instance_exec do
|
68
|
+
constant = me.const_get(name)
|
69
|
+
render(constant.new(*args, **kwargs), &block)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
raise "You can't call `#{name}' outside of a Phlex rendering context."
|
68
73
|
end
|
69
|
-
else
|
70
|
-
raise "You can't call `#{name}' outside of a Phlex rendering context."
|
71
74
|
end
|
72
75
|
end
|
76
|
+
when Module
|
77
|
+
constant.extend(Phlex::Kit)
|
73
78
|
end
|
74
79
|
|
75
80
|
super
|
data/lib/phlex/sgml/elements.rb
CHANGED
@@ -6,30 +6,17 @@ module Phlex::SGML::Elements
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def register_element(method_name, tag: method_name.name.tr("_", "-"))
|
9
|
-
class_eval(
|
9
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
10
10
|
# frozen_string_literal: true
|
11
11
|
|
12
12
|
def #{method_name}(**attributes)
|
13
|
-
|
14
|
-
buffer =
|
15
|
-
fragment = context.fragments
|
16
|
-
target_found = false
|
13
|
+
state = @_state
|
14
|
+
buffer = state.buffer
|
17
15
|
block_given = block_given?
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
id = attributes[:id]
|
23
|
-
|
24
|
-
if !context.in_target_fragment
|
25
|
-
if fragment[id]
|
26
|
-
context.begin_target(id)
|
27
|
-
target_found = true
|
28
|
-
else
|
29
|
-
yield(self) if block_given
|
30
|
-
return nil
|
31
|
-
end
|
32
|
-
end
|
17
|
+
unless state.should_render?
|
18
|
+
yield(self) if block_given
|
19
|
+
return nil
|
33
20
|
end
|
34
21
|
|
35
22
|
if attributes.length > 0 # with attributes
|
@@ -40,14 +27,14 @@ module Phlex::SGML::Elements
|
|
40
27
|
content = yield(self)
|
41
28
|
if original_length == buffer.bytesize
|
42
29
|
case content
|
43
|
-
when
|
44
|
-
|
30
|
+
when nil
|
31
|
+
nil
|
45
32
|
when String
|
46
33
|
buffer << ::Phlex::Escape.html_escape(content)
|
47
34
|
when Symbol
|
48
35
|
buffer << ::Phlex::Escape.html_escape(content.name)
|
49
|
-
when
|
50
|
-
|
36
|
+
when ::Phlex::SGML::SafeObject
|
37
|
+
buffer << content.to_s
|
51
38
|
else
|
52
39
|
if (formatted_object = format_object(content))
|
53
40
|
buffer << ::Phlex::Escape.html_escape(formatted_object)
|
@@ -67,14 +54,14 @@ module Phlex::SGML::Elements
|
|
67
54
|
content = yield(self)
|
68
55
|
if original_length == buffer.bytesize
|
69
56
|
case content
|
70
|
-
when
|
71
|
-
|
57
|
+
when nil
|
58
|
+
nil
|
72
59
|
when String
|
73
60
|
buffer << ::Phlex::Escape.html_escape(content)
|
74
61
|
when Symbol
|
75
62
|
buffer << ::Phlex::Escape.html_escape(content.name)
|
76
|
-
when
|
77
|
-
|
63
|
+
when ::Phlex::SGML::SafeObject
|
64
|
+
buffer << content.to_s
|
78
65
|
else
|
79
66
|
if (formatted_object = format_object(content))
|
80
67
|
buffer << ::Phlex::Escape.html_escape(formatted_object)
|
@@ -90,8 +77,6 @@ module Phlex::SGML::Elements
|
|
90
77
|
|
91
78
|
#{'flush' if tag == 'head'}
|
92
79
|
|
93
|
-
context.end_target if target_found
|
94
|
-
|
95
80
|
nil
|
96
81
|
end
|
97
82
|
RUBY
|
@@ -102,41 +87,22 @@ module Phlex::SGML::Elements
|
|
102
87
|
end
|
103
88
|
|
104
89
|
def __register_void_element__(method_name, tag: method_name.name.tr("_", "-"))
|
105
|
-
class_eval(
|
90
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
106
91
|
# frozen_string_literal: true
|
107
92
|
|
108
93
|
def #{method_name}(**attributes)
|
109
|
-
|
110
|
-
buffer = context.buffer
|
111
|
-
fragment = context.fragments
|
94
|
+
state = @_state
|
112
95
|
|
113
|
-
|
114
|
-
return if fragment.length == 0 # we found all our fragments already
|
115
|
-
|
116
|
-
id = attributes[:id]
|
117
|
-
|
118
|
-
if !context.in_target_fragment
|
119
|
-
if fragment[id]
|
120
|
-
context.begin_target(id)
|
121
|
-
target_found = true
|
122
|
-
else
|
123
|
-
return nil
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
96
|
+
return unless state.should_render?
|
127
97
|
|
128
98
|
if attributes.length > 0 # with attributes
|
129
|
-
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
99
|
+
state.buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
130
100
|
else # without attributes
|
131
|
-
buffer << "<#{tag}>"
|
101
|
+
state.buffer << "<#{tag}>"
|
132
102
|
end
|
133
103
|
|
134
|
-
context.end_target if target_found
|
135
|
-
|
136
104
|
nil
|
137
105
|
end
|
138
|
-
|
139
|
-
alias_method :_#{method_name}, :#{method_name}
|
140
106
|
RUBY
|
141
107
|
|
142
108
|
__registered_elements__[method_name] = tag
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Phlex::SGML::State
|
4
|
+
def initialize(user_context: {}, view_context: nil, output_buffer:, fragments:)
|
5
|
+
@buffer = +""
|
6
|
+
@capturing = false
|
7
|
+
@user_context = user_context
|
8
|
+
@fragments = fragments
|
9
|
+
@fragment_depth = 0
|
10
|
+
@cache_stack = []
|
11
|
+
@halt_signal = nil
|
12
|
+
@view_context = view_context
|
13
|
+
@output_buffer = output_buffer
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :buffer, :capturing, :user_context
|
17
|
+
|
18
|
+
attr_reader :fragments, :fragment_depth, :view_context, :output_buffer
|
19
|
+
|
20
|
+
def around_render(component)
|
21
|
+
stack = @stack
|
22
|
+
|
23
|
+
if !@fragments || @halt_signal
|
24
|
+
yield
|
25
|
+
else
|
26
|
+
catch do |signal|
|
27
|
+
@halt_signal = signal
|
28
|
+
yield
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def should_render?
|
34
|
+
!@fragments || @fragment_depth > 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def begin_fragment(id)
|
38
|
+
@fragment_depth += 1 if @fragments&.include?(id)
|
39
|
+
|
40
|
+
if caching?
|
41
|
+
current_byte_offset = 0 # Start tracking the byte offset of this fragment from the start of the cache buffer
|
42
|
+
@cache_stack.reverse_each do |(cache_buffer, fragment_map)| # We'll iterate deepest to shallowest
|
43
|
+
current_byte_offset += cache_buffer.bytesize # Add the length of the cache buffer to the current byte offset
|
44
|
+
fragment_map[id] = [current_byte_offset, nil, []] # Record the byte offset, length, and store a list of the nested fragments
|
45
|
+
|
46
|
+
fragment_map.each do |name, (_offset, length, nested_fragments)| # Iterate over the other fragments
|
47
|
+
next if name == id || length # Skip if it's the current fragment, or if the fragment has already ended
|
48
|
+
nested_fragments << id # Add the current fragment to the list of nested fragments
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def end_fragment(id)
|
55
|
+
if caching?
|
56
|
+
byte_length = nil
|
57
|
+
@cache_stack.reverse_each do |(cache_buffer, fragment_map)| # We'll iterate deepest to shallowest
|
58
|
+
byte_length ||= cache_buffer.bytesize - fragment_map[id][0] # The byte length is the difference between the current byte offset and the byte offset of the fragment
|
59
|
+
fragment_map[id][1] = byte_length # All cache contexts will use the same by
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return unless @fragments&.include?(id)
|
64
|
+
|
65
|
+
@fragments.delete(id)
|
66
|
+
@fragment_depth -= 1
|
67
|
+
throw @halt_signal if @fragments.length == 0
|
68
|
+
end
|
69
|
+
|
70
|
+
def record_fragment(id, offset, length, nested_fragments)
|
71
|
+
return unless caching?
|
72
|
+
|
73
|
+
@cache_stack.reverse_each do |(cache_buffer, fragment_map)|
|
74
|
+
offset += cache_buffer.bytesize
|
75
|
+
fragment_map[id] = [offset, length, nested_fragments]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def caching(&)
|
80
|
+
buffer = +""
|
81
|
+
@cache_stack.push([buffer, {}].freeze)
|
82
|
+
capturing_into(buffer, &)
|
83
|
+
@cache_stack.pop
|
84
|
+
end
|
85
|
+
|
86
|
+
def caching?
|
87
|
+
@cache_stack.length > 0
|
88
|
+
end
|
89
|
+
|
90
|
+
def capturing_into(new_buffer)
|
91
|
+
original_buffer = @buffer
|
92
|
+
original_capturing = @capturing
|
93
|
+
original_fragments = @fragments
|
94
|
+
|
95
|
+
begin
|
96
|
+
@buffer = new_buffer
|
97
|
+
@capturing = true
|
98
|
+
@fragments = nil
|
99
|
+
yield
|
100
|
+
ensure
|
101
|
+
@buffer = original_buffer
|
102
|
+
@capturing = original_capturing
|
103
|
+
@fragments = original_fragments
|
104
|
+
end
|
105
|
+
|
106
|
+
new_buffer
|
107
|
+
end
|
108
|
+
|
109
|
+
def flush
|
110
|
+
return if capturing
|
111
|
+
|
112
|
+
buffer = @buffer
|
113
|
+
@output_buffer << buffer.dup
|
114
|
+
|
115
|
+
buffer.clear
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|