phlex 2.3.2 → 2.4.0.beta1
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 +4 -4
- data/lib/phlex/compiler/class_compiler.rb +35 -0
- data/lib/phlex/compiler/compilation.rb +45 -0
- data/lib/phlex/compiler/file_compiler.rb +46 -0
- data/lib/phlex/compiler/method_compiler.rb +591 -0
- data/lib/phlex/compiler.rb +18 -0
- data/lib/phlex/html.rb +4 -4
- data/lib/phlex/sgml/attributes.rb +280 -0
- data/lib/phlex/sgml/elements.rb +8 -8
- data/lib/phlex/sgml.rb +11 -289
- data/lib/phlex/svg.rb +3 -3
- data/lib/phlex/version.rb +1 -1
- metadata +22 -2
data/lib/phlex/sgml.rb
CHANGED
|
@@ -2,15 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
# **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
|
|
4
4
|
class Phlex::SGML
|
|
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])
|
|
13
|
-
|
|
14
5
|
ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
|
|
15
6
|
compiler.pre_cmd = [""]
|
|
16
7
|
compiler.put_cmd = "@_state.buffer.<<"
|
|
@@ -468,284 +459,10 @@ class Phlex::SGML
|
|
|
468
459
|
true
|
|
469
460
|
end
|
|
470
461
|
|
|
471
|
-
private def
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
name = case k
|
|
476
|
-
when String then k
|
|
477
|
-
when Symbol then k.name.tr("_", "-")
|
|
478
|
-
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.")
|
|
479
|
-
end
|
|
480
|
-
|
|
481
|
-
value = case v
|
|
482
|
-
when true
|
|
483
|
-
true
|
|
484
|
-
when String
|
|
485
|
-
v.gsub('"', """)
|
|
486
|
-
when Symbol
|
|
487
|
-
v.name.tr("_", "-").gsub('"', """)
|
|
488
|
-
when Integer, Float
|
|
489
|
-
v.to_s
|
|
490
|
-
when Date
|
|
491
|
-
v.iso8601
|
|
492
|
-
when Time
|
|
493
|
-
v.respond_to?(:iso8601) ? v.iso8601 : v.strftime("%Y-%m-%dT%H:%M:%S%:z")
|
|
494
|
-
when Hash
|
|
495
|
-
case k
|
|
496
|
-
when :style
|
|
497
|
-
__styles__(v).gsub('"', """)
|
|
498
|
-
else
|
|
499
|
-
__nested_attributes__(v, "#{name}-", buffer)
|
|
500
|
-
end
|
|
501
|
-
when Array
|
|
502
|
-
case k
|
|
503
|
-
when :style
|
|
504
|
-
__styles__(v).gsub('"', """)
|
|
505
|
-
else
|
|
506
|
-
__nested_tokens__(v)
|
|
507
|
-
end
|
|
508
|
-
when Set
|
|
509
|
-
case k
|
|
510
|
-
when :style
|
|
511
|
-
__styles__(v).gsub('"', """)
|
|
512
|
-
else
|
|
513
|
-
__nested_tokens__(v.to_a)
|
|
514
|
-
end
|
|
515
|
-
when Phlex::SGML::SafeObject
|
|
516
|
-
v.to_s.gsub('"', """)
|
|
517
|
-
else
|
|
518
|
-
raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
|
|
519
|
-
end
|
|
520
|
-
|
|
521
|
-
lower_name = name.downcase
|
|
522
|
-
|
|
523
|
-
unless Phlex::SGML::SafeObject === v
|
|
524
|
-
normalized_name = lower_name.delete("^a-z-")
|
|
525
|
-
|
|
526
|
-
if value != true && REF_ATTRIBUTES.include?(normalized_name)
|
|
527
|
-
case value
|
|
528
|
-
when String
|
|
529
|
-
decoded_value = decode_html_character_references(value)
|
|
530
|
-
|
|
531
|
-
if decoded_value.downcase.delete("^a-z:").start_with?("javascript:")
|
|
532
|
-
# We just ignore these because they were likely not specified by the developer.
|
|
533
|
-
next
|
|
534
|
-
end
|
|
535
|
-
else
|
|
536
|
-
raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
|
|
537
|
-
end
|
|
538
|
-
end
|
|
539
|
-
|
|
540
|
-
if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-")
|
|
541
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
if UNSAFE_ATTRIBUTES.include?(normalized_name)
|
|
545
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
546
|
-
end
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
if name.match?(UNSAFE_ATTRIBUTE_NAME_CHARS)
|
|
550
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
551
|
-
end
|
|
552
|
-
|
|
553
|
-
if lower_name.to_sym == :id && k != :id
|
|
554
|
-
raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.")
|
|
555
|
-
end
|
|
556
|
-
|
|
557
|
-
case value
|
|
558
|
-
when true
|
|
559
|
-
buffer << " " << name
|
|
560
|
-
when String
|
|
561
|
-
buffer << " " << name << '="' << value << '"'
|
|
562
|
-
end
|
|
563
|
-
end
|
|
564
|
-
|
|
565
|
-
buffer
|
|
566
|
-
end
|
|
567
|
-
|
|
568
|
-
# Provides the nested-attributes case for serializing out attributes.
|
|
569
|
-
# This allows us to skip many of the checks the `__attributes__` method must perform.
|
|
570
|
-
private def __nested_attributes__(attributes, base_name, buffer = +"")
|
|
571
|
-
attributes.each do |k, v|
|
|
572
|
-
next unless v
|
|
573
|
-
|
|
574
|
-
if (root_key = (:_ == k))
|
|
575
|
-
name = ""
|
|
576
|
-
original_base_name = base_name
|
|
577
|
-
base_name = base_name.delete_suffix("-")
|
|
578
|
-
else
|
|
579
|
-
name = case k
|
|
580
|
-
when String then k
|
|
581
|
-
when Symbol then k.name.tr("_", "-")
|
|
582
|
-
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
|
|
583
|
-
end
|
|
584
|
-
|
|
585
|
-
if name.match?(UNSAFE_ATTRIBUTE_NAME_CHARS)
|
|
586
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
587
|
-
end
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
case v
|
|
591
|
-
when true
|
|
592
|
-
buffer << " " << base_name << name
|
|
593
|
-
when String
|
|
594
|
-
buffer << " " << base_name << name << '="' << v.gsub('"', """) << '"'
|
|
595
|
-
when Symbol
|
|
596
|
-
buffer << " " << base_name << name << '="' << v.name.tr("_", "-").gsub('"', """) << '"'
|
|
597
|
-
when Integer, Float
|
|
598
|
-
buffer << " " << base_name << name << '="' << v.to_s << '"'
|
|
599
|
-
when Hash
|
|
600
|
-
__nested_attributes__(v, "#{base_name}#{name}-", buffer)
|
|
601
|
-
when Array
|
|
602
|
-
buffer << " " << base_name << name << '="' << __nested_tokens__(v) << '"'
|
|
603
|
-
when Set
|
|
604
|
-
buffer << " " << base_name << name << '="' << __nested_tokens__(v.to_a) << '"'
|
|
605
|
-
when Phlex::SGML::SafeObject
|
|
606
|
-
buffer << " " << base_name << name << '="' << v.to_s.gsub('"', """) << '"'
|
|
607
|
-
else
|
|
608
|
-
raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
|
|
609
|
-
end
|
|
610
|
-
|
|
611
|
-
if root_key
|
|
612
|
-
base_name = original_base_name
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
buffer
|
|
616
|
-
end
|
|
617
|
-
end
|
|
618
|
-
|
|
619
|
-
private def __nested_tokens__(tokens, sep = " ")
|
|
620
|
-
buffer = +""
|
|
621
|
-
|
|
622
|
-
i, length = 0, tokens.length
|
|
623
|
-
|
|
624
|
-
while i < length
|
|
625
|
-
token = tokens[i]
|
|
626
|
-
|
|
627
|
-
case token
|
|
628
|
-
when String
|
|
629
|
-
if i > 0
|
|
630
|
-
buffer << sep << token
|
|
631
|
-
else
|
|
632
|
-
buffer << token
|
|
633
|
-
end
|
|
634
|
-
when Symbol
|
|
635
|
-
if i > 0
|
|
636
|
-
buffer << sep << token.name.tr("_", "-")
|
|
637
|
-
else
|
|
638
|
-
buffer << token.name.tr("_", "-")
|
|
639
|
-
end
|
|
640
|
-
when Integer, Float, Phlex::SGML::SafeObject
|
|
641
|
-
if i > 0
|
|
642
|
-
buffer << sep << token.to_s
|
|
643
|
-
else
|
|
644
|
-
buffer << token.to_s
|
|
645
|
-
end
|
|
646
|
-
when Array
|
|
647
|
-
if token.length > 0
|
|
648
|
-
if i > 0
|
|
649
|
-
buffer << sep << __nested_tokens__(token, sep)
|
|
650
|
-
else
|
|
651
|
-
buffer << __nested_tokens__(token, sep)
|
|
652
|
-
end
|
|
653
|
-
end
|
|
654
|
-
when nil
|
|
655
|
-
# Do nothing
|
|
656
|
-
else
|
|
657
|
-
raise Phlex::ArgumentError.new("Invalid token type: #{token.class}.")
|
|
658
|
-
end
|
|
659
|
-
|
|
660
|
-
i += 1
|
|
661
|
-
end
|
|
662
|
-
|
|
663
|
-
buffer.gsub('"', """)
|
|
664
|
-
end
|
|
665
|
-
|
|
666
|
-
private def decode_html_character_references(value)
|
|
667
|
-
value
|
|
668
|
-
.gsub(/&#x([0-9a-f]+);?/i) {
|
|
669
|
-
begin
|
|
670
|
-
[$1.to_i(16)].pack("U*")
|
|
671
|
-
rescue
|
|
672
|
-
""
|
|
673
|
-
end
|
|
674
|
-
}
|
|
675
|
-
.gsub(/&#(\d+);?/) {
|
|
676
|
-
begin
|
|
677
|
-
[$1.to_i].pack("U*")
|
|
678
|
-
rescue
|
|
679
|
-
""
|
|
680
|
-
end
|
|
681
|
-
}
|
|
682
|
-
.gsub(/&([a-z][a-z0-9]+);?/i) {
|
|
683
|
-
NAMED_CHARACTER_REFERENCES[$1.downcase] || ""
|
|
684
|
-
}
|
|
685
|
-
end
|
|
686
|
-
|
|
687
|
-
# Result is **unsafe**, so it should be escaped!
|
|
688
|
-
private def __styles__(styles)
|
|
689
|
-
case styles
|
|
690
|
-
when Array, Set
|
|
691
|
-
styles.filter_map do |s|
|
|
692
|
-
case s
|
|
693
|
-
when String
|
|
694
|
-
if s == "" || s.end_with?(";")
|
|
695
|
-
s
|
|
696
|
-
else
|
|
697
|
-
"#{s};"
|
|
698
|
-
end
|
|
699
|
-
when Phlex::SGML::SafeObject
|
|
700
|
-
value = s.to_s
|
|
701
|
-
value.end_with?(";") ? value : "#{value};"
|
|
702
|
-
when Hash
|
|
703
|
-
next __styles__(s)
|
|
704
|
-
when nil
|
|
705
|
-
next nil
|
|
706
|
-
else
|
|
707
|
-
raise Phlex::ArgumentError.new("Invalid style: #{s.inspect}.")
|
|
708
|
-
end
|
|
709
|
-
end.join(" ")
|
|
710
|
-
when Hash
|
|
711
|
-
buffer = +""
|
|
712
|
-
i = 0
|
|
713
|
-
styles.each do |k, v|
|
|
714
|
-
prop = case k
|
|
715
|
-
when String
|
|
716
|
-
k
|
|
717
|
-
when Symbol
|
|
718
|
-
k.name.tr("_", "-")
|
|
719
|
-
else
|
|
720
|
-
raise Phlex::ArgumentError.new("Style keys should be Strings or Symbols.")
|
|
721
|
-
end
|
|
722
|
-
|
|
723
|
-
value = case v
|
|
724
|
-
when String
|
|
725
|
-
v
|
|
726
|
-
when Symbol
|
|
727
|
-
v.name.tr("_", "-")
|
|
728
|
-
when Integer, Float, Phlex::SGML::SafeObject
|
|
729
|
-
v.to_s
|
|
730
|
-
when nil
|
|
731
|
-
nil
|
|
732
|
-
else
|
|
733
|
-
raise Phlex::ArgumentError.new("Invalid style value: #{v.inspect}")
|
|
734
|
-
end
|
|
735
|
-
|
|
736
|
-
if value
|
|
737
|
-
if i == 0
|
|
738
|
-
buffer << prop << ": " << value << ";"
|
|
739
|
-
else
|
|
740
|
-
buffer << " " << prop << ": " << value << ";"
|
|
741
|
-
end
|
|
742
|
-
end
|
|
743
|
-
|
|
744
|
-
i += 1
|
|
745
|
-
end
|
|
746
|
-
|
|
747
|
-
buffer
|
|
748
|
-
end
|
|
462
|
+
private def __render_attributes__(attributes)
|
|
463
|
+
state = @_state
|
|
464
|
+
return unless state.should_render?
|
|
465
|
+
state.buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
|
|
749
466
|
end
|
|
750
467
|
|
|
751
468
|
private_class_method def self.method_added(method_name)
|
|
@@ -755,8 +472,13 @@ class Phlex::SGML
|
|
|
755
472
|
if location[0] in "/" | "."
|
|
756
473
|
Phlex.__expand_attribute_cache__(location)
|
|
757
474
|
end
|
|
758
|
-
else
|
|
759
|
-
super
|
|
760
475
|
end
|
|
476
|
+
|
|
477
|
+
super
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def self.__compile__(method_name)
|
|
481
|
+
path, line = instance_method(method_name).source_location
|
|
482
|
+
Phlex::Compiler::Method.new(self, path, line, method_name).compile
|
|
761
483
|
end
|
|
762
484
|
end
|
data/lib/phlex/svg.rb
CHANGED
|
@@ -41,14 +41,14 @@ 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]) || (
|
|
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
|
-
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
|
47
|
+
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
|
|
48
48
|
__yield_content__(&)
|
|
49
49
|
buffer << "</#{tag}>"
|
|
50
50
|
else # without content
|
|
51
|
-
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
|
51
|
+
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << "></#{tag}>"
|
|
52
52
|
end
|
|
53
53
|
else # without attributes
|
|
54
54
|
if block_given # with content block
|
data/lib/phlex/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phlex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0.beta1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joel Drapper
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '2.7'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: refract
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.0'
|
|
27
41
|
description: Build HTML, SVG and CSV views with Ruby classes.
|
|
28
42
|
email:
|
|
29
43
|
- joel@drapper.me
|
|
@@ -34,6 +48,11 @@ files:
|
|
|
34
48
|
- LICENSE.txt
|
|
35
49
|
- README.md
|
|
36
50
|
- lib/phlex.rb
|
|
51
|
+
- lib/phlex/compiler.rb
|
|
52
|
+
- lib/phlex/compiler/class_compiler.rb
|
|
53
|
+
- lib/phlex/compiler/compilation.rb
|
|
54
|
+
- lib/phlex/compiler/file_compiler.rb
|
|
55
|
+
- lib/phlex/compiler/method_compiler.rb
|
|
37
56
|
- lib/phlex/csv.rb
|
|
38
57
|
- lib/phlex/error.rb
|
|
39
58
|
- lib/phlex/errors/argument_error.rb
|
|
@@ -48,6 +67,7 @@ files:
|
|
|
48
67
|
- lib/phlex/html/void_elements.rb
|
|
49
68
|
- lib/phlex/kit.rb
|
|
50
69
|
- lib/phlex/sgml.rb
|
|
70
|
+
- lib/phlex/sgml/attributes.rb
|
|
51
71
|
- lib/phlex/sgml/elements.rb
|
|
52
72
|
- lib/phlex/sgml/safe_object.rb
|
|
53
73
|
- lib/phlex/sgml/safe_value.rb
|
|
@@ -79,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
79
99
|
- !ruby/object:Gem::Version
|
|
80
100
|
version: '0'
|
|
81
101
|
requirements: []
|
|
82
|
-
rubygems_version:
|
|
102
|
+
rubygems_version: 3.6.7
|
|
83
103
|
specification_version: 4
|
|
84
104
|
summary: Object-oriented views in Ruby.
|
|
85
105
|
test_files: []
|