phlex 1.3.3 → 1.4.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: 1159a0896a3d636c2a5c2a9a08f33e9763378aaba3cef5ab91a0f6988c3fc13d
4
- data.tar.gz: '0288a77615f58f361e0d5cbebd87b937dfd4ec040eda9d1229247168db48bc71'
3
+ metadata.gz: '095f1f63ec98350892c0e8602d2cdee3540c27e47535738589139b0c1bad35b3'
4
+ data.tar.gz: 62a32d3d94a1bb2803d48df510c0ea01a2903e047cbe6a6da76d9ceceed7004c
5
5
  SHA512:
6
- metadata.gz: 8d51860ec7cf43b2071baaa32636450e1f31684ff5bef5f87352f1f2ac31fc4edd5740f0441f8a3e26469e026583be4e0bf047447226327ba981569c83cc982d
7
- data.tar.gz: 82738cc1998a31e41c9d7989b77d3e90c33135b4347c6b98d1999156ac696dd3d7739623578fb4569d17776e7e40fb751aafb0e01965ae5df9cbb78d2b41c198
6
+ metadata.gz: 427fefde9d6bbd5af1988825dccaa78b46fa5ed507abde64695bb19b0103a0c2968f1e578232bcf4e98a308809a92c8e05ce60a9380f151de6f6af5828a7791e
7
+ data.tar.gz: a26a4bd4916b368590a813cf4e29d687ed93b1a086a155b349207c8de145aee22c6e03cc17f957cc859d8f7e5e030ece34de3c14aedc59c2b3fb512b10f359a2
data/.rubocop.yml CHANGED
@@ -5,27 +5,5 @@ inherit_from:
5
5
  AllCops:
6
6
  TargetRubyVersion: 2.7
7
7
 
8
- Style/PercentLiteralDelimiters:
9
- Enabled: false
10
-
11
- Layout/CaseIndentation:
12
- Enabled: false
13
-
14
- Style/StringConcatenation:
15
- Enabled: false
16
-
17
- Style/CaseEquality:
18
- Enabled: false
19
-
20
- Security/Eval:
21
- Enabled: false
22
-
23
- Style/MethodCallWithoutArgsParentheses:
24
- Enabled: false
25
-
26
- Style/MixinUsage:
27
- Enabled: false
28
-
29
- Style/AccessModifierDeclarations:
30
- Enabled: true
31
- EnforcedStyle: inline
8
+ Style/ExplicitBlockArgument:
9
+ Enabled: false
data/Gemfile CHANGED
@@ -10,6 +10,7 @@ gem "sus"
10
10
  gem "zeitwerk"
11
11
  gem "benchmark-ips"
12
12
  gem "erb"
13
+ gem "ruby-lsp"
13
14
 
14
15
  group :test do
15
16
  gem "i18n"
