phlex 2.2.2 → 2.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ad9e30b1fc9db64488ae58d78b09bb2c8cd57db032da7e28dff9e9c2c035677
4
- data.tar.gz: 343cf214ef539fac85e35d63bdfa39e104a7902d8765a7c10f6fc26d3d5704f4
3
+ metadata.gz: 43c33e9802643a8519ff45f208bccd4d8f6430cd642187e75ca2c7a8dbba7ea2
4
+ data.tar.gz: 8963d6c803fe8b20fc7143a5ff3c447f99a420b0ceb8556e697e844b2782a9f5
5
5
  SHA512:
6
- metadata.gz: 9d554ae1c210699c059d848ebe22d1f06939c93123f160be75b51eebd90271fefdd813093676fb570a8e23c9d6d58932be38a57b330f710f292f34cbd6452ef1
7
- data.tar.gz: 5d5794df169f3a98c7c1de9cde0c75e2fb268c5eeaf9c6e74f731665cb52823dc972ef288589acefcdbae7907f7f1a57a0f2b59a6e933a5ac2c6b6d5dcd54e2f
6
+ metadata.gz: cfd387e534c132acb663c28b3e5e1c28cba54251d1fc3ed1c1ceb649cd41825b243e6eb54eeb32f91934fc3493f8d473cd73f1d036995a97a09f2211b6b0ce61
7
+ data.tar.gz: 0b5f88d2d1f291719e61494a9027dd47e20a33eabe8987f01e4492ec0d16f01b1029177b5c8116402bf00f02f3d14f81f9604289ced1cd216cd05373f7db7220
data/lib/phlex/csv.rb CHANGED
@@ -139,32 +139,30 @@ class Phlex::CSV
139
139
  ","
140
140
  end
141
141
 
142
- private
143
-
144
- def column(header = nil, value)
142
+ private def column(header = nil, value)
145
143
  @_row_buffer << [header, value]
146
144
  end
147
145
 
148
- def each_item(&)
146
+ private def each_item(&)
149
147
  collection.each(&)
150
148
  end
151
149
 
152
150
  # Override and set to `false` to disable rendering headers.
153
- def render_headers?
151
+ private def render_headers?
154
152
  true
155
153
  end
156
154
 
157
155
  # Override and set to `true` to strip leading and trailing whitespace from values.
158
- def trim_whitespace?
156
+ private def trim_whitespace?
159
157
  false
160
158
  end
161
159
 
162
160
  # Override and set to `false` to disable CSV injection escapes or `true` to enable.
163
- def escape_csv_injection?
161
+ private def escape_csv_injection?
164
162
  UNDEFINED
165
163
  end
166
164
 
167
- def __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
165
+ private def __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
168
166
  value = case value
169
167
  when String
170
168
  value
@@ -218,12 +216,12 @@ class Phlex::CSV
218
216
  end
219
217
 
220
218
  # Handle legacy `view_template` method
221
- def respond_to_missing?(method_name, include_private)
219
+ private def respond_to_missing?(method_name, include_private)
222
220
  (method_name == :row_template && respond_to?(:view_template)) || super
223
221
  end
224
222
 
225
223
  # Handle legacy `view_template` method
226
- def method_missing(method_name, ...)
224
+ private def method_missing(method_name, ...)
227
225
  if method_name == :row_template && respond_to?(:view_template)
228
226
  warn "Deprecated: Use `row_template` instead of `view_template` in Phlex CSVs."
229
227
  self.class.alias_method :row_template, :view_template
@@ -233,7 +231,7 @@ class Phlex::CSV
233
231
  end
234
232
  end
235
233
 
236
- def ensure_escape_csv_injection_configured!
234
+ private def ensure_escape_csv_injection_configured!
237
235
  if escape_csv_injection? == UNDEFINED
238
236
  raise <<~MESSAGE
239
237
  You need to define `escape_csv_injection?` in #{self.class.name}.
@@ -28,9 +28,7 @@ class Phlex::FIFOCacheStore
28
28
  @fifo.clear
29
29
  end
30
30
 
31
- private
32
-
33
- def map_key(value)
31
+ private def map_key(value)
34
32
  case value
35
33
  when Array
36
34
  value.map { |it| map_key(it) }
data/lib/phlex/helpers.rb CHANGED
@@ -2,9 +2,7 @@
2
2
 
3
3
  # @api private
4
4
  module Phlex::Helpers
5
- private
6
-
7
- def mix(*args)
5
+ private def mix(*args)
8
6
  args.each_with_object({}) do |object, result|
9
7
  result.merge!(object) do |_key, old, new|
