phlex 1.5.1 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of phlex might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 647e5f1a1f3beb8dd596cb87a5fb848f07e8e87e28ce705f3d1c93d8c9cfe9ac
4
- data.tar.gz: d2b327f45d36a44069cf6dd9de03310918f56647bc1a40c6eb675837eb1cb653
3
+ metadata.gz: ada5b83ea9c2fbd305a249ffa81a43c9e9da26a7c87860dac62e594a43341001
4
+ data.tar.gz: e728f6c83cae97671bf4092f7bd384c93611d51158f613a75518d1109a669942
5
5
  SHA512:
6
- metadata.gz: '0387ae5e9183fb312619950e5a5a6d75d1383c58e46be6f389e781a5cbdf2b3658efc4cbd298eb7b9d1ad0f9042c857ecc11c3812a2bd1600ee04a79f9fc1d8a'
7
- data.tar.gz: 6159fbe6ac1f52baf8924e9309a43ae252a47655f7feaf380787fb4d5a56e83380154dd906e8a964b0754fa0f4827e3e3b028ca9f4a03ded5aace8431edf0452
6
+ metadata.gz: 6f8b512d89247318f657482d3eeb1ba9fd0c1de73edcf4f3c5d0b6ff843bbd0bbca6d47d9a957780a1831e70b67ec8c2d1d1beba10a00bc9546a8966f67d545d
7
+ data.tar.gz: a5a780a277bc36e8fa028af0ccdd3b985189ae6c49241d737b9d86e4f37fa43bbbfc8e4cb1736914735a35821a741c8fcc964f45c8b82b96c5389053c72b2684
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Phlex::Context
4
+ def initialize
5
+ @target = +""
6
+ end
7
+
8
+ attr_accessor :target
9
+
10
+ def with_target(new_target)
11
+ original_target = @target
12
+
13
+ begin
14
+ @target = new_target
15
+ yield
16
+ ensure
17
+ @target = original_target
18
+ end
19
+
20
+ new_target
21
+ end
22
+ end
@@ -3,11 +3,11 @@
3
3
  module Phlex
4
4
  module ElementClobberingGuard
5
5
  def method_added(method_name)
6
- if method_name[0] == "_" && private_instance_methods.include?(:"__phlex#{method_name}__")
6
+ if method_name[0] == "_" && element_method?(method_name[1..].to_sym)
7
7
  raise NameError, "👋 Redefining the method `#{name}##{method_name}` is not a good idea."
8
+ else
9
+ super
8
10
  end
9
-
10
- super
11
11
  end
12
12
  end
13
13
  end
@@ -5,45 +5,44 @@ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0")
5
5
  end
6
6
 
7
7
  module Phlex::Elements
8
- private def slow_registered_elements
9
- private_instance_methods
10
- .lazy
11
- .map(&:to_s)
12
- .select { |m| m.start_with?("__phlex_") }
13
- .map { |m| m[8...-2].to_sym }
8
+ def registered_elements
9
+ @registered_elements ||= Concurrent::Map.new
14
10
  end
15
11
 
16
12
  def register_element(element, tag: element.name.tr("_", "-"))
17
13
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
18
14
  # frozen_string_literal: true
19
15
 
20
- def __phlex_#{element}__(**attributes, &block)
16
+
17
+ def #{element}(**attributes, &block)
18
+ target = @_context.target
19
+
21
20
  if attributes.length > 0 # with attributes
22
21
  if block_given? # with content block
23
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
22
+ target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
24
23
  yield_content(&block)
25
- @_target << "</#{tag}>"
24
+ target << "</#{tag}>"
26
25
  else # without content block
27
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << "></#{tag}>"
26
+ target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << "></#{tag}>"
28
27
  end
29
28
  else # without attributes
30
29
  if block_given? # with content block
31
- @_target << "<#{tag}>"
30
+ target << "<#{tag}>"
32
31
  yield_content(&block)
33
- @_target << "</#{tag}>"
32
+ target << "</#{tag}>"
34
33
  else # without content block
35
- @_target << "<#{tag}></#{tag}>"
34
+ target << "<#{tag}></#{tag}>"
36
35
  end
37
36
  end
38
37
 
39
38
  nil
40
39
  end
41
40
 
42
- alias_method :_#{element}, :__phlex_#{element}__
43
- alias_method :#{element}, :__phlex_#{element}__
44
- private :__phlex_#{element}__
41
+ alias_method :_#{element}, :#{element}
45
42
  RUBY
46
43
 
44
+ registered_elements[element] = tag
45
+
47
46
  element
48
47
  end
49
48
 
@@ -51,21 +50,23 @@ module Phlex::Elements
51
50
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
52
51
  # frozen_string_literal: true
