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.
@@ -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
- context = @_context
13
- return if context.fragments && !context.in_target_fragment
12
+ state = @_state
13
+ return unless state.should_render?
14
14
 
15
- context.buffer << "<!doctype html>"
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
- if Class === constant && constant < Phlex::SGML
53
- constant.include(self)
52
+ case constant
53
+ when Class
54
+ if constant < Phlex::SGML
55
+ constant.include(self)
54
56
 
55
- constant = nil
57
+ constant = nil
56
58
 
57
- define_method(name) do |*args, **kwargs, &block|
58
- constant = me.const_get(name)
59
- render(constant.new(*args, **kwargs), &block)
60
- end
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
- define_singleton_method(name) do |*args, **kwargs, &block|
63
- component, fiber_id = Thread.current[:__phlex_component__]
64
- if (component && fiber_id == Fiber.current.object_id)
65
- component.instance_exec do
66
- constant = me.const_get(name)
67
- render(constant.new(*args, **kwargs), &block)
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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex::NullCacheStore
4
+ extend self
5
+
6
+ def fetch(key)
7
+ yield
8
+ end
9
+ end
@@ -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(<<-RUBY, __FILE__, __LINE__ + 1)
9
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
10
10
  # frozen_string_literal: true
11
11
 
12
12
  def #{method_name}(**attributes)
13
- context = @_context
14
- buffer = context.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
- if fragment
20
- return if fragment.length == 0 # we found all our fragments already
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 ::Phlex::SGML::SafeObject
44
- buffer << content.to_s
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 nil
50
- nil
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 ::Phlex::SGML::SafeObject
71
- buffer << content.to_s
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 nil
77
- nil
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(<<-RUBY, __FILE__, __LINE__ + 1)
90
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
106
91
  # frozen_string_literal: true
107
92
 
108
93
  def #{method_name}(**attributes)
109
- context = @_context
110
- buffer = context.buffer
111
- fragment = context.fragments
94
+ state = @_state
112
95
 
113
- if fragment
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