data/README.md CHANGED
@@ -43,3 +43,4 @@ Thanks [Logology](https://www.logology.co) for sponsoring our logo.
43
43
  - [hoshi](https://github.com/pete/hoshi)
44
44
  - [hyperstack](https://github.com/hyperstack-org/hyperstack)
45
45
  - [clearwater](https://github.com/clearwater-rb/clearwater)
46
+ - [paggio](https://github.com/opal/paggio)
@@ -13,4 +13,10 @@ module ViewHelper
13
13
  Class.new(Phlex::HTML, &block)
14
14
  end
15
15
  end
16
+
17
+ def svg_view(&block)
18
+ let :view do
19
+ Class.new(Phlex::SVG, &block)
20
+ end
21
+ end
16
22
  end
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Phlex
4
- module BlackHole
5
- extend self
3
+ module Phlex::BlackHole
4
+ extend self
6
5
 
7
- def <<(anything)
8
- self
9
- end
6
+ def <<(anything)
7
+ self
8
+ end
10
9
 
11
- def length
12
- 0
13
- end
10
+ def length
11
+ 0
14
12
  end
15
13
  end
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Phlex
4
- module Callable
5
- def to_proc
6
- method(:call).to_proc
7
- end
3
+ module Phlex::Callable
4
+ def to_proc
5
+ method(:call).to_proc
8
6
  end
9
7
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Phlex
4
- module DeferredRender
5
- # This module doesn't do anything. Phlex::HTML#call checks for its inclusion in the ancestry instead.
6
- end
3
+ module Phlex::DeferredRender
4
+ # This module doesn't do anything. Phlex::HTML#call checks for its inclusion in the ancestry instead.
7
5
  end
@@ -1,61 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0")
4
- using Overrides::Symbol::Name
4
+ using Phlex::Overrides::Symbol::Name
5
5
  end
6
6
 
7
- module Phlex
8
- module Elements
9
- def register_element(element, tag: element.name.tr("_", "-"))
10
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
11
- # frozen_string_literal: true
12
-
13
- def #{element}(**attributes, &block)
14
- if attributes.length > 0
15
- if block_given?
16
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << ">"
17
- yield_content(&block)
18
- @_target << "</#{tag}>"
19
- else
20
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << "></#{tag}>"
21
- end
22
- else
23
- if block_given?
24
- @_target << "<#{tag}>"
25
- yield_content(&block)
26
- @_target << "</#{tag}>"
27
- else
28
- @_target << "<#{tag}></#{tag}>"
29
- end
30
- end
31
-
32
- nil
33
- end
34
-
35
- alias_method :_#{element}, :#{element}
36
- RUBY
37
-
38
- Phlex::HTML::STANDARD_ELEMENTS[element] = tag
39
- end
40
-
41
- def register_void_element(element, tag: element.name.tr("_", "-"))
42
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
43
- # frozen_string_literal: true
44
-
45
- def #{element}(**attributes)
46
- if attributes.length > 0
47
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << ">"
48
- else
49
- @_target << "<#{tag}>"
50
- end
51
-
52
- nil
53
- end
54
-
55
- alias_method :_#{element}, :#{element}
56
- RUBY
57
-
58
- Phlex::HTML::VOID_ELEMENTS[element] = tag
59
- end
7
+ module Phlex::Elements
8
+ def register_element(element, tag: element.name.tr("_", "-"))
9
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
10
+ # frozen_string_literal: true
11
+
12
+ def #{element}(**attributes, &block)
13
+ if attributes.length > 0
14
+ if block_given?
15
+ @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << ">"
16
+ yield_content(&block)
17
+ @_target << "</#{tag}>"
18
+ else
19
+ @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << "></#{tag}>"
20
+ end
21
+ else
22
+ if block_given?
23
+ @_target << "<#{tag}>"
24
+ yield_content(&block)
25
+ @_target << "</#{tag}>"
26
+ else
27
+ @_target << "<#{tag}></#{tag}>"
28
+ end
29
+ end
30
+
31
+ nil
32
+ end
33
+
34
+ alias_method :_#{element}, :#{element}
35
+ RUBY
36
+
37
+ self::REGISTERED_ELEMENTS[element] = tag
38
+
39
+ element
40
+ end
41
+
42
+ def register_void_element(element, tag: element.name.tr("_", "-"))
43
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
44
+ # frozen_string_literal: true
45
+
46
+ def #{element}(**attributes)
47
+ if attributes.length > 0
48
+ @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << ">"
49
+ else
50
+ @_target << "<#{tag}>"
51
+ end
52
+
53
+ nil
54
+ end
55
+
56
+ alias_method :_#{element}, :#{element}
57
+ RUBY
58
+
59
+ self::REGISTERED_ELEMENTS[element] = tag
60
+
61
+ element
60
62
  end
61
63
  end
data/lib/phlex/helpers.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0")
4
- using Overrides::Symbol::Name
4
+ using Phlex::Overrides::Symbol::Name
5
5
  end
6
6
 
7
7
  module Phlex::Helpers
@@ -25,7 +25,10 @@ module Phlex::Helpers
25
25
  end
26
26
  end
27
27
 
28
- tokens.join(" ")
28
+ tokens = tokens.select(&:itself).join(" ")
29
+ tokens.strip!
30
+ tokens.gsub!(/\s+/, " ")
31
+ tokens
29
32
  end
30
33
 
31
34
  private def __append_token__(tokens, token)
@@ -3,6 +3,8 @@
3
3
  module Phlex::HTML::StandardElements
4
4
  extend Phlex::Elements
5
5
 
6
+ REGISTERED_ELEMENTS = Concurrent::Map.new
7
+
6
8
  # @!method a(**attributes, &content)
7
9
  # Outputs an <code>a</code> tag
8
10
  # @return [nil]
@@ -189,13 +191,6 @@ module Phlex::HTML::StandardElements
189
191
  # @see https://developer.mozilla.org/docs/Web/HTML/Element/form
190
192
  register_element :form, tag: "form"
191
193
 
192
- # @!method g(**attributes, &content)
193
- # Outputs a <code>g</code> tag
194
- # @return [nil]
195
- # @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
196
- # @todo move this to SVG
197
- register_element :g, tag: "g"
198
-
199
194
  # @!method h1(**attributes, &content)
200
195
  # Outputs an <code>h1</code> tag
201
196
  # @return [nil]
@@ -377,13 +372,6 @@ module Phlex::HTML::StandardElements
377
372
  # @see https://developer.mozilla.org/docs/Web/HTML/Element/p
378
373
  register_element :p, tag: "p"
379
374
 
380
- # @!method path(**attributes, &content)
381
- # Outputs a <code>path</code> tag
382
- # @return [nil]
383
- # @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
384
- # @todo move this to SVG
385
- register_element :path, tag: "path"
386
-
387
375
  # @!method picture(**attributes, &content)
388
376
  # Outputs a <code>picture</code> tag
389
377
  # @return [nil]
@@ -3,6 +3,8 @@
3
3
  module Phlex::HTML::VoidElements
4
4
  extend Phlex::Elements
5
5
 
6
+ REGISTERED_ELEMENTS = Concurrent::Map.new
7
+
6
8
  # @!method area(**attributes, &content)
7
9
  # Outputs an <code>area</code> tag
8
10
  # @return [nil]
data/lib/phlex/html.rb CHANGED
@@ -1,47 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0")
4
- using Overrides::Symbol::Name
5
- end
6
-
7
3
  module Phlex
8
- class HTML
9
- DOCTYPE = "<!DOCTYPE html>"
10
-
11
- STANDARD_ELEMENTS = Concurrent::Map.new
12
- VOID_ELEMENTS = Concurrent::Map.new
13
-
4
+ class HTML < SGML
5
+ # A list of HTML attributes that have the potential to execute unsafe JavaScript.
14
6
  EVENT_ATTRIBUTES = %w[onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick oncontextmenu oncopy oncuechange oncut ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus onhashchange oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmessage onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onoffline ononline onpagehide onpageshow onpaste onpause onplay onplaying onpopstate onprogress onratechange onreset onresize onscroll onsearch onseeked onseeking onselect onstalled onstorage onsubmit onsuspend ontimeupdate ontoggle onunload onvolumechange onwaiting onwheel].to_h { [_1, true] }.freeze
15
7
 
16
8
  UNBUFFERED_MUTEX = Mutex.new
17
9
 
18
- extend Elements
19
-
20
- include Helpers
21
- include VoidElements
22
- include StandardElements
23
-
24
10
  class << self
25
- def call(...)
26
- new(...).call
27
- end
28
- alias_method :render, :call
29
-
30
- def new(*args, **kwargs, &block)
31
- if block
32
- object = super(*args, **kwargs, &nil)
33
- object.instance_variable_set(:@_content_block, block)
34
- object
35
- else
36
- super
37
- end
38
- end
39
-
40
- def rendered_at_least_once!
41
- alias_method :__attributes__, :__final_attributes__
42
- alias_method :call, :__final_call__
43
- end
44
-
11
+ # @api private
45
12
  def __unbuffered_class__
46
13
  UNBUFFERED_MUTEX.synchronize do
47
14
  if defined? @unbuffered_class
@@ -53,268 +20,28 @@ module Phlex
53
20
  end
54
21
  end
55
22
 
56
- def call(...)
57
- __final_call__(...).tap do
58
- self.class.rendered_at_least_once!
59
- end
60
- end
61
-
62
- def __final_call__(buffer = +"", view_context: nil, parent: nil, &block)
63
- @_target = buffer
64
- @_view_context = view_context
65
- @_parent = parent
66
-
67
- block ||= @_content_block
68
-
69
- return buffer unless render?
70
-
71
- around_template do
72
- if block
73
- if DeferredRender === self
74
- __vanish__(self, &block)
75
- template
76
- else
77
- template do |*args|
78
- if args.length > 0
79
- yield_content_with_args(*args, &block)
80
- else
81
- yield_content(&block)
82
- end
83
- end
84
- end
85
- else
86
- template
87
- end
88
- end
89
-
90
- buffer
91
- end
92
-
93
- def render(renderable, &block)
94
- case renderable
95
- when Phlex::HTML
96
- renderable.call(@_target, view_context: @_view_context, parent: self, &block)
97
- when Class
98
- if renderable < Phlex::HTML
99
- renderable.new.call(@_target, view_context: @_view_context, parent: self, &block)
100
- end
101
- else
102
- raise ArgumentError, "You can't render a #{renderable}."
103
- end
23
+ extend Elements
24
+ include Helpers, VoidElements, StandardElements
104
25
 
26
+ # Output an HTML doctype.
27
+ def doctype
28
+ @_target << "<!DOCTYPE html>"
105
29
  nil
106
30
  end
107
31
 
108
- def format
109
- :html
110
- end
111
-
112
- def text(content)
113
- @_target << ERB::Util.html_escape(
114
- case content
115
- when String then content
116
- when Symbol then content.name
117
- when Integer then content.to_s
118
- else format_object(content) || content.to_s
32
+ def svg(...)
33
+ super do
34
+ render Phlex::SVG.new do |svg|
35
+ yield(svg)
119
36
  end
120
- )
121
-
122
- nil
123
- end
124
-
125
- def whitespace
126
- @_target << " "
127
-
128
- if block_given?
129
- yield
130
- @_target << " "
131
37
  end
132
-
133
- nil
134
- end
135
-
136
- def comment(&block)
137
- @_target << "<!-- "
138
- yield_content(&block)
139
- @_target << " -->"
140
-
141
- nil
142
- end
143
-
144
- def doctype
145
- @_target << DOCTYPE
146
- nil
147
- end
148
-
149
- def unsafe_raw(content = nil)
150
- return nil unless content
151
-
152
- @_target << content
153
- end
154
-
155
- def capture(&block)
156
- return unless block_given?
157
-
158
- original_buffer = @_target
159
- new_buffer = +""
160
- @_target = new_buffer
161
-
162
- yield_content(&block)
163
-
164
- new_buffer
165
- ensure
166
- @_target = original_buffer
167
38
  end
168
39
 
40
+ # @api private
169
41
  def unbuffered
170
42
  self.class.__unbuffered_class__.new(self)
171
43
  end
172
44
 
173
- # Like `capture` but the output is vanished into a BlackHole buffer.
174
- # Becuase the BlackHole does nothing with the output, this should be faster.
175
- private def __vanish__(*args)
176
- return unless block_given?
177
-
178
- original_buffer = @_target
179
- @_target = BlackHole
180
-
181
- yield(*args)
182
- nil
183
- ensure
184
- @_target = original_buffer
185
- end
186
-
187
- # Default render predicate can be overridden to prevent rendering
188
- private def render?
189
- true
190
- end
191
-
192
- private def format_object(object)
193
- case object
194
- when Float
195
- object.to_s
196
- end
197
- end
198
-
199
- private def around_template
200
- before_template
201
- yield
202
- after_template
203
- end
204
-
205
- private def before_template
206
- nil
207
- end
208
-
209
- private def after_template
210
- nil
211
- end
212
-
213
- private def yield_content
214
- return unless block_given?
215
-
216
- original_length = @_target.length
217
- content = yield(self)
218
- unchanged = (original_length == @_target.length)
219
-
220
- if unchanged
221
- case content
222
- when String
223
- @_target << ERB::Util.html_escape(content)
224
- when Symbol
225
- @_target << ERB::Util.html_escape(content.name)
226
- when Integer
227
- @_target << ERB::Util.html_escape(content.to_s)
228
- else
229
- if (formatted_object = format_object(content))
230
- @_target << ERB::Util.html_escape(formatted_object)
231
- end
232
- end
233
- end
234
-
235
- nil
236
- end
237
-
238
- private def yield_content_with_args(*args)
239
- return unless block_given?
240
-
241
- original_length = @_target.length
242
- content = yield(*args)
243
- unchanged = (original_length == @_target.length)
244
-
245
- if unchanged
246
- case content
247
- when String
248
- @_target << ERB::Util.html_escape(content)
249
- when Symbol
250
- @_target << ERB::Util.html_escape(content.name)
251
- when Integer, Float
252
- @_target << ERB::Util.html_escape(content.to_s)
253
- else
254
- if (formatted_object = format_object(content))
255
- @_target << ERB::Util.html_escape(formatted_object)
256
- end
257
- end
258
- end
259
-
260
- nil
261
- end
262
-
263
- private def __attributes__(**attributes)
264
- __final_attributes__(**attributes).tap do |buffer|
265
- Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
266
- end
267
- end
268
-
269
- private def __final_attributes__(**attributes)
270
- buffer = +""
271
- __build_attributes__(attributes, buffer: buffer)
272
-
273
- buffer
274
- end
275
-
276
- private def __build_attributes__(attributes, buffer:)
277
- attributes.each do |k, v|
278
- next unless v
279
-
280
- name = case k
281
- when String then k
282
- when Symbol then k.name.tr("_", "-")
283
- else k.to_s
284
- end
285
-
286
- lower_name = name.downcase
287
- next if lower_name == "href" && v.start_with?(/\s*javascript:/i)
288
-
289
- # Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters.
290
- if HTML::EVENT_ATTRIBUTES[lower_name] || name.match?(/[<>&"']/)
291
- raise ArgumentError, "Unsafe attribute name detected: #{k}."
292
- end
293
-
294
- case v
295
- when true
296
- buffer << " " << name
297
- when String
298
- buffer << " " << name << '="' << ERB::Util.html_escape(v) << '"'
299
- when Symbol
300
- buffer << " " << name << '="' << ERB::Util.html_escape(v.name) << '"'
301
- when Hash
302
- __build_attributes__(
303
- v.transform_keys { |subkey|
304
- case subkey
305
- when Symbol then"#{k}-#{subkey.name.tr('_', '-')}"
306
- else "#{k}-#{subkey}"
307
- end
308
- }, buffer: buffer
309
- )
310
- else
311
- buffer << " " << name << '="' << ERB::Util.html_escape(v.to_s) << '"'
312
- end
313
- end
314
-
315
- buffer
316
- end
317
-
318
45
  # This should be the last method defined
319
46
  def self.method_added(method_name)
320
47
  if method_name[0] == "_" && Phlex::HTML.instance_methods.include?(method_name) && instance_method(method_name).owner != Phlex::HTML
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Overrides::Symbol::Name
3
+ module Phlex::Overrides::Symbol::Name
4
4
  refine(Symbol) { alias_method :name, :to_s }
5
5
  end