53
52
 
54
- def __phlex_#{element}__(**attributes)
53
+ def #{element}(**attributes)
54
+ target = @_context.target
55
+
55
56
  if attributes.length > 0 # with attributes
56
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
57
+ target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
57
58
  else # without attributes
58
- @_target << "<#{tag}>"
59
+ target << "<#{tag}>"
59
60
  end
60
61
 
61
62
  nil
62
63
  end
63
64
 
64
- alias_method :_#{element}, :__phlex_#{element}__
65
- alias_method :#{element}, :__phlex_#{element}__
66
- private :__phlex_#{element}__
65
+ alias_method :_#{element}, :#{element}
67
66
  RUBY
68
67
 
68
+ registered_elements[element] = tag
69
+
69
70
  element
70
71
  end
71
72
  end
data/lib/phlex/html.rb CHANGED
@@ -25,7 +25,7 @@ module Phlex
25
25
 
26
26
  # Output an HTML doctype.
27
27
  def doctype
28
- @_target << "<!DOCTYPE html>"
28
+ @_context.target << "<!DOCTYPE html>"
29
29
  nil
30
30
  end
31
31
 
data/lib/phlex/sgml.rb CHANGED
@@ -12,8 +12,6 @@ module Phlex
12
12
  new(...).call
13
13
  end
14
14
 
15
- alias_method :render, :call
16
-
17
15
  # Create a new instance of the component.
18
16
  # @note The block will not be delegated to the initializer. Instead, it will be provided to `template` when rendering.
19
17
  def new(*args, **kwargs, &block)
@@ -31,28 +29,39 @@ module Phlex
31
29
  alias_method :__attributes__, :__final_attributes__
32
30
  alias_method :call, :__final_call__
33
31
  end
32
+
33
+ # @api private
34
+ def element_method?(method_name)
35
+ return false unless instance_methods.include?(method_name)
36
+
37
+ owner = instance_method(method_name).owner
38
+
39
+ return true if owner.is_a?(Phlex::Elements) && owner.registered_elements[method_name]
40
+
41
+ false
42
+ end
34
43
  end
35
44
 
36
45
  # Renders the view and returns the buffer. The default buffer is a mutable String.
37
- def call(buffer = nil, target: +"", view_context: nil, parent: nil, &block)
38
- __final_call__(buffer, target: target, view_context: view_context, parent: parent, &block).tap do
46
+ def call(buffer = nil, context: Phlex::Context.new, view_context: nil, parent: nil, &block)
47
+ __final_call__(buffer, context: context, view_context: view_context, parent: parent, &block).tap do
39
48
  self.class.rendered_at_least_once!
40
49
  end
41
50
  end
42
51
 
43
52
  # @api private
44
- def __final_call__(buffer = nil, target: +"", view_context: nil, parent: nil, &block)
45
- @_target = target
53
+ def __final_call__(buffer = nil, context: Phlex::Context.new, view_context: nil, parent: nil, &block)
54
+ @_context = context
46
55
  @_view_context = view_context
47
56
  @_parent = parent
48
57
 
49
58
  block ||= @_content_block
50
59
 
51
- return buffer || target unless render?
60
+ return buffer || context.target unless render?
52
61
 
53
62
  around_template do
54
63
  if block
55
- if DeferredRender === self
64
+ if is_a?(DeferredRender)
56
65
  __vanish__(self, &block)
57
66
  template
58
67
  else
@@ -69,7 +78,7 @@ module Phlex
69
78
  end
70
79
  end
71
80
 
72
- buffer ? (buffer << target) : target
81
+ buffer ? (buffer << context.target) : context.target
73
82
  end
74
83
 
75
84
  # Render another view
@@ -78,11 +87,15 @@ module Phlex
78
87
  def render(renderable, &block)
79
88
  case renderable
80
89
  when Phlex::SGML
81
- renderable.call(target: @_target, view_context: @_view_context, parent: self, &block)
90
+ renderable.call(context: @_context, view_context: @_view_context, parent: self, &block)
82
91
  when Class
83
92
  if renderable < Phlex::SGML
84
- renderable.new.call(target: @_target, view_context: @_view_context, parent: self, &block)
93
+ renderable.new.call(context: @_context, view_context: @_view_context, parent: self, &block)
85
94
  end
95
+ when Enumerable
96
+ renderable.each { |r| render(r, &block) }
97
+ when Proc
98
+ yield_content(&renderable)
86
99
  else
87
100
  raise ArgumentError, "You can't render a #{renderable}."
88
101
  end
@@ -95,16 +108,16 @@ module Phlex
95
108
  def plain(content)