10
8
  case [old, new].freeze
@@ -43,7 +41,7 @@ module Phlex::Helpers
43
41
  end
44
42
  end
45
43
 
46
- def grab(**bindings)
44
+ private def grab(**bindings)
47
45
  if bindings.size > 1
48
46
  bindings.values
49
47
  else
data/lib/phlex/html.rb CHANGED
@@ -55,7 +55,7 @@ class Phlex::HTML < Phlex::SGML
55
55
  raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
56
56
  end
57
57
 
58
- if (tag = StandardElements.__registered_elements__[name]) || ((tag = name.name.tr("_", "-")).include?("-") && tag.match?(/\A[a-z0-9-]+\z/))
58
+ if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
59
59
  if attributes.length > 0 # with attributes
60
60
  if block_given # with content block
61
61
  buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
@@ -1,6 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex::SGML::Elements
4
+ COMMA_SEPARATED_TOKENS = {
5
+ img: <<~RUBY,
6
+ if Array === (srcset_attribute = attributes[:srcset])
7
+ attributes[:srcset] = __nested_tokens__(srcset_attribute, ", ")
8
+ end
9
+ RUBY
10
+ link: <<~RUBY,
11
+ if Array === (media_attribute = attributes[:media])
12
+ attributes[:media] = __nested_tokens__(media_attribute, ", ")
13
+ end
14
+
15
+ if Array === (sizes_attribute = attributes[:sizes])
16
+ attributes[:sizes] = __nested_tokens__(sizes_attribute, ", ")
17
+ end
18
+
19
+ if Array === (imagesrcset_attribute = attributes[:imagesrcset])
20
+ rel_attribute = attributes[:rel] || attributes["rel"]
21
+ as_attribute = attributes[:as] || attributes["as"]
22
+
23
+ if ("preload" == rel_attribute || :preload == rel_attribute) && ("image" == as_attribute || :image == as_attribute)
24
+ attributes[:imagesrcset] = __nested_tokens__(imagesrcset_attribute, ", ")
25
+ end
26
+ end
27
+ RUBY
28
+ input: <<~RUBY,
29
+ if Array === (accept_attribute = attributes[:accept])
30
+ type_attribute = attributes[:type] || attributes["type"]
31
+
32
+ if "file" == type_attribute || :file == type_attribute
33
+ attributes[:accept] = __nested_tokens__(accept_attribute, ", ")
34
+ end
35
+ end
36
+ RUBY
37
+ }.freeze
38
+
4
39
  def __registered_elements__
5
40
  @__registered_elements__ ||= {}
6
41
  end
@@ -23,6 +58,7 @@ module Phlex::SGML::Elements
23
58
  if block_given # with content block
24
59
  buffer << "<#{tag}"
25
60
  begin
61
+ #{COMMA_SEPARATED_TOKENS[method_name]}
26
62
  buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
27
63
  ensure
28
64
  buffer << ">"
@@ -53,6 +89,7 @@ module Phlex::SGML::Elements
53
89
  else # without content
54
90
  buffer << "<#{tag}"
55
91
  begin
92
+ #{COMMA_SEPARATED_TOKENS[method_name]}
56
93
  buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
57
94
  ensure
58
95
  buffer << "></#{tag}>"
@@ -114,6 +151,7 @@ module Phlex::SGML::Elements
114
151
  if attributes.length > 0 # with attributes
115
152
  buffer << "<#{tag}"
116
153
  begin
154
+ #{COMMA_SEPARATED_TOKENS[method_name]}
117
155
  buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
118
156
  ensure
119
157
  buffer << ">"
data/lib/phlex/sgml.rb CHANGED
@@ -3,13 +3,7 @@
3
3
  # **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
4
4
  class Phlex::SGML
5
5
  UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
