phlex 2.0.0.rc1 → 2.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|