96
109
  case content
97
110
  when String
98
- @_target << ERB::Escape.html_escape(content)
111
+ @_context.target << ERB::Escape.html_escape(content)
99
112
  when Symbol
100
- @_target << ERB::Escape.html_escape(content.name)
113
+ @_context.target << ERB::Escape.html_escape(content.name)
101
114
  when Integer
102
- @_target << ERB::Escape.html_escape(content.to_s)
115
+ @_context.target << ERB::Escape.html_escape(content.to_s)
103
116
  when nil
104
117
  nil
105
118
  else
106
119
  if (formatted_object = format_object(content))
107
- @_target << ERB::Escape.html_escape(formatted_object)
120
+ @_context.target << ERB::Escape.html_escape(formatted_object)
108
121
  end
109
122
  end
110
123
 
@@ -114,11 +127,13 @@ module Phlex
114
127
  # Output a whitespace character. This is useful for getting inline elements to wrap. If you pass a block, a whitespace will be output before and after yielding the block.
115
128
  # @return [nil]
116
129
  def whitespace
117
- @_target << " "
130
+ target = @_context.target
131
+
132
+ target << " "
118
133
 
119
134
  if block_given?
120
135
  yield
121
- @_target << " "
136
+ target << " "
122
137
  end
123
138
 
124
139
  nil
@@ -127,9 +142,11 @@ module Phlex
127
142
  # Output an HTML comment.
128
143
  # @return [nil]
129
144
  def comment(&block)
130
- @_target << "<!-- "
145
+ target = @_context.target
146
+
147
+ target << "<!-- "
131
148
  yield_content(&block)
132
- @_target << " -->"
149
+ target << " -->"
133
150
 
134
151
  nil
135
152
  end
@@ -140,27 +157,17 @@ module Phlex
140
157
  def unsafe_raw(content = nil)
141
158
  return nil unless content
142
159
 
143
- @_target << content
160
+ @_context.target << content
144
161
  nil
145
162
  end
146
163
 
147
164
  # Capture a block of output as a String.
165
+ # @note This only works if the block's receiver is the current component or the block returns a String.
148
166
  # @return [String]
149
167
  def capture(&block)
150
- return "" unless block_given?
168
+ return "" unless block
151
169
 
152
- original_buffer_content = @_target.dup
153
- @_target.clear
154
-
155
- begin
156
- yield_content(&block)
157
- new_buffer_content = @_target.dup
158
- ensure
159
- @_target.clear
160
- @_target << original_buffer_content
161
- end
162
-
163
- new_buffer_content.is_a?(String) ? new_buffer_content : ""
170
+ @_context.with_target(+"") { yield_content(&block) }
164
171
  end
165
172
 
166
173
  # Like `capture` but the output is vanished into a BlackHole buffer.
@@ -169,14 +176,7 @@ module Phlex
169
176
  private def __vanish__(*args)
170
177
  return unless block_given?
171
178
 
172
- original_buffer = @_target
173
-
174
- begin
175
- @_target = BlackHole
176
- yield(*args)
177
- ensure
178
- @_target = original_buffer
179
- end
179
+ @_context.with_target(BlackHole) { yield(*args) }
180
180
 
181
181
  nil
182
182
  end
@@ -223,10 +223,11 @@ module Phlex
223
223
  private def yield_content
224
224
  return unless block_given?
225
225
 
226
- original_length = @_target.length
227
- content = yield(self)
226
+ target = @_context.target
228
227
 
229
- plain(content) if original_length == @_target.length
228
+ original_length = target.length
229
+ content = yield(self)
230
+ plain(content) if original_length == target.length
230
231
 
231
232
  nil
232
233
  end
@@ -236,9 +237,11 @@ module Phlex
236
237
  private def yield_content_with_args(*args)
237
238
  return unless block_given?
238
239
 
239
- original_length = @_target.length
240
+ target = @_context.target
241
+
242
+ original_length = target.length
240
243
  content = yield(*args)
241
- plain(content) if original_length == @_target.length
244
+ plain(content) if original_length == target.length
242
245
 
243
246
  nil
244
247
  end
data/lib/phlex/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex
4
- VERSION = "1.5.1"
4
+ VERSION = "1.6.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlex
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-06 00:00:00.000000000 Z
11
+ date: 2023-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -76,6 +76,7 @@ files:
76
76
  - lib/phlex.rb
77
77
  - lib/phlex/black_hole.rb
78
78
  - lib/phlex/callable.rb
79
+ - lib/phlex/context.rb
79
80
  - lib/phlex/deferred_render.rb
80
81
  - lib/phlex/element_clobbering_guard.rb
81
82
  - lib/phlex/elements.rb