6
- REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping xlinkhref]).freeze
7
- NAMED_CHARACTER_REFERENCES = {
8
- "colon" => ":",
9
- "tab" => "\t",
10
- "newline" => "\n",
11
- }.freeze
12
- UNSAFE_ATTRIBUTE_NAME_CHARS = %r([<>&"'/=\s\x00])
6
+ REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
13
7
 
14
8
  ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
15
9
  compiler.pre_cmd = [""]
@@ -31,7 +25,7 @@ class Phlex::SGML
31
25
  end
32
26
 
33
27
  # Create a new instance of the component.
34
- # @note The block will not be delegated {#initialize}. Instead, it will be sent to {#template} when rendering.
28
+ # @note The block will not be delegated to {#initialize}. Instead, it will be sent to {#view_template} when rendering.
35
29
  def new(*a, **k, &block)
36
30
  if block
37
31
  object = super(*a, **k, &nil)
@@ -290,12 +284,12 @@ class Phlex::SGML
290
284
  location = caller_locations(1, 1)[0]
291
285
 
292
286
  full_key = [
293
- app_version_key, # invalidates the key when deploying new code in case of changes
287
+ app_version_key, # invalidates the key when deploying new code in case of changes
294
288
  self.class.name, # prevents collisions between classes
295
289
  (self.class.object_id if enable_cache_reloading?), # enables reloading
296
290
  location.base_label, # prevents collisions between different methods
297
291
  location.lineno, # prevents collisions between different lines
298
- cache_key, # allows for custom cache keys
292
+ cache_key, # allows for custom cache keys
299
293
  ].freeze
300
294
 
301
295
  low_level_cache(full_key, **, &content)
@@ -342,52 +336,50 @@ class Phlex::SGML
342
336
  ERB::Util.json_escape(string)
343
337
  end
344
338
 
345
- private
346
-
347
339
  # Override this method to use a different deployment key.
348
- def app_version_key
340
+ private def app_version_key
349
341
  Phlex::DEPLOYED_AT
350
342
  end
351
343
 
352
344
  # Override this method to use a different cache store.
353
- def cache_store
345
+ private def cache_store
354
346
  raise "Cache store not implemented."
355
347
  end
356
348
 
357
- def enable_cache_reloading?
349
+ private def enable_cache_reloading?
358
350
  false
359
351
  end
360
352
 
361
- def vanish(...)
353
+ private def vanish(...)
362
354
  capture(...)
363
355
  nil
364
356
  end
365
357
 
366
- def render?
358
+ private def render?
367
359
  true
368
360
  end
369
361
 
370
- def format_object(object)
362
+ private def format_object(object)
371
363
  case object
372
364
  when Float, Integer
373
365
  object.to_s
374
366
  end
375
367
  end
376
368
 
377
- def around_template
369
+ private def around_template
378
370
  yield
379
371
  nil
380
372
  end
381
373
 
382
- def before_template
374
+ private def before_template
383
375
  nil
384
376
  end
385
377
 
386
- def after_template
378
+ private def after_template
387
379
  nil
388
380
  end
389
381
 
390
- def __yield_content__
382
+ private def __yield_content__
391
383
  return unless block_given?
392
384
 
393
385
  buffer = @_state.buffer
@@ -399,7 +391,7 @@ class Phlex::SGML
399
391
  nil
400
392
  end
401
393
 
402
- def __yield_content_with_no_yield_args__
394
+ private def __yield_content_with_no_yield_args__
403
395
  return unless block_given?
404
396
 
405
397
  buffer = @_state.buffer
@@ -411,7 +403,7 @@ class Phlex::SGML
411
403
  nil
412
404
  end
413
405
 
414
- def __yield_content_with_args__(*a)
406
+ private def __yield_content_with_args__(*a)
415
407
  return unless block_given?
416
408
 
417
409
  buffer = @_state.buffer
@@ -423,7 +415,7 @@ class Phlex::SGML
423
415
  nil
424
416
  end
425
417
 
426
- def __implicit_output__(content)
418
+ private def __implicit_output__(content)
427
419
  state = @_state
428
420
  return true unless state.should_render?
429
421
 
@@ -448,7 +440,7 @@ class Phlex::SGML
448
440
  end
449
441
 
450
442
  # same as __implicit_output__ but escapes even `safe` objects
451
- def __text__(content)
443
+ private def __text__(content)
452
444
  state = @_state
453
445
  return true unless state.should_render?
454
446
 
@@ -470,7 +462,7 @@ class Phlex::SGML
470
462
  true
471
463
  end
472
464
 
473
- def __attributes__(attributes, buffer = +"")
465
+ private def __attributes__(attributes, buffer = +"")
474
466
  attributes.each do |k, v|
475
467
  next unless v
476
468
 
@@ -489,6 +481,10 @@ class Phlex::SGML
489
481
  v.name.tr("_", "-").gsub('"', "&quot;")
490
482
  when Integer, Float
491
483
  v.to_s
484
+ when Date
485
+ v.iso8601
486
+ when Time
487
+ v.respond_to?(:iso8601) ? v.iso8601 : v.strftime("%Y-%m-%dT%H:%M:%S%:z")
492
488
  when Hash
493
489
  case k
494
490
  when :style
@@ -524,9 +520,7 @@ class Phlex::SGML
524
520
  if value != true && REF_ATTRIBUTES.include?(normalized_name)
525
521
  case value
526
522
  when String
527
- decoded_value = decode_html_character_references(value)
528
-
529
- if decoded_value.downcase.delete("^a-z:").start_with?("javascript:")
523
+ if value.downcase.delete("^a-z:").start_with?("javascript:")
530
524
  # We just ignore these because they were likely not specified by the developer.
531
525
  next
532
526
  end
@@ -544,7 +538,7 @@ class Phlex::SGML
544
538
  end
545
539
  end
546
540
 
547
- if name.match?(UNSAFE_ATTRIBUTE_NAME_CHARS)
541
+ if name.match?(/[<>&"']/)
548
542
  raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
549
543
  end
550
544
 
@@ -565,7 +559,7 @@ class Phlex::SGML
565
559
 
566
560
  # Provides the nested-attributes case for serializing out attributes.
567
561
  # This allows us to skip many of the checks the `__attributes__` method must perform.
568
- def __nested_attributes__(attributes, base_name, buffer = +"")
562
+ private def __nested_attributes__(attributes, base_name, buffer = +"")
569
563
  attributes.each do |k, v|
570
564
  next unless v
571
565
 
@@ -580,7 +574,7 @@ class Phlex::SGML
580
574
  else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
581
575
  end
582
576
 
583
- if name.match?(UNSAFE_ATTRIBUTE_NAME_CHARS)
577
+ if name.match?(/[<>&"']/)
584
578
  raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
585
579
  end
586
580
  end
@@ -614,7 +608,7 @@ class Phlex::SGML
614
608
  end
615
609
  end
616
610
 
617
- def __nested_tokens__(tokens)
611
+ private def __nested_tokens__(tokens, sep = " ")
618
612
  buffer = +""
619
613
 
620
614
  i, length = 0, tokens.length
@@ -625,28 +619,28 @@ class Phlex::SGML
625
619
  case token
626
620
  when String
627
621
  if i > 0
628
- buffer << " " << token
622
+ buffer << sep << token
629
623
  else
630
624
  buffer << token
631
625
  end
632
626
  when Symbol
633
627
  if i > 0
634
- buffer << " " << token.name.tr("_", "-")
628
+ buffer << sep << token.name.tr("_", "-")
635
629
  else
636
630
  buffer << token.name.tr("_", "-")
637
631
  end
638
632
  when Integer, Float, Phlex::SGML::SafeObject
639
633
  if i > 0
640
- buffer << " " << token.to_s
634
+ buffer << sep << token.to_s
641
635
  else
642
636
  buffer << token.to_s
643
637
  end
644
638
  when Array
645
639
  if token.length > 0
646
640
  if i > 0
647
- buffer << " " << __nested_tokens__(token)
641
+ buffer << sep << __nested_tokens__(token, sep)
648
642
  else
649
- buffer << __nested_tokens__(token)
643
+ buffer << __nested_tokens__(token, sep)
650
644
  end
651
645
  end
652
646
  when nil
@@ -661,29 +655,8 @@ class Phlex::SGML
661
655
  buffer.gsub('"', "&quot;")
662
656
  end
663
657
 
664
- private def decode_html_character_references(value)
665
- value
666
- .gsub(/&#x([0-9a-f]+);?/i) {
667
- begin
668
- [$1.to_i(16)].pack("U*")
669
- rescue
670
- ""
671
- end
672
- }
673
- .gsub(/&#(\d+);?/) {
674
- begin
675
- [$1.to_i].pack("U*")
676
- rescue
677
- ""
678
- end
679
- }
680
- .gsub(/&([a-z][a-z0-9]+);?/i) {
681
- NAMED_CHARACTER_REFERENCES[$1.downcase] || ""
682
- }
683
- end
684
-
685
658
  # Result is **unsafe**, so it should be escaped!
686
- def __styles__(styles)
659
+ private def __styles__(styles)
687
660
  case styles
688
661
  when Array, Set
689
662
  styles.filter_map do |s|
data/lib/phlex/svg.rb CHANGED
@@ -41,7 +41,7 @@ class Phlex::SVG < Phlex::SGML
41
41
  raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
42
42
  end
43
43
 
44
- if (tag = StandardElements.__registered_elements__[name]) || ((tag = name.name.tr("_", "-")).include?("-") && tag.match?(/\A[a-z0-9-]+\z/))
44
+ if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
45
45
  if attributes.length > 0 # with attributes
46
46
  if block_given # with content block
47
47
  buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
data/lib/phlex/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex
4
- VERSION = "2.2.2"
4
+ VERSION = "2.3.0"
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: 2.2.2
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  - Will Cosgrove
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-06 00:00:00.000000000 Z
11
+ date: 2025-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk