loading
Generated 2025-07-10T17:34:11+00:00

All Files ( 85.83% covered at 80.82 hits/line )

38 files in total.
1778 relevant lines, 1526 lines covered and 252 lines missed. ( 85.83% )
3490 total branches, 1222 branches covered and 2268 branches missed. ( 35.01% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex.rb 80.00 % 72 35 28 7 23.69 50.00 % 6 3 3
lib/phlex/compiler.rb 100.00 % 15 9 9 0 6.56 50.00 % 2 1 1
lib/phlex/compiler/class_compiler.rb 82.35 % 35 17 14 3 5.59 66.67 % 6 4 2
lib/phlex/compiler/compilation.rb 100.00 % 24 14 14 0 6.86 100.00 % 0 0 0
lib/phlex/compiler/file_compiler.rb 83.33 % 29 12 10 2 4.17 50.00 % 2 1 1
lib/phlex/compiler/formatter.rb 90.67 % 126 75 68 7 58.27 72.73 % 22 16 6
lib/phlex/compiler/method_compiler.rb 89.50 % 400 181 162 19 15.55 81.94 % 72 59 13
lib/phlex/compiler/verbatim_formatter.rb 56.88 % 644 327 186 141 16.21 83.33 % 6 5 1
lib/phlex/csv.rb 95.35 % 265 129 123 6 31.64 94.12 % 51 48 3
lib/phlex/error.rb 100.00 % 5 1 1 0 1.00 100.00 % 0 0 0
lib/phlex/errors/argument_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/errors/double_render_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/errors/name_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/errors/runtime_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/fifo.rb 90.00 % 55 30 27 3 199.10 100.00 % 7 7 0
lib/phlex/fifo_cache_store.rb 69.57 % 49 23 16 7 31.43 40.00 % 10 4 6
lib/phlex/helpers.rb 95.83 % 51 24 23 1 6.25 94.12 % 17 16 1
lib/phlex/html.rb 93.88 % 100 49 46 3 97.37 89.29 % 28 25 3
lib/phlex/html/standard_elements.rb 100.00 % 633 105 105 0 1.00 100.00 % 0 0 0
lib/phlex/html/void_elements.rb 100.00 % 66 14 14 0 1.00 100.00 % 0 0 0
lib/phlex/kit.rb 85.00 % 83 40 34 6 2.50 66.67 % 15 10 5
lib/phlex/sgml.rb 84.14 % 484 227 191 36 221.83 71.13 % 97 69 28
lib/phlex/sgml/attributes.rb 99.22 % 266 129 128 1 45.14 97.17 % 106 103 3
lib/phlex/sgml/elements.rb 94.29 % 171 70 66 4 472.26 27.01 % 2973 803 2170
lib/phlex/sgml/safe_object.rb 100.00 % 7 1 1 0 1.00 100.00 % 0 0 0
lib/phlex/sgml/safe_value.rb 100.00 % 11 5 5 0 3.40 100.00 % 0 0 0
lib/phlex/sgml/state.rb 100.00 % 123 74 74 0 299.55 95.45 % 22 21 1
lib/phlex/svg.rb 83.33 % 66 36 30 6 71.56 65.00 % 20 13 7
lib/phlex/svg/standard_elements.rb 100.00 % 421 66 66 0 1.00 100.00 % 0 0 0
quickdraw/compilation_equivalence_cases/attributes.rb 100.00 % 22 7 7 0 1.00 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/basic.rb 100.00 % 9 6 6 0 1.17 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/comment.rb 100.00 % 8 6 6 0 1.17 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/heredoc.rb 100.00 % 16 12 12 0 1.08 50.00 % 4 2 2
quickdraw/compilation_equivalence_cases/interpolated_string.rb 100.00 % 11 8 8 0 1.13 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/override_elements.rb 100.00 % 16 15 15 0 1.80 50.00 % 6 3 3
quickdraw/compilation_equivalence_cases/plain.rb 100.00 % 10 6 6 0 1.00 50.00 % 4 2 2
quickdraw/compilation_equivalence_cases/raw.rb 100.00 % 9 6 6 0 1.17 50.00 % 4 2 2
quickdraw/compilation_equivalence_cases/whitespace.rb 100.00 % 16 11 11 0 1.09 50.00 % 2 1 1

SGML ( 91.9% covered at 220.2 hits/line )

6 files in total.
506 relevant lines, 465 lines covered and 41 lines missed. ( 91.9% )
3198 total branches, 996 branches covered and 2202 branches missed. ( 31.14% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex/sgml.rb 84.14 % 484 227 191 36 221.83 71.13 % 97 69 28
lib/phlex/sgml/attributes.rb 99.22 % 266 129 128 1 45.14 97.17 % 106 103 3
lib/phlex/sgml/elements.rb 94.29 % 171 70 66 4 472.26 27.01 % 2973 803 2170
lib/phlex/sgml/safe_object.rb 100.00 % 7 1 1 0 1.00 100.00 % 0 0 0
lib/phlex/sgml/safe_value.rb 100.00 % 11 5 5 0 3.40 100.00 % 0 0 0
lib/phlex/sgml/state.rb 100.00 % 123 74 74 0 299.55 95.45 % 22 21 1

HTML ( 98.21% covered at 29.11 hits/line )

3 files in total.
168 relevant lines, 165 lines covered and 3 lines missed. ( 98.21% )
28 total branches, 25 branches covered and 3 branches missed. ( 89.29% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex/html.rb 93.88 % 100 49 46 3 97.37 89.29 % 28 25 3
lib/phlex/html/standard_elements.rb 100.00 % 633 105 105 0 1.00 100.00 % 0 0 0
lib/phlex/html/void_elements.rb 100.00 % 66 14 14 0 1.00 100.00 % 0 0 0

SVG ( 94.12% covered at 25.9 hits/line )

2 files in total.
102 relevant lines, 96 lines covered and 6 lines missed. ( 94.12% )
20 total branches, 13 branches covered and 7 branches missed. ( 65.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex/svg.rb 83.33 % 66 36 30 6 71.56 65.00 % 20 13 7
lib/phlex/svg/standard_elements.rb 100.00 % 421 66 66 0 1.00 100.00 % 0 0 0

CSV ( 95.35% covered at 31.64 hits/line )

1 files in total.
129 relevant lines, 123 lines covered and 6 lines missed. ( 95.35% )
51 total branches, 48 branches covered and 3 branches missed. ( 94.12% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex/csv.rb 95.35 % 265 129 123 6 31.64 94.12 % 51 48 3

Ungrouped ( 77.55% covered at 23.67 hits/line )

26 files in total.
873 relevant lines, 677 lines covered and 196 lines missed. ( 77.55% )
193 total branches, 140 branches covered and 53 branches missed. ( 72.54% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex.rb 80.00 % 72 35 28 7 23.69 50.00 % 6 3 3
lib/phlex/compiler.rb 100.00 % 15 9 9 0 6.56 50.00 % 2 1 1
lib/phlex/compiler/class_compiler.rb 82.35 % 35 17 14 3 5.59 66.67 % 6 4 2
lib/phlex/compiler/compilation.rb 100.00 % 24 14 14 0 6.86 100.00 % 0 0 0
lib/phlex/compiler/file_compiler.rb 83.33 % 29 12 10 2 4.17 50.00 % 2 1 1
lib/phlex/compiler/formatter.rb 90.67 % 126 75 68 7 58.27 72.73 % 22 16 6
lib/phlex/compiler/method_compiler.rb 89.50 % 400 181 162 19 15.55 81.94 % 72 59 13
lib/phlex/compiler/verbatim_formatter.rb 56.88 % 644 327 186 141 16.21 83.33 % 6 5 1
lib/phlex/error.rb 100.00 % 5 1 1 0 1.00 100.00 % 0 0 0
lib/phlex/errors/argument_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/errors/double_render_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/errors/name_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/errors/runtime_error.rb 100.00 % 5 2 2 0 1.00 100.00 % 0 0 0
lib/phlex/fifo.rb 90.00 % 55 30 27 3 199.10 100.00 % 7 7 0
lib/phlex/fifo_cache_store.rb 69.57 % 49 23 16 7 31.43 40.00 % 10 4 6
lib/phlex/helpers.rb 95.83 % 51 24 23 1 6.25 94.12 % 17 16 1
lib/phlex/kit.rb 85.00 % 83 40 34 6 2.50 66.67 % 15 10 5
quickdraw/compilation_equivalence_cases/attributes.rb 100.00 % 22 7 7 0 1.00 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/basic.rb 100.00 % 9 6 6 0 1.17 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/comment.rb 100.00 % 8 6 6 0 1.17 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/heredoc.rb 100.00 % 16 12 12 0 1.08 50.00 % 4 2 2
quickdraw/compilation_equivalence_cases/interpolated_string.rb 100.00 % 11 8 8 0 1.13 50.00 % 2 1 1
quickdraw/compilation_equivalence_cases/override_elements.rb 100.00 % 16 15 15 0 1.80 50.00 % 6 3 3
quickdraw/compilation_equivalence_cases/plain.rb 100.00 % 10 6 6 0 1.00 50.00 % 4 2 2
quickdraw/compilation_equivalence_cases/raw.rb 100.00 % 9 6 6 0 1.17 50.00 % 4 2 2
quickdraw/compilation_equivalence_cases/whitespace.rb 100.00 % 16 11 11 0 1.09 50.00 % 2 1 1

lib/phlex.rb

80.0% lines covered

50.0% branches covered

35 relevant lines. 28 lines covered and 7 lines missed.
6 total branches, 3 branches covered and 3 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "erb"
  3. 1 require "set"
  4. 1 require "zeitwerk"
  5. 1 module Phlex
  6. 1 Loader = Zeitwerk::Loader.for_gem.tap do |loader|
  7. 1 loader.ignore("#{__dir__}/ruby_lsp")
  8. 1 loader.inflector.inflect(
  9. "csv" => "CSV",
  10. "fifo" => "FIFO",
  11. "fifo_cache_store" => "FIFOCacheStore",
  12. "html" => "HTML",
  13. "sgml" => "SGML",
  14. "svg" => "SVG",
  15. )
  16. 1 loader.collapse("#{__dir__}/phlex/errors")
  17. 1 loader.setup
  18. end
  19. 1 Escape = ERB::Escape
  20. 1 DEPLOYED_AT = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
  21. 1 CACHED_FILES = Set.new
  22. 1 ATTRIBUTE_CACHE = FIFO.new
  23. 1 def self.__expand_attribute_cache__(file_path)
  24. 694 else: 653 then: 41 unless CACHED_FILES.include?(file_path)
  25. 41 CACHED_FILES << file_path
  26. 41 Phlex::ATTRIBUTE_CACHE.expand(File.size(file_path))
  27. end
  28. end
  29. # Generate an HTML string using Phlex’ HTML DSL
  30. 1 def self.html(&block)
  31. 2 HTML.call do |component|
  32. 2 receiver = block.binding.receiver
  33. 2 receiver.instance_variables.each do |ivar|
  34. 9 then: 0 else: 9 next if component.instance_variable_defined?(ivar)
  35. 9 value = receiver.instance_variable_get(ivar)
  36. 9 component.instance_variable_set(ivar, value)
  37. end
  38. 2 component.instance_exec(receiver, &block)
  39. end
  40. end
  41. # Generate an SVG string using Phlex’ SVG DSL
  42. 1 def self.svg(&block)
  43. SVG.call do |component|
  44. receiver = block.binding.receiver
  45. receiver.instance_variables.each do |ivar|
  46. then: 0 else: 0 next if component.instance_variable_defined?(ivar)
  47. value = receiver.instance_variable_get(ivar)
  48. component.instance_variable_set(ivar, value)
  49. end
  50. component.instance_exec(receiver, &block)
  51. end
  52. end
  53. end
  54. 1 def 💪
  55. 1 Phlex
  56. end

lib/phlex/compiler.rb

100.0% lines covered

50.0% branches covered

9 relevant lines. 9 lines covered and 0 lines missed.
2 total branches, 1 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "prism"
  3. 1 module Phlex::Compiler
  4. 1 Error = Class.new(StandardError)
  5. 1 def self.compile(component)
  6. 11 path, line = Object.const_source_location(component.name)
  7. 11 else: 11 then: 0 return unless File.exist?(path)
  8. 11 source = File.read(path)
  9. 11 tree = Prism.parse(source).value
  10. 11 Compilation.new(component, path, line, source, tree).compile
  11. end
  12. end

lib/phlex/compiler/class_compiler.rb

82.35% lines covered

66.67% branches covered

17 relevant lines. 14 lines covered and 3 lines missed.
6 total branches, 4 branches covered and 2 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::Compiler::ClassCompiler < Prism::Visitor
  3. 1 def initialize(compiler)
  4. 11 @compiler = compiler
  5. end
  6. 1 def compile(node)
  7. 11 visit_all(node.child_nodes)
  8. end
  9. 1 def visit_def_node(node)
  10. 14 then: 1 else: 13 return if node.name == :initialize
  11. 13 then: 0 else: 13 return if node.receiver
  12. 13 compiled_source = Phlex::Compiler::MethodCompiler.new(@compiler.component).compile(node)
  13. 13 else: 0 if compiled_source
  14. then: 13 # puts compiled_source
  15. 13 @compiler.redefine_method(compiled_source, node.location.start_line)
  16. end
  17. end
  18. 1 def visit_class_node(node)
  19. nil
  20. end
  21. 1 def visit_module_node(node)
  22. nil
  23. end
  24. 1 def visit_block_node(node)
  25. nil
  26. end
  27. end

lib/phlex/compiler/compilation.rb

100.0% lines covered

100.0% branches covered

14 relevant lines. 14 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::Compiler
  3. 1 class Compilation
  4. 1 def initialize(component, path, line, source, tree)
  5. 11 @component = component
  6. 11 @path = path
  7. 11 @line = line
  8. 11 @source = source
  9. 11 @tree = tree
  10. 11 freeze
  11. end
  12. 1 attr_reader :component, :line, :source, :path
  13. 1 def compile
  14. 11 FileCompiler.new(self).compile(@tree)
  15. end
  16. 1 def redefine_method(source, line)
  17. 13 @component.class_eval("# frozen_string_literal: true\n#{source}", @path, line - 1)
  18. end
  19. end
  20. end

lib/phlex/compiler/file_compiler.rb

83.33% lines covered

50.0% branches covered

12 relevant lines. 10 lines covered and 2 lines missed.
2 total branches, 1 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::Compiler::FileCompiler < Prism::Visitor
  3. 1 def initialize(compiler)
  4. 11 @compiler = compiler
  5. end
  6. 1 def compile(node)
  7. 11 visit(node)
  8. end
  9. 1 def visit_class_node(node)
  10. 11 then: 11 else: 0 if @compiler.line == node.location.start_line
  11. 11 Phlex::Compiler::ClassCompiler.new(@compiler).compile(node)
  12. end
  13. end
  14. # def visit_module_node(node)
  15. # super
  16. # end
  17. 1 def visit_def_node(node)
  18. nil
  19. end
  20. 1 def visit_block_node(node)
  21. nil
  22. end
  23. end

lib/phlex/compiler/formatter.rb

90.67% lines covered

72.73% branches covered

75 relevant lines. 68 lines covered and 7 lines missed.
22 total branches, 16 branches covered and 6 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::Compiler
  3. 1 class Formatter < VerbatimFormatter
  4. 1 def visit(node)
  5. 890 case node
  6. when: 230 when nil
  7. 230 nil
  8. when: 157 when String
  9. 157 push node
  10. when: 229 when Array, Set
  11. 816 node.each { |n| visit(n) }
  12. when: 0 when Proc
  13. visit node.call
  14. when: 88 when :new_line
  15. 88 new_line
  16. when: 0 when :space
  17. space
  18. else: 186 else
  19. 186 super
  20. end
  21. end
  22. 1 def format(node)
  23. 14 @buffer.clear
  24. 14 visit(node)
  25. 14 [@buffer.join, @source_map]
  26. end
  27. 1 def visit_each(nodes)
  28. 66 i, len = 0, nodes.length
  29. 66 then: 66 if block_given?
  30. 66 body: 137 while i < len
  31. 137 node = nodes[i]
  32. 137 i += 1
  33. 137 then: 137 else: 0 if node
  34. 137 visit node
  35. 137 else: 66 then: 71 yield unless i == len
  36. end
  37. end
  38. else: 0 else
  39. body: 0 while i < len
  40. visit nodes[i]
  41. i += 1
  42. end
  43. end
  44. end
  45. 1 def statement
  46. new_line
  47. yield
  48. end
  49. 1 def parens
  50. 2 push "("
  51. 2 yield
  52. 2 push ")"
  53. end
  54. 1 def new_line
  55. 247 push "\n"
  56. end
  57. 1 def space
  58. 80 push " "
  59. end
  60. 1 def visit_block_node(node)
  61. 30 emit node.opening_loc
  62. 30 new_line
  63. 30 visit node.body
  64. 30 new_line
  65. 30 emit node.closing_loc
  66. end
  67. 1 def visit_call_node(node)
  68. 35 visit node.receiver
  69. 35 emit node.call_operator_loc
  70. 35 emit node.message_loc
  71. 35 then: 4 if node.opening_loc
  72. 4 emit node.opening_loc
  73. else: 31 else
  74. 31 space
  75. end
  76. 35 visit node.arguments
  77. 35 emit node.closing_loc
  78. 35 space
  79. 35 visit node.block
  80. end
  81. 1 def visit_def_node(node)
  82. 14 push "def"
  83. 14 space
  84. 14 push node.name
  85. 14 then: 2 else: 12 if node.parameters
  86. 2 parens do
  87. 2 visit node.parameters
  88. end
  89. end
  90. 14 new_line
  91. 14 visit node.body
  92. 14 new_line
  93. 14 push "end"
  94. end
  95. 1 def visit_statements_node(node)
  96. 137 visit_each(node.compact_child_nodes) { new_line }
  97. end
  98. 1 def visit_interpolated_string_node(node)
  99. 1 push '"'
  100. 1 node.parts.each do |part|
  101. 5 else: 0 case part
  102. when: 4 when Prism::StringNode
  103. 4 push part.unescaped.gsub('"', '\"')
  104. when: 1 when Prism::EmbeddedStatementsNode
  105. 1 visit(part)
  106. end
  107. end
  108. 1 push '"'
  109. end
  110. end
  111. end

lib/phlex/compiler/method_compiler.rb

89.5% lines covered

81.94% branches covered

181 relevant lines. 162 lines covered and 19 lines missed.
72 total branches, 59 branches covered and 13 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "prism"
  3. 1 module Phlex::Compiler
  4. 1 class MethodCompiler < Prism::MutationCompiler
  5. 1 def initialize(component)
  6. 14 @component = component
  7. 14 @current_buffer = nil
  8. 14 @preamble = Set[]
  9. end
  10. 1 def compile(node)
  11. 14 result = visit(node)
  12. 14 then: 14 else: 0 then: 14 else: 0 result.body&.body&.unshift(@preamble, statement("nil"))
  13. 14 source, map = Phlex::Compiler::Formatter.new.format(result)
  14. 14 source
  15. end
  16. 1 def visit_call_node(node)
  17. 63 then: 62 else: 1 if nil == node.receiver
  18. 62 then: 41 if (tag = standard_element?(node))
  19. 41 else: 21 return compile_standard_element(node, tag)
  20. 21 then: 6 elsif (tag = void_element?(node))
  21. 6 else: 15 return compile_void_element(node, tag)
  22. 15 then: 2 elsif whitespace_helper?(node)
  23. 2 else: 13 return compile_whitespace_helper(node)
  24. 13 then: 0 elsif doctype_helper?(node)
  25. else: 13 return compile_doctype_helper(node)
  26. 13 then: 6 elsif plain_helper?(node)
  27. 6 else: 7 return compile_plain_helper(node)
  28. 7 then: 0 elsif fragment_helper?(node)
  29. else: 7 return compile_fragment_helper(node)
  30. 7 then: 2 elsif comment_helper?(node)
  31. 2 else: 5 return compile_comment_helper(node)
  32. 5 then: 1 else: 4 elsif raw_helper?(node)
  33. 1 return compile_raw_helper(node)
  34. end
  35. end
  36. 5 clear_buffer
  37. 5 super
  38. end
  39. 1 def visit_class_node(node)
  40. node
  41. end
  42. 1 def visit_module_node(node)
  43. node
  44. end
  45. 1 def compile_standard_element(node, tag)
  46. [
  47. [
  48. 41 buffer("<#{tag}"),
  49. *(
  50. 41 then: 16 else: 25 if node.arguments
  51. 16 visit_phlex_attributes(node.arguments)
  52. end
  53. ),
  54. buffer(">"),
  55. *(
  56. 41 then: 39 else: 2 if node.block
  57. 39 [visit_phlex_block(node.block)]
  58. end
  59. ),
  60. buffer("</#{tag}>"),
  61. ],
  62. ]
  63. end
  64. 1 def visit_phlex_attributes(node)
  65. 19 then: 19 else: 0 if node.arguments in [Prism::KeywordHashNode[elements: attributes]]
  66. 19 literal_attributes = attributes.all? do |attribute|
  67. 38 Prism::AssocNode === attribute && static_attribute_value_literal?(attribute)
  68. end
  69. 19 then: 19 else: 0 if literal_attributes
  70. 19 return buffer(Phlex::SGML::Attributes.generate_attributes(eval("{#{node.slice}}")))
  71. end
  72. end
  73. [
  74. :new_line,
  75. push("__render_attributes__("),
  76. node,
  77. push(")"),
  78. ]
  79. end
  80. 1 def visit_phlex_block(node)
  81. 42 then: 1 if Prism::BlockArgumentNode === node
  82. 1 else: 41 [push("__yield_content__("), node, push(")")]
  83. 41 then: 17 elsif output_block?(node)
  84. 17 else: 24 visit(node.body)
  85. 24 then: 22 elsif content_block?(node)
  86. 22 content = node.body.body.first
  87. 22 case content
  88. when: 17 when Prism::StringNode
  89. 17 buffer(Phlex::Escape.html_escape(content.unescaped))
  90. when: 5 when Prism::InterpolatedStringNode
  91. 5 compile_interpolated_string_node(content)
  92. else: 0 else
  93. raise
  94. end
  95. else: 2 else
  96. [
  97. 2 statement("__yield_content__ do"),
  98. [node.body],
  99. statement("end"),
  100. ]
  101. end
  102. end
  103. 1 def visit_block_node(node)
  104. 3 node.copy(
  105. body: compile_block_body_node(node.body)
  106. )
  107. end
  108. 1 def compile_block_body_node(node)
  109. [
  110. 3 statement("if #{self_local} == self"),
  111. visit(node),
  112. statement("else"),
  113. [[node]],
  114. statement("end"),
  115. ]
  116. end
  117. 1 def compile_interpolated_string_node(node)
  118. 5 node.parts.map do |part|
  119. 15 case part
  120. when: 10 when Prism::StringNode
  121. 10 buffer(Phlex::Escape.html_escape(part.unescaped))
  122. when: 1 when Prism::EmbeddedVariableNode
  123. [
  124. 1 buffer('#{'),
  125. buffer("::Phlex::Escape.html_escape(("),
  126. buffer(part.variable.slice),
  127. buffer(").to_s)}"),
  128. ]
  129. when: 4 when Prism::EmbeddedStatementsNode
  130. [
  131. 4 buffer('#{'),
  132. buffer("::Phlex::Escape.html_escape(("),
  133. buffer(part.statements.slice, escape: false),
  134. buffer(").to_s)}"),
  135. ]
  136. else: 0 else
  137. raise Phlex::Compiler::Error, "Unexpected node type in InterpolatedStringNode: #{part.class}"
  138. end
  139. end
  140. end
  141. 1 def compile_void_element(node, tag)
  142. [
  143. [
  144. 6 buffer("<#{tag}"),
  145. *(
  146. 6 then: 3 else: 3 if node.arguments
  147. 3 visit_phlex_attributes(node.arguments)
  148. end
  149. ),
  150. buffer(">"),
  151. ],
  152. ]
  153. end
  154. 1 def compile_whitespace_helper(node)
  155. 2 then: 1 if node.block
  156. [
  157. 1 buffer(" "),
  158. visit_phlex_block(node.block),
  159. buffer(" "),
  160. ]
  161. else: 1 else
  162. [
  163. 1 buffer(" "),
  164. ]
  165. end
  166. end
  167. 1 def compile_doctype_helper(node)
  168. [
  169. buffer("<!doctype html>"),
  170. ]
  171. end
  172. 1 def compile_plain_helper(node)
  173. 6 then: 5 if node.arguments in [Prism::StringNode]
  174. [
  175. 5 buffer(node.arguments.child_nodes.first.unescaped),
  176. ]
  177. else: 1 else
  178. 1 @current_buffer = nil
  179. 1 node
  180. end
  181. end
  182. 1 def compile_fragment_helper(node)
  183. node.copy(
  184. block: compile_fragment_helper_block(node.block)
  185. )
  186. end
  187. 1 def compile_fragment_helper_block(node)
  188. node.copy(
  189. body: [
  190. statement("__phlex_original_should_render__ = #{should_render_local}"),
  191. statement("#{should_render_local} = #{state_local}.should_render?"),
  192. visit(node.body),
  193. statement("#{should_render_local} = __phlex_original_should_render__"),
  194. ]
  195. )
  196. end
  197. 1 def compile_comment_helper(node)
  198. [
  199. 2 buffer("<!-- "),
  200. visit_phlex_block(node.block),
  201. buffer(" -->"),
  202. ]
  203. end
  204. 1 def compile_raw_helper(node)
  205. 1 clear_buffer
  206. 1 node
  207. end
  208. 1 private def statement(string)
  209. 27 clear_buffer
  210. [
  211. 27 :new_line,
  212. string,
  213. ";",
  214. ]
  215. end
  216. 1 private def push(value)
  217. 2 clear_buffer
  218. 2 value
  219. end
  220. 1 private def clear_buffer
  221. 35 @current_buffer = nil
  222. end
  223. 1 private def buffer(value, escape: true)
  224. 213 then: 193 if @current_buffer
  225. 193 then: 189 if escape
  226. 189 @current_buffer << value.gsub('"', '\\"')
  227. else: 4 else
  228. 4 @current_buffer << value
  229. end
  230. 193 nil
  231. else: 20 else
  232. 20 new_buffer = +""
  233. 20 @current_buffer = new_buffer
  234. 20 then: 20 if escape
  235. 20 new_buffer << value.gsub('"', '\\"')
  236. else: 0 else
  237. new_buffer << value
  238. end
  239. [
  240. 20 :new_line,
  241. "#{buffer_local} << \"",
  242. new_buffer,
  243. "\" if #{should_render_local}; nil;",
  244. ]
  245. end
  246. end
  247. 1 private def new_scope
  248. original_in_scope = @in_scope
  249. @in_scope = false
  250. yield
  251. @in_scope = original_in_scope
  252. end
  253. 1 private def output_block?(node)
  254. 41 node.body.body.any? do |child|
  255. 41 Prism::CallNode === child && (standard_element?(child) || void_element?(child) || plain_helper?(child) || whitespace_helper?(child) || raw_helper?(child))
  256. end
  257. end
  258. 1 private def content_block?(node)
  259. 24 else: 24 then: 0 return false unless node.body.body.length == 1
  260. 24 node.body.body.first in Prism::StringNode | Prism::InterpolatedStringNode
  261. end
  262. 1 private def standard_element?(node)
  263. 80 if (tag = Phlex::HTML::StandardElements.__registered_elements__[node.name]) &&
  264. 57 (Phlex::HTML::StandardElements == @component.instance_method(node.name).owner)
  265. then: 57
  266. 57 tag
  267. else: 23 else
  268. 23 false
  269. end
  270. end
  271. 1 private def void_element?(node)
  272. 23 if (tag = Phlex::HTML::VoidElements.__registered_elements__[node.name]) &&
  273. 9 (Phlex::HTML::VoidElements == @component.instance_method(node.name).owner)
  274. then: 6
  275. 6 tag
  276. else: 17 else
  277. 17 false
  278. end
  279. end
  280. 1 private def static_attribute_value_literal?(value)
  281. 80 case value
  282. when: 37 when Prism::SymbolNode, Prism::StringNode, Prism::IntegerNode, Prism::FloatNode, Prism::TrueNode, Prism::FalseNode, Prism::NilNode
  283. 37 true
  284. when: 1 when Prism::ArrayNode
  285. 7 value.elements.all? { |n| static_token_value_literal?(n) }
  286. when: 1 when Prism::HashNode
  287. 3 value.elements.all? { |n| static_attribute_value_literal?(n) }
  288. when: 40 when Prism::AssocNode
  289. 40 (Prism::StringNode === value.key || Prism::SymbolNode === value.key) && static_attribute_value_literal?(value.value)
  290. when: 1 when Prism::CallNode
  291. 1 then: 1 if value in { receiver: Prism::ConstantReadNode[name: :Set]| Prism::ConstantPathNode[name: :Set, parent: nil], name: :[] }
  292. 7 value.arguments.arguments.all? { |n| static_token_value_literal?(n) }
  293. else: 0 else
  294. false
  295. end
  296. else: 0 else
  297. false
  298. end
  299. end
  300. 1 private def static_token_value_literal?(value)
  301. 14 case value
  302. when: 12 when Prism::SymbolNode, Prism::StringNode, Prism::IntegerNode, Prism::FloatNode, Prism::NilNode
  303. 12 true
  304. when: 2 when Prism::ArrayNode
  305. 4 value.elements.all? { |n| static_token_value_literal?(n) }
  306. else: 0 else
  307. false
  308. end
  309. end
  310. 1 private def whitespace_helper?(node)
  311. 16 node.name == :whitespace && own_method_without_scope?(node)
  312. end
  313. 1 private def doctype_helper?(node)
  314. 13 node.name == :doctype && own_method_without_scope?(node)
  315. end
  316. 1 private def plain_helper?(node)
  317. 15 node.name == :plain && own_method_without_scope?(node)
  318. end
  319. 1 private def fragment_helper?(node)
  320. 7 node.name == :fragment && own_method_without_scope?(node)
  321. end
  322. 1 private def comment_helper?(node)
  323. 7 node.name == :comment && own_method_without_scope?(node)
  324. end
  325. 1 private def raw_helper?(node)
  326. 6 node.name == :raw && own_method_without_scope?(node)
  327. end
  328. 1 ALLOWED_OWNERS = [Phlex::SGML, Phlex::HTML, Phlex::SVG]
  329. 1 private def own_method_without_scope?(node)
  330. 12 ALLOWED_OWNERS.include?(@component.instance_method(node.name).owner)
  331. end
  332. 1 private def extract_kwargs_from_string(string)
  333. eval("{#{string}}")
  334. end
  335. 1 private def state_local
  336. 40 @preamble << [:new_line, "__phlex_state__ = @_state;"]
  337. 40 "__phlex_state__"
  338. end
  339. 1 private def buffer_local
  340. 20 @preamble << [:new_line, "__phlex_buffer__ = #{state_local}.buffer;"]
  341. 20 "__phlex_buffer__"
  342. end
  343. 1 private def self_local
  344. 3 @preamble << [:new_line, "__phlex_self__ = self;"]
  345. 3 "__phlex_self__"
  346. end
  347. 1 private def should_render_local
  348. 20 @preamble << [:new_line, "__phlex_should_render__ = #{state_local}.should_render?;"]
  349. 20 "__phlex_should_render__"
  350. end
  351. end
  352. end

lib/phlex/compiler/verbatim_formatter.rb

56.88% lines covered

83.33% branches covered

327 relevant lines. 186 lines covered and 141 lines missed.
6 total branches, 5 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::Compiler
  3. 1 class VerbatimFormatter < Prism::BasicVisitor
  4. 1 def initialize
  5. 14 @buffer = []
  6. 14 @source_map = []
  7. 14 @current_line = 1
  8. end
  9. 1 def push(value)
  10. 681 else: 0 string = case value
  11. when: 667 when String
  12. 667 value
  13. when: 14 when Symbol
  14. 14 value.name
  15. end
  16. 681 @buffer << string
  17. 681 new_lines = string.count("\n")
  18. 681 @current_line += new_lines
  19. end
  20. 1 def emit(node)
  21. 209 else: 145 then: 64 return unless node
  22. 145 source_map = @source_map
  23. 145 current_line = @current_line
  24. 145 start_line = node.start_line
  25. 145 end_line = node.end_line
  26. 145 number_of_lines = end_line - start_line
  27. 145 i = 0
  28. 145 body: 145 while i <= number_of_lines
  29. 145 source_map[current_line + i] = start_line + i
  30. 145 i += 1
  31. end
  32. 145 push node.slice
  33. end
  34. 1 def visit_alias_global_variable_node(node)
  35. emit node
  36. end
  37. 1 def visit_alias_method_node(node)
  38. emit node
  39. end
  40. 1 def visit_alternation_pattern_node(node)
  41. emit node
  42. end
  43. 1 def visit_and_node(node)
  44. emit node
  45. end
  46. 1 def visit_arguments_node(node)
  47. 17 emit node
  48. end
  49. 1 def visit_array_node(node)
  50. emit node
  51. end
  52. 1 def visit_array_pattern_node(node)
  53. emit node
  54. end
  55. 1 def visit_assoc_node(node)
  56. emit node
  57. end
  58. 1 def visit_assoc_splat_node(node)
  59. emit node
  60. end
  61. 1 def visit_back_reference_read_node(node)
  62. emit node
  63. end
  64. 1 def visit_begin_node(node)
  65. emit node
  66. end
  67. 1 def visit_block_argument_node(node)
  68. 1 emit node
  69. end
  70. 1 def visit_block_local_variable_node(node)
  71. emit node
  72. end
  73. 1 def visit_block_node(node)
  74. emit node
  75. end
  76. 1 def visit_block_parameter_node(node)
  77. emit node
  78. end
  79. 1 def visit_block_parameters_node(node)
  80. emit node
  81. end
  82. 1 def visit_break_node(node)
  83. emit node
  84. end
  85. 1 def visit_call_and_write_node(node)
  86. emit node
  87. end
  88. 1 def visit_call_node(node)
  89. emit node
  90. end
  91. 1 def visit_call_operator_write_node(node)
  92. emit node
  93. end
  94. 1 def visit_call_or_write_node(node)
  95. emit node
  96. end
  97. 1 def visit_call_target_node(node)
  98. emit node
  99. end
  100. 1 def visit_capture_pattern_node(node)
  101. emit node
  102. end
  103. 1 def visit_case_match_node(node)
  104. emit node
  105. end
  106. 1 def visit_case_node(node)
  107. emit node
  108. end
  109. 1 def visit_class_node(node)
  110. emit node
  111. end
  112. 1 def visit_class_variable_and_write_node(node)
  113. emit node
  114. end
  115. 1 def visit_class_variable_operator_write_node(node)
  116. emit node
  117. end
  118. 1 def visit_class_variable_or_write_node(node)
  119. emit node
  120. end
  121. 1 def visit_class_variable_read_node(node)
  122. emit node
  123. end
  124. 1 def visit_class_variable_target_node(node)
  125. emit node
  126. end
  127. 1 def visit_class_variable_write_node(node)
  128. emit node
  129. end
  130. 1 def visit_constant_and_write_node(node)
  131. emit node
  132. end
  133. 1 def visit_constant_operator_write_node(node)
  134. emit node
  135. end
  136. 1 def visit_constant_or_write_node(node)
  137. emit node
  138. end
  139. 1 def visit_constant_path_and_write_node(node)
  140. emit node
  141. end
  142. 1 def visit_constant_path_node(node)
  143. emit node
  144. end
  145. 1 def visit_constant_path_operator_write_node(node)
  146. emit node
  147. end
  148. 1 def visit_constant_path_or_write_node(node)
  149. emit node
  150. end
  151. 1 def visit_constant_path_target_node(node)
  152. emit node
  153. end
  154. 1 def visit_constant_path_write_node(node)
  155. emit node
  156. end
  157. 1 def visit_constant_read_node(node)
  158. emit node
  159. end
  160. 1 def visit_constant_target_node(node)
  161. emit node
  162. end
  163. 1 def visit_constant_write_node(node)
  164. emit node
  165. end
  166. 1 def visit_def_node(node)
  167. emit node
  168. end
  169. 1 def visit_defined_node(node)
  170. emit node
  171. end
  172. 1 def visit_else_node(node)
  173. emit node
  174. end
  175. 1 def visit_embedded_statements_node(node)
  176. 1 emit node
  177. end
  178. 1 def visit_embedded_variable_node(node)
  179. emit node
  180. end
  181. 1 def visit_ensure_node(node)
  182. emit node
  183. end
  184. 1 def visit_false_node(node)
  185. emit node
  186. end
  187. 1 def visit_find_pattern_node(node)
  188. emit node
  189. end
  190. 1 def visit_flip_flop_node(node)
  191. emit node
  192. end
  193. 1 def visit_float_node(node)
  194. emit node
  195. end
  196. 1 def visit_for_node(node)
  197. emit node
  198. end
  199. 1 def visit_forwarding_arguments_node(node)
  200. emit node
  201. end
  202. 1 def visit_forwarding_parameter_node(node)
  203. emit node
  204. end
  205. 1 def visit_forwarding_super_node(node)
  206. emit node
  207. end
  208. 1 def visit_global_variable_and_write_node(node)
  209. emit node
  210. end
  211. 1 def visit_global_variable_operator_write_node(node)
  212. emit node
  213. end
  214. 1 def visit_global_variable_or_write_node(node)
  215. emit node
  216. end
  217. 1 def visit_global_variable_read_node(node)
  218. emit node
  219. end
  220. 1 def visit_global_variable_target_node(node)
  221. emit node
  222. end
  223. 1 def visit_global_variable_write_node(node)
  224. emit node
  225. end
  226. 1 def visit_hash_node(node)
  227. emit node
  228. end
  229. 1 def visit_hash_pattern_node(node)
  230. emit node
  231. end
  232. 1 def visit_if_node(node)
  233. emit node
  234. end
  235. 1 def visit_imaginary_node(node)
  236. emit node
  237. end
  238. 1 def visit_implicit_node(node)
  239. emit node
  240. end
  241. 1 def visit_implicit_rest_node(node)
  242. emit node
  243. end
  244. 1 def visit_in_node(node)
  245. emit node
  246. end
  247. 1 def visit_index_and_write_node(node)
  248. emit node
  249. end
  250. 1 def visit_index_operator_write_node(node)
  251. emit node
  252. end
  253. 1 def visit_index_or_write_node(node)
  254. emit node
  255. end
  256. 1 def visit_index_target_node(node)
  257. emit node
  258. end
  259. 1 def visit_instance_variable_and_write_node(node)
  260. emit node
  261. end
  262. 1 def visit_instance_variable_operator_write_node(node)
  263. emit node
  264. end
  265. 1 def visit_instance_variable_or_write_node(node)
  266. emit node
  267. end
  268. 1 def visit_instance_variable_read_node(node)
  269. 1 emit node
  270. end
  271. 1 def visit_instance_variable_target_node(node)
  272. emit node
  273. end
  274. 1 def visit_instance_variable_write_node(node)
  275. 1 emit node
  276. end
  277. 1 def visit_integer_node(node)
  278. 2 emit node
  279. end
  280. 1 def visit_interpolated_match_last_line_node(node)
  281. emit node
  282. end
  283. 1 def visit_interpolated_regular_expression_node(node)
  284. emit node
  285. end
  286. 1 def visit_interpolated_string_node(node)
  287. emit node
  288. end
  289. 1 def visit_interpolated_symbol_node(node)
  290. emit node
  291. end
  292. 1 def visit_interpolated_x_string_node(node)
  293. emit node
  294. end
  295. 1 def visit_it_local_variable_read_node(node)
  296. emit node
  297. end
  298. 1 def visit_it_parameters_node(node)
  299. emit node
  300. end
  301. 1 def visit_keyword_hash_node(node)
  302. emit node
  303. end
  304. 1 def visit_keyword_rest_parameter_node(node)
  305. emit node
  306. end
  307. 1 def visit_lambda_node(node)
  308. emit node
  309. end
  310. 1 def visit_local_variable_and_write_node(node)
  311. emit node
  312. end
  313. 1 def visit_local_variable_operator_write_node(node)
  314. emit node
  315. end
  316. 1 def visit_local_variable_or_write_node(node)
  317. emit node
  318. end
  319. 1 def visit_local_variable_read_node(node)
  320. emit node
  321. end
  322. 1 def visit_local_variable_target_node(node)
  323. emit node
  324. end
  325. 1 def visit_local_variable_write_node(node)
  326. 3 emit node
  327. end
  328. 1 def visit_match_last_line_node(node)
  329. emit node
  330. end
  331. 1 def visit_match_predicate_node(node)
  332. emit node
  333. end
  334. 1 def visit_match_required_node(node)
  335. emit node
  336. end
  337. 1 def visit_match_write_node(node)
  338. emit node
  339. end
  340. 1 def visit_missing_node(node)
  341. emit node
  342. end
  343. 1 def visit_module_node(node)
  344. emit node
  345. end
  346. 1 def visit_multi_target_node(node)
  347. emit node
  348. end
  349. 1 def visit_multi_write_node(node)
  350. emit node
  351. end
  352. 1 def visit_next_node(node)
  353. emit node
  354. end
  355. 1 def visit_nil_node(node)
  356. emit node
  357. end
  358. 1 def visit_no_keywords_parameter_node(node)
  359. emit node
  360. end
  361. 1 def visit_numbered_parameters_node(node)
  362. emit node
  363. end
  364. 1 def visit_numbered_reference_read_node(node)
  365. emit node
  366. end
  367. 1 def visit_optional_keyword_parameter_node(node)
  368. emit node
  369. end
  370. 1 def visit_optional_parameter_node(node)
  371. emit node
  372. end
  373. 1 def visit_or_node(node)
  374. emit node
  375. end
  376. 1 def visit_parameters_node(node)
  377. 2 emit node
  378. end
  379. 1 def visit_parentheses_node(node)
  380. emit node
  381. end
  382. 1 def visit_pinned_expression_node(node)
  383. emit node
  384. end
  385. 1 def visit_pinned_variable_node(node)
  386. emit node
  387. end
  388. 1 def visit_post_execution_node(node)
  389. emit node
  390. end
  391. 1 def visit_pre_execution_node(node)
  392. emit node
  393. end
  394. 1 def visit_program_node(node)
  395. emit node
  396. end
  397. 1 def visit_range_node(node)
  398. emit node
  399. end
  400. 1 def visit_rational_node(node)
  401. emit node
  402. end
  403. 1 def visit_redo_node(node)
  404. emit node
  405. end
  406. 1 def visit_regular_expression_node(node)
  407. emit node
  408. end
  409. 1 def visit_required_keyword_parameter_node(node)
  410. emit node
  411. end
  412. 1 def visit_required_parameter_node(node)
  413. emit node
  414. end
  415. 1 def visit_rescue_modifier_node(node)
  416. emit node
  417. end
  418. 1 def visit_rescue_node(node)
  419. emit node
  420. end
  421. 1 def visit_rest_parameter_node(node)
  422. emit node
  423. end
  424. 1 def visit_retry_node(node)
  425. emit node
  426. end
  427. 1 def visit_return_node(node)
  428. emit node
  429. end
  430. 1 def visit_self_node(node)
  431. emit node
  432. end
  433. 1 def visit_shareable_constant_node(node)
  434. emit node
  435. end
  436. 1 def visit_singleton_class_node(node)
  437. emit node
  438. end
  439. 1 def visit_source_encoding_node(node)
  440. emit node
  441. end
  442. 1 def visit_source_file_node(node)
  443. emit node
  444. end
  445. 1 def visit_source_line_node(node)
  446. emit node
  447. end
  448. 1 def visit_splat_node(node)
  449. emit node
  450. end
  451. 1 def visit_statements_node(node)
  452. emit node
  453. end
  454. 1 def visit_string_node(node)
  455. 11 emit node
  456. end
  457. 1 def visit_super_node(node)
  458. emit node
  459. end
  460. 1 def visit_symbol_node(node)
  461. emit node
  462. end
  463. 1 def visit_true_node(node)
  464. emit node
  465. end
  466. 1 def visit_undef_node(node)
  467. emit node
  468. end
  469. 1 def visit_unless_node(node)
  470. emit node
  471. end
  472. 1 def visit_until_node(node)
  473. emit node
  474. end
  475. 1 def visit_when_node(node)
  476. emit node
  477. end
  478. 1 def visit_while_node(node)
  479. emit node
  480. end
  481. 1 def visit_x_string_node(node)
  482. emit node
  483. end
  484. 1 def visit_yield_node(node)
  485. 1 emit node
  486. end
  487. end
  488. end

lib/phlex/csv.rb

95.35% lines covered

94.12% branches covered

129 relevant lines. 123 lines covered and 6 lines missed.
51 total branches, 48 branches covered and 3 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::CSV
  3. 1 FORMULA_PREFIXES_MAP = Array.new(128).tap do |map|
  4. 1 "=+-@\t\r".each_byte do |byte|
  5. 6 map[byte] = true
  6. end
  7. end.freeze
  8. 1 UNDEFINED = Object.new
  9. 1 def initialize(collection)
  10. 14 @collection = collection
  11. 14 @_row_buffer = []
  12. 14 @_headers = []
  13. 14 @_row_appender = nil
  14. end
  15. 1 attr_reader :collection
  16. 1 def call(buffer = +"", context: nil, delimiter: self.delimiter)
  17. 12 ensure_escape_csv_injection_configured!
  18. 11 strip_whitespace = trim_whitespace?
  19. 11 escape_csv_injection = escape_csv_injection?
  20. 11 row_buffer = @_row_buffer
  21. 11 headers = @_headers
  22. 11 has_yielder = respond_to?(:yielder, true)
  23. 11 first_row = true
  24. 11 render_headers = render_headers?
  25. 11 then: 1 else: 10 if delimiter.length != 1
  26. 1 raise Phlex::ArgumentError.new("Delimiter must be a single character")
  27. end
  28. 10 then: 4 if strip_whitespace
  29. 4 escape_regex = /[\n"#{delimiter}]/
  30. else: 6 else
  31. 6 escape_regex = /^\s|\s$|[\n"#{delimiter}]/
  32. end
  33. 10 then: 1 else: 9 if has_yielder
  34. 1 warn <<~MESSAGE
  35. Custom yielders are deprecated in Phlex::CSV.
  36. Please replace your yielder with an `around_row` method.
  37. You should be able to just rename your yielder method
  38. and change `yield` to `super`.
  39. MESSAGE
  40. end
  41. 10 row_appender = -> {
  42. 84 row = row_buffer
  43. 84 then: 10 else: 74 if first_row
  44. 10 first_row = false
  45. 10 i = 0
  46. 10 number_of_columns = row.length
  47. 10 first_col = true
  48. 10 body: 20 while i < number_of_columns
  49. 20 header, = row[i]
  50. 20 headers[i] = header
  51. 20 then: 18 else: 2 if render_headers
  52. 18 then: 9 if first_col
  53. 9 first_col = false
  54. else: 9 else
  55. 9 buffer << delimiter
  56. end
  57. 18 __escape__(buffer, header, escape_csv_injection:, strip_whitespace:, escape_regex:)
  58. end
  59. 20 i += 1
  60. end
  61. 10 then: 9 else: 1 buffer << "\n" if render_headers
  62. end
  63. 84 i = 0
  64. 84 number_of_columns = row.length
  65. 84 first_col = true
  66. 84 body: 168 while i < number_of_columns
  67. 168 header, value = row[i]
  68. 168 else: 168 then: 0 unless headers[i] == header
  69. raise Phlex::RuntimeError.new("Header mismatch at index #{i}: expected #{headers[i]}, got #{header}.")
  70. end
  71. 168 then: 84 if first_col
  72. 84 first_col = false
  73. else: 84 else
  74. 84 buffer << delimiter
  75. end
  76. 168 __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
  77. 168 i += 1
  78. end
  79. 84 buffer << "\n"
  80. 84 row_buffer.clear
  81. }
  82. 10 then: 1 if has_yielder
  83. 1 each_item do |record|
  84. 7 yielder(record) do |*a, **k|
  85. 14 row_template(*a, **k)
  86. 14 row_appender.call
  87. end
  88. end
  89. else: 9 else
  90. 9 @row_appender = row_appender
  91. 9 each_item do |record|
  92. 63 around_row(record)
  93. end
  94. end
  95. 10 buffer
  96. end
  97. 1 def around_row(...)
  98. 70 row_template(...)
  99. 70 @row_appender.call
  100. end
  101. 1 def filename
  102. 1 nil
  103. end
  104. 1 def content_type
  105. 1 "text/csv"
  106. end
  107. 1 def delimiter
  108. 9 ","
  109. end
  110. 1 private def column(header = nil, value)
  111. 168 @_row_buffer << [header, value]
  112. end
  113. 1 private def each_item(&)
  114. 10 collection.each(&)
  115. end
  116. # Override and set to `false` to disable rendering headers.
  117. 1 private def render_headers?
  118. 10 true
  119. end
  120. # Override and set to `true` to strip leading and trailing whitespace from values.
  121. 1 private def trim_whitespace?
  122. 3 false
  123. end
  124. # Override and set to `false` to disable CSV injection escapes or `true` to enable.
  125. 1 private def escape_csv_injection?
  126. 1 UNDEFINED
  127. end
  128. 1 private def __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
  129. 186 value = case value
  130. when: 126 when String
  131. 126 value
  132. when: 12 when Symbol
  133. 12 value.name
  134. else: 48 else
  135. 48 value.to_s
  136. end
  137. 186 then: 64 if strip_whitespace
  138. 64 value = value.strip
  139. 64 then: 48 if escape_csv_injection
  140. 48 then: 12 if value.empty?
  141. 12 else: 36 buffer << value
  142. 36 then: 6 elsif FORMULA_PREFIXES_MAP[value.getbyte(0)]
  143. 6 value.gsub!('"', '""')
  144. 6 else: 30 buffer << '"\'' << value << '"'
  145. 30 then: 6 elsif value.match?(escape_regex)
  146. 6 value.gsub!('"', '""')
  147. 6 buffer << '"' << value << '"'
  148. else: 24 else
  149. 24 buffer << value
  150. end
  151. else: 16 else # not escaping CSV injection
  152. 16 buffer << value
  153. end
  154. else: 122 else # not stripping whitespace
  155. 122 then: 92 if escape_csv_injection
  156. 92 first_byte = value.getbyte(0)
  157. 92 then: 24 if value.empty?
  158. 24 else: 68 buffer << '""'
  159. 68 then: 12 elsif FORMULA_PREFIXES_MAP[first_byte]
  160. 12 else: 56 buffer << '"\'' << value.gsub('"', '""') << '"'
  161. 56 then: 18 elsif value.match?(escape_regex)
  162. 18 buffer << '"' << value.gsub('"', '""') << '"'
  163. else: 38 else
  164. 38 buffer << value
  165. end
  166. else: 30 else # not escaping CSV injection
  167. 30 then: 8 if value.empty?
  168. 8 else: 22 buffer << '""'
  169. 22 then: 6 elsif value.match?(escape_regex)
  170. 6 buffer << '"' << value.gsub('"', '""') << '"'
  171. else: 16 else
  172. 16 buffer << value
  173. end
  174. end
  175. end
  176. end
  177. # Handle legacy `view_template` method
  178. 1 private def respond_to_missing?(method_name, include_private)
  179. 10 (method_name == :row_template && respond_to?(:view_template)) || super
  180. end
  181. # Handle legacy `view_template` method
  182. 1 private def method_missing(method_name, ...)
  183. then: 0 if method_name == :row_template && respond_to?(:view_template)
  184. warn "Deprecated: Use `row_template` instead of `view_template` in Phlex CSVs."
  185. self.class.alias_method :row_template, :view_template
  186. view_template(...)
  187. else: 0 else
  188. super
  189. end
  190. end
  191. 1 private def ensure_escape_csv_injection_configured!
  192. 12 then: 1 else: 11 if escape_csv_injection? == UNDEFINED
  193. 1 raise <<~MESSAGE
  194. You need to define `escape_csv_injection?` in #{self.class.name}.
  195. CSV injection is a security vulnerability where malicious spreadsheet
  196. formulae are used to execute code or exfiltrate data when a CSV is opened
  197. in a spreadsheet program such as Microsoft Excel or Google Sheets.
  198. For more information, see https://owasp.org/www-community/attacks/CSV_Injection
  199. If you’re sure this CSV will never be opened in a spreadsheet program,
  200. you can *disable* CSV injection escapes:
  201. def escape_csv_injection? = false
  202. This is useful when using CSVs for byte-for-byte data exchange between secure systems.
  203. Alternatively, you can *enable* CSV injection escapes at the cost of data integrity:
  204. def escape_csv_injection? = true
  205. Enabling the CSV injection escapes will prefix with a single quote `'` any
  206. values that start with: `=`, `+`, `-`, `@`, `\\t`, `\\r`
  207. Unfortunately, there is no one-size-fits-all solution to CSV injection.
  208. You need to decide based on your specific use case.
  209. MESSAGE
  210. end
  211. end
  212. end

lib/phlex/error.rb

100.0% lines covered

100.0% branches covered

1 relevant lines. 1 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. # @api private
  3. 1 module Phlex::Error
  4. end

lib/phlex/errors/argument_error.rb

100.0% lines covered

100.0% branches covered

2 relevant lines. 2 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::ArgumentError < ArgumentError
  3. 1 include Phlex::Error
  4. end

lib/phlex/errors/double_render_error.rb

100.0% lines covered

100.0% branches covered

2 relevant lines. 2 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::DoubleRenderError < RuntimeError
  3. 1 include Phlex::Error
  4. end

lib/phlex/errors/name_error.rb

100.0% lines covered

100.0% branches covered

2 relevant lines. 2 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::NameError < NameError
  3. 1 include Phlex::Error
  4. end

lib/phlex/errors/runtime_error.rb

100.0% lines covered

100.0% branches covered

2 relevant lines. 2 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::RuntimeError < RuntimeError
  3. 1 include Phlex::Error
  4. end

lib/phlex/fifo.rb

90.0% lines covered

100.0% branches covered

30 relevant lines. 27 lines covered and 3 lines missed.
7 total branches, 7 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. # @api private
  3. 1 class Phlex::FIFO
  4. 1 def initialize(max_bytesize: 2_000, max_value_bytesize: 2_000)
  5. 15 @store = {}
  6. 15 @max_bytesize = max_bytesize
  7. 15 @max_value_bytesize = max_value_bytesize
  8. 15 @bytesize = 0
  9. 15 @mutex = Monitor.new
  10. end
  11. 1 attr_reader :bytesize, :max_bytesize
  12. 1 def expand(bytes)
  13. 41 @mutex.synchronize do
  14. 41 @max_bytesize += bytes
  15. end
  16. end
  17. 1 def [](key)
  18. 1806 k, v = @store[key.hash]
  19. 1806 then: 1593 else: 213 v if k.eql?(key)
  20. end
  21. 1 def []=(key, value)
  22. 287 then: 1 else: 286 return if value.bytesize > @max_value_bytesize
  23. 286 digest = key.hash
  24. 286 @mutex.synchronize do
  25. # Check the key definitely doesn't exist now we have the lock
  26. 286 then: 1 else: 285 return if @store[digest]
  27. 285 @store[digest] = [key, value].freeze
  28. 285 @bytesize += value.bytesize
  29. 285 body: 97 while @bytesize > @max_bytesize
  30. 97 k, v = @store.shift
  31. 97 @bytesize -= v[1].bytesize
  32. end
  33. end
  34. end
  35. 1 def size
  36. 2 @store.size
  37. end
  38. 1 def clear
  39. @mutex.synchronize do
  40. @store.clear
  41. @bytesize = 0
  42. end
  43. end
  44. end

lib/phlex/fifo_cache_store.rb

69.57% lines covered

40.0% branches covered

23 relevant lines. 16 lines covered and 7 lines missed.
10 total branches, 4 branches covered and 6 branches missed.
    
  1. # frozen_string_literal: true
  2. # An extremely fast in-memory cache store that evicts keys on a first-in-first-out basis.
  3. 1 class Phlex::FIFOCacheStore
  4. 1 def initialize(max_bytesize: 2 ** 20)
  5. 11 @fifo = Phlex::FIFO.new(
  6. max_bytesize:,
  7. max_value_bytesize: max_bytesize
  8. )
  9. end
  10. 1 def fetch(key)
  11. 29 fifo = @fifo
  12. 29 key = map_key(key)
  13. 29 then: 7 if (result = fifo[key])
  14. 7 JSON.parse(result)
  15. else: 22 else
  16. 22 result = yield
  17. 22 fifo[key] = JSON.fast_generate(result)
  18. 22 result
  19. end
  20. end
  21. 1 def clear
  22. @fifo.clear
  23. end
  24. 1 private def map_key(value)
  25. 192 case value
  26. when: 50 when Array
  27. 213 value.map { |it| map_key(it) }
  28. when: 0 when Hash
  29. value.to_h { |k, v| [map_key(k), map_key(v)].freeze }
  30. when: 142 when String, Symbol, Integer, Float, Time, true, false, nil
  31. 142 value
  32. else: 0 else
  33. then: 0 if value.respond_to?(:cache_key_with_version)
  34. else: 0 map_key(value.cache_key_with_version)
  35. then: 0 elsif value.respond_to?(:cache_key)
  36. map_key(value.cache_key)
  37. else: 0 else
  38. raise ArgumentError.new("Invalid cache key: #{value.class}")
  39. end
  40. end
  41. end
  42. end

lib/phlex/helpers.rb

95.83% lines covered

94.12% branches covered

24 relevant lines. 23 lines covered and 1 lines missed.
17 total branches, 16 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. # @api private
  3. 1 module Phlex::Helpers
  4. 1 private def mix(*args)
  5. 16 args.each_with_object({}) do |object, result|
  6. 32 result.merge!(object) do |_key, old, new|
  7. 15 case [old, new].freeze
  8. in: 2 in [Array, Array] | [Set, Set]
  9. 2 old + new
  10. in: 1 in [Array, Set]
  11. 1 old + new.to_a
  12. in: 1 in [Array, String]
  13. 1 old + [new]
  14. in: 1 in [Hash, Hash]
  15. 1 mix(old, new)
  16. in: 1 in [Set, Array]
  17. 1 old.to_a + new
  18. in: 1 in [Set, String]
  19. 1 old.to_a + [new]
  20. in: 1 in [String, Array]
  21. 1 [old] + new
  22. in: 1 in [String, Set]
  23. 1 [old] + new.to_a
  24. in: 2 in [String, String]
  25. 2 "#{old} #{new}"
  26. in: 1 in [_, Hash]
  27. 1 { _: old, **new }
  28. in: 0 in [Hash, _]
  29. { **old, _: new }
  30. in: 2 in [_, nil]
  31. 2 old
  32. else: 1 else
  33. 1 new
  34. end
  35. end
  36. 32 result.transform_keys! do |key|
  37. 33 then: 1 else: 32 key.end_with?("!") ? key.name.chop.to_sym : key
  38. end
  39. end
  40. end
  41. 1 private def grab(**bindings)
  42. 2 then: 1 if bindings.size > 1
  43. 1 bindings.values
  44. else: 1 else
  45. 1 bindings.values.first
  46. end
  47. end
  48. end

lib/phlex/html.rb

93.88% lines covered

89.29% branches covered

49 relevant lines. 46 lines covered and 3 lines missed.
28 total branches, 25 branches covered and 3 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::HTML < Phlex::SGML
  3. 1 extend Phlex::SGML::Elements
  4. 1 include VoidElements, StandardElements
  5. # Output an HTML doctype.
  6. 1 def doctype
  7. 5 state = @_state
  8. 5 else: 1 then: 4 return unless state.should_render?
  9. 1 state.buffer << "<!doctype html>"
  10. 1 nil
  11. end
  12. # Outputs an `<svg>` tag.
  13. #
  14. # [MDN Docs](https://developer.mozilla.org/docs/Web/SVG/Element/svg)
  15. # [Spec](https://html.spec.whatwg.org/#the-svg-element)
  16. 1 def svg(*, **, &)
  17. 6 then: 5 if block_given?
  18. 10 super { render Phlex::SVG.new(&) }
  19. else: 1 else
  20. 1 super
  21. end
  22. end
  23. # Override to provide a filename for the HTML file
  24. 1 def filename
  25. nil
  26. end
  27. # Returns the string "text/html"
  28. 1 def content_type
  29. 1 "text/html"
  30. end
  31. # Output an HTML tag dynamically, e.g:
  32. #
  33. # ```ruby
  34. # @tag_name = :h1
  35. # tag(@tag_name, class: "title")
  36. # ```
  37. 1 def tag(name, **attributes, &)
  38. 452 state = @_state
  39. 452 block_given = block_given?
  40. 452 buffer = state.buffer
  41. 452 else: 452 then: 0 unless state.should_render?
  42. then: 0 else: 0 yield(self) if block_given
  43. return nil
  44. end
  45. 452 else: 451 then: 1 unless Symbol === name
  46. 1 raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
  47. end
  48. 451 then: 414 if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
  49. 414 then: 206 if attributes.length > 0 # with attributes
  50. 206 then: 103 if block_given # with content block
  51. 103 buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
  52. 103 then: 1 if tag == "svg"
  53. 1 render Phlex::SVG.new(&)
  54. else: 102 else
  55. 102 __yield_content__(&)
  56. end
  57. 103 buffer << "</#{tag}>"
  58. else: 103 else # without content
  59. 103 buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << "></#{tag}>"
  60. end
  61. else: 208 else # without attributes
  62. 208 then: 104 if block_given # with content block
  63. 104 buffer << ("<#{tag}>")
  64. 104 then: 2 if tag == "svg"
  65. 2 render Phlex::SVG.new(&)
  66. else: 102 else
  67. 102 __yield_content__(&)
  68. end
  69. 104 buffer << "</#{tag}>"
  70. else: 104 else # without content
  71. 104 buffer << "<#{tag}></#{tag}>"
  72. end
  73. else: 37 end
  74. 37 then: 36 elsif (tag = VoidElements.__registered_elements__[name])
  75. 36 then: 12 else: 24 if block_given
  76. 12 raise Phlex::ArgumentError.new("Void elements cannot have content blocks.")
  77. end
  78. 24 then: 12 if attributes.length > 0 # with attributes
  79. 12 buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
  80. else: 12 else # without attributes
  81. 12 buffer << "<#{tag}>"
  82. end
  83. 24 nil
  84. else: 1 else
  85. 1 raise Phlex::ArgumentError.new("Invalid HTML tag: #{name}")
  86. end
  87. end
  88. end

lib/phlex/html/standard_elements.rb

100.0% lines covered

100.0% branches covered

105 relevant lines. 105 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. # Standard HTML elements accept content and always have a closing tag.
  3. 1 module Phlex::HTML::StandardElements
  4. 1 extend Phlex::SGML::Elements
  5. # Outputs an `<a>` tag.
  6. #
  7. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/a)
  8. # [Spec](https://html.spec.whatwg.org/#the-a-element)
  9. 1 register_element def a(**attributes, &content) = nil
  10. # Outputs an `<abbr>` tag.
  11. #
  12. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/abbr)
  13. # [Spec](https://html.spec.whatwg.org/#the-abbr-element)
  14. 1 register_element def abbr(**attributes, &content) = nil
  15. # Outputs an `<address>` tag.
  16. #
  17. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/address)
  18. # [Spec](https://html.spec.whatwg.org/#the-address-element)
  19. 1 register_element def address(**attributes, &content) = nil
  20. # Outputs an `<article>` tag.
  21. #
  22. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/article)
  23. # [Spec](https://html.spec.whatwg.org/#the-article-element)
  24. 1 register_element def article(**attributes, &content) = nil
  25. # Outputs an `<aside>` tag.
  26. #
  27. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/aside)
  28. # [Spec](https://html.spec.whatwg.org/#the-aside-element)
  29. 1 register_element def aside(**attributes, &content) = nil
  30. # Outputs an `<audio>` tag.
  31. #
  32. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/audio)
  33. # [Spec](https://html.spec.whatwg.org/#the-audio-element)
  34. # [Can I Use?](https://caniuse.com/audio)
  35. 1 register_element def audio(**attributes, &content) = nil
  36. # Outputs a `<b>` tag.
  37. #
  38. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/b)
  39. # [Spec](https://html.spec.whatwg.org/#the-b-element)
  40. 1 register_element def b(**attributes, &content) = nil
  41. # Outputs a `<bdi>` tag.
  42. #
  43. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/bdi)
  44. # [Spec](https://html.spec.whatwg.org/#the-bdi-element)
  45. 1 register_element def bdi(**attributes, &content) = nil
  46. # Outputs a `<bdo>` tag.
  47. #
  48. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/bdo)
  49. # [Spec](https://html.spec.whatwg.org/#the-bdo-element)
  50. 1 register_element def bdo(**attributes, &content) = nil
  51. # Outputs a `<blockquote>` tag.
  52. #
  53. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/blockquote)
  54. # [Spec](https://html.spec.whatwg.org/#the-blockquote-element)
  55. 1 register_element def blockquote(**attributes, &content) = nil
  56. # Outputs a `<body>` tag.
  57. #
  58. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/body)
  59. # [Spec](https://html.spec.whatwg.org/#the-body-element)
  60. 1 register_element def body(**attributes, &content) = nil
  61. # Outputs a `<button>` tag.
  62. # The `<button>` element is an interactive element activated by a user with a mouse, keyboard, finger, voice command, or other assistive technology to perform an action, such as submitting a form or opening a dialog.
  63. #
  64. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/button)
  65. # [Spec](https://html.spec.whatwg.org/#the-button-element)
  66. 1 register_element def button(**attributes, &content) = nil
  67. # Outputs a `<canvas>` tag.
  68. #
  69. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/canvas)
  70. # [Spec](https://html.spec.whatwg.org/#the-canvas-element)
  71. 1 register_element def canvas(**attributes, &content) = nil
  72. # Outputs a `<caption>` tag.
  73. #
  74. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/caption)
  75. # [Spec](https://html.spec.whatwg.org/#the-caption-element)
  76. 1 register_element def caption(**attributes, &content) = nil
  77. # Outputs a `<cite>` tag.
  78. #
  79. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/cite)
  80. # [Spec](https://html.spec.whatwg.org/#the-cite-element)
  81. 1 register_element def cite(**attributes, &content) = nil
  82. # Outputs a `<code>` tag.
  83. #
  84. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/code)
  85. # [Spec](https://html.spec.whatwg.org/#the-code-element)
  86. 1 register_element def code(**attributes, &content) = nil
  87. # Outputs a `<colgroup>` tag.
  88. #
  89. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/colgroup)
  90. # [Spec](https://html.spec.whatwg.org/#the-colgroup-element)
  91. 1 register_element def colgroup(**attributes, &content) = nil
  92. # Outputs a `<data>` tag.
  93. #
  94. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/data)
  95. # [Spec](https://html.spec.whatwg.org/#the-data-element)
  96. 1 register_element def data(**attributes, &content) = nil
  97. # Outputs a `<datalist>` tag.
  98. #
  99. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/datalist)
  100. # [Spec](https://html.spec.whatwg.org/#the-datalist-element)
  101. 1 register_element def datalist(**attributes, &content) = nil
  102. # Outputs a `<dd>` tag.
  103. #
  104. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/dd)
  105. # [Spec](https://html.spec.whatwg.org/#the-dd-element)
  106. 1 register_element def dd(**attributes, &content) = nil
  107. # Outputs a `<del>` tag.
  108. #
  109. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/del)
  110. # [Spec](https://html.spec.whatwg.org/#the-del-element)
  111. 1 register_element def del(**attributes, &content) = nil
  112. # Outputs a `<details>` tag.
  113. #
  114. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/details)
  115. # [Spec](https://html.spec.whatwg.org/#the-details-element)
  116. # [Can I Use?](https://caniuse.com/details)
  117. 1 register_element def details(**attributes, &content) = nil
  118. # Outputs a `<dfn>` tag.
  119. #
  120. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/dfn)
  121. # [Spec](https://html.spec.whatwg.org/#the-dfn-element)
  122. 1 register_element def dfn(**attributes, &content) = nil
  123. # Outputs a `<dialog>` tag.
  124. #
  125. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/dialog)
  126. # [Spec](https://html.spec.whatwg.org/#the-dialog-element)
  127. # [Can I use?](https://caniuse.com/dialog)
  128. 1 register_element def dialog(**attributes, &content) = nil
  129. # Outputs a `<div>` tag.
  130. #
  131. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/div)
  132. # [Spec](https://html.spec.whatwg.org/#the-div-element)
  133. 1 register_element def div(**attributes, &content) = nil
  134. # Outputs a `<dl>` tag.
  135. #
  136. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/dl)
  137. # [Spec](https://html.spec.whatwg.org/#the-dl-element)
  138. 1 register_element def dl(**attributes, &content) = nil
  139. # Outputs a `<dt>` tag.
  140. #
  141. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/dt)
  142. # [Spec](https://html.spec.whatwg.org/#the-dt-element)
  143. 1 register_element def dt(**attributes, &content) = nil
  144. # Outputs an `<em>` tag.
  145. #
  146. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/em)
  147. # [Spec](https://html.spec.whatwg.org/#the-em-element)
  148. 1 register_element def em(**attributes, &content) = nil
  149. # [EXPERIMENTAL] Outputs a `<fencedframe>` tag.
  150. #
  151. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/fencedframe)
  152. # [Can I Use?](https://caniuse.com/mdn-html_elements_fencedframe)
  153. 1 register_element def fencedframe(**attributes, &content) = nil
  154. # Outputs a `<fieldset>` tag.
  155. #
  156. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/fieldset)
  157. # [Spec](https://html.spec.whatwg.org/#the-fieldset-element)
  158. 1 register_element def fieldset(**attributes, &content) = nil
  159. # Outputs a `<figcaption>` tag.
  160. #
  161. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/figcaption)
  162. # [Spec](https://html.spec.whatwg.org/#the-figcaption-element)
  163. 1 register_element def figcaption(**attributes, &content) = nil
  164. # Outputs a `<figure>` tag.
  165. #
  166. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/figure)
  167. # [Spec](https://html.spec.whatwg.org/#the-figure-element)
  168. 1 register_element def figure(**attributes, &content) = nil
  169. # Outputs a `<footer>` tag.
  170. #
  171. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/footer)
  172. # [Spec](https://html.spec.whatwg.org/#the-footer-element)
  173. 1 register_element def footer(**attributes, &content) = nil
  174. # Outputs a `<form>` tag.
  175. #
  176. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/form)
  177. # [Spec](https://html.spec.whatwg.org/#the-form-element)
  178. 1 register_element def form(**attributes, &content) = nil
  179. # Outputs an `<h1>` tag.
  180. #
  181. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/h1)
  182. # [Spec](https://html.spec.whatwg.org/#the-h1-element)
  183. 1 register_element def h1(**attributes, &content) = nil
  184. # Outputs an `<h2>` tag.
  185. #
  186. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/h2)
  187. # [Spec](https://html.spec.whatwg.org/#the-h2-element)
  188. 1 register_element def h2(**attributes, &content) = nil
  189. # Outputs an `<h3>` tag.
  190. #
  191. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/h3)
  192. # [Spec](https://html.spec.whatwg.org/#the-h3-element)
  193. 1 register_element def h3(**attributes, &content) = nil
  194. # Outputs an `<h4>` tag.
  195. #
  196. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/h4)
  197. # [Spec](https://html.spec.whatwg.org/#the-h4-element)
  198. 1 register_element def h4(**attributes, &content) = nil
  199. # Outputs an `<h5>` tag.
  200. #
  201. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/h5)
  202. # [Spec](https://html.spec.whatwg.org/#the-h5-element)
  203. 1 register_element def h5(**attributes, &content) = nil
  204. # Outputs an `<h6>` tag.
  205. #
  206. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/h6)
  207. # [Spec](https://html.spec.whatwg.org/#the-h6-element)
  208. 1 register_element def h6(**attributes, &content) = nil
  209. # Outputs a `<head>` tag.
  210. #
  211. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/head)
  212. # [Spec](https://html.spec.whatwg.org/#the-head-element)
  213. 1 register_element def head(**attributes, &content) = nil
  214. # Outputs a `<header>` tag.
  215. #
  216. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/header)
  217. # [Spec](https://html.spec.whatwg.org/#the-header-element)
  218. 1 register_element def header(**attributes, &content) = nil
  219. # Outputs an `<hgroup>` tag.
  220. #
  221. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/hgroup)
  222. # [Spec](https://html.spec.whatwg.org/#the-hgroup-element)
  223. 1 register_element def hgroup(**attributes, &content) = nil
  224. # Outputs an `<html>` tag.
  225. #
  226. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/html)
  227. # [Spec](https://html.spec.whatwg.org/#the-html-element)
  228. 1 register_element def html(**attributes, &content) = nil
  229. # Outputs an `<i>` tag.
  230. #
  231. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/i)
  232. # [Spec](https://html.spec.whatwg.org/#the-i-element)
  233. 1 register_element def i(**attributes, &content) = nil
  234. # Outputs an `<iframe>` tag.
  235. #
  236. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/iframe)
  237. # [Spec](https://html.spec.whatwg.org/#the-iframe-element)
  238. 1 register_element def iframe(**attributes, &content) = nil
  239. # Outputs an `<ins>` tag.
  240. #
  241. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/ins)
  242. # [Spec](https://html.spec.whatwg.org/#the-ins-element)
  243. 1 register_element def ins(**attributes, &content) = nil
  244. # Outputs a `<kbd>` tag.
  245. #
  246. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/kbd)
  247. # [Spec](https://html.spec.whatwg.org/#the-kbd-element)
  248. 1 register_element def kbd(**attributes, &content) = nil
  249. # Outputs a `<label>` tag.
  250. #
  251. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/label)
  252. # [Spec](https://html.spec.whatwg.org/#the-label-element)
  253. 1 register_element def label(**attributes, &content) = nil
  254. # Outputs a `<legend>` tag.
  255. #
  256. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/legend)
  257. # [Spec](https://html.spec.whatwg.org/#the-legend-element)
  258. 1 register_element def legend(**attributes, &content) = nil
  259. # Outputs a `<li>` tag.
  260. #
  261. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/li)
  262. # [Spec](https://html.spec.whatwg.org/#the-li-element)
  263. 1 register_element def li(**attributes, &content) = nil
  264. # Outputs a `<main>` tag.
  265. #
  266. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/main)
  267. # [Spec](https://html.spec.whatwg.org/#the-main-element)
  268. 1 register_element def main(**attributes, &content) = nil
  269. # Outputs a `<map>` tag.
  270. #
  271. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/map)
  272. # [Spec](https://html.spec.whatwg.org/#the-map-element)
  273. 1 register_element def map(**attributes, &content) = nil
  274. # Outputs a `<mark>` tag.
  275. #
  276. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/mark)
  277. # [Spec](https://html.spec.whatwg.org/#the-mark-element)
  278. 1 register_element def mark(**attributes, &content) = nil
  279. # Outputs a `<menu>` tag.
  280. #
  281. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/menu)
  282. # [Spec](https://html.spec.whatwg.org/#the-menu-element)
  283. 1 register_element def menu(**attributes, &content) = nil
  284. # Outputs a `<meter>` tag.
  285. #
  286. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/meter)
  287. # [Spec](https://html.spec.whatwg.org/#the-meter-element)
  288. 1 register_element def meter(**attributes, &content) = nil
  289. # Outputs a `<nav>` tag.
  290. #
  291. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/nav)
  292. # [Spec](https://html.spec.whatwg.org/#the-nav-element)
  293. 1 register_element def nav(**attributes, &content) = nil
  294. # Outputs a `<noscript>` tag.
  295. #
  296. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/noscript)
  297. # [Spec](https://html.spec.whatwg.org/#the-noscript-element)
  298. 1 register_element def noscript(**attributes, &content) = nil
  299. # Outputs an `<object>` tag.
  300. #
  301. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/object)
  302. # [Spec](https://html.spec.whatwg.org/#the-object-element)
  303. 1 register_element def object(**attributes, &content) = nil
  304. # Outputs an `<ol>` tag.
  305. #
  306. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/ol)
  307. # [Spec](https://html.spec.whatwg.org/#the-ol-element)
  308. 1 register_element def ol(**attributes, &content) = nil
  309. # Outputs an `<optgroup>` tag.
  310. #
  311. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/optgroup)
  312. # [Spec](https://html.spec.whatwg.org/#the-optgroup-element)
  313. 1 register_element def optgroup(**attributes, &content) = nil
  314. # Outputs an `<option>` tag.
  315. #
  316. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/option)
  317. # [Spec](https://html.spec.whatwg.org/#the-option-element)
  318. 1 register_element def option(**attributes, &content) = nil
  319. # Outputs an `<output>` tag.
  320. #
  321. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/output)
  322. # [Spec](https://html.spec.whatwg.org/#the-output-element)
  323. 1 register_element def output(**attributes, &content) = nil
  324. # Outputs a `<p>` tag.
  325. #
  326. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/p)
  327. # [Spec](https://html.spec.whatwg.org/#the-p-element)
  328. 1 register_element def p(**attributes, &content) = nil
  329. # Outputs a `<picture>` tag.
  330. #
  331. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/picture)
  332. # [Spec](https://html.spec.whatwg.org/#the-picture-element)
  333. # [Can I Use?](https://caniuse.com/picture)
  334. 1 register_element def picture(**attributes, &content) = nil
  335. # Outputs a `<pre>` tag.
  336. #
  337. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/pre)
  338. # [Spec](https://html.spec.whatwg.org/#the-pre-element)
  339. 1 register_element def pre(**attributes, &content) = nil
  340. # Outputs a `<progress>` tag.
  341. #
  342. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/progress)
  343. # [Spec](https://html.spec.whatwg.org/#the-progress-element)
  344. 1 register_element def progress(**attributes, &content) = nil
  345. # Outputs a `<q>` tag.
  346. #
  347. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/q)
  348. # [Spec](https://html.spec.whatwg.org/#the-q-element)
  349. 1 register_element def q(**attributes, &content) = nil
  350. # Outputs an `<rp>` tag.
  351. #
  352. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/rp)
  353. # [Spec](https://html.spec.whatwg.org/#the-rp-element)
  354. 1 register_element def rp(**attributes, &content) = nil
  355. # Outputs an `<rt>` tag.
  356. #
  357. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/rt)
  358. # [Spec](https://html.spec.whatwg.org/#the-rt-element)
  359. 1 register_element def rt(**attributes, &content) = nil
  360. # Outputs a `<ruby>` tag. (The best tag ever!)
  361. #
  362. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/ruby)
  363. # [Spec](https://html.spec.whatwg.org/#the-ruby-element)
  364. 1 register_element def ruby(**attributes, &content) = nil
  365. # Outputs an `<s>` tag.
  366. #
  367. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/s)
  368. # [Spec](https://html.spec.whatwg.org/#the-s-element)
  369. 1 register_element def s(**attributes, &content) = nil
  370. # Outputs a `<samp>` tag.
  371. #
  372. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/samp)
  373. # [Spec](https://html.spec.whatwg.org/#the-samp-element)
  374. 1 register_element def samp(**attributes, &content) = nil
  375. # Outputs a `<script>` tag.
  376. #
  377. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/script)
  378. # [Spec](https://html.spec.whatwg.org/#the-script-element)
  379. 1 register_element def script(**attributes, &content) = nil
  380. # Outputs a `<search>` tag.
  381. #
  382. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/search)
  383. # [Spec](https://html.spec.whatwg.org/#the-search-element)
  384. # [Can I Use?](https://caniuse.com/mdn-html_elements_search)
  385. 1 register_element def search(**attributes, &content) = nil
  386. # Outputs a `<section>` tag.
  387. #
  388. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/section)
  389. # [Spec](https://html.spec.whatwg.org/#the-section-element)
  390. 1 register_element def section(**attributes, &content) = nil
  391. # Outputs a `<select>` tag.
  392. #
  393. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/select)
  394. # [Spec](https://html.spec.whatwg.org/#the-select-element)
  395. 1 register_element def select(**attributes, &content) = nil
  396. # [EXPERIMENTAL] Outputs a `<selectedcontent>` tag.
  397. #
  398. # [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/selectedcontent)
  399. # [Draft Spec](https://github.com/whatwg/html/pull/10633)
  400. # [Can I Use?](https://caniuse.com/mdn-html_elements_selectedcontent)
  401. 1 register_element def selectedcontent(**attributes, &content) = nil
  402. # Outputs a `<slot>` tag.
  403. #
  404. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/slot)
  405. # [Spec](https://html.spec.whatwg.org/#the-slot-element)
  406. 1 register_element def slot(**attributes, &content) = nil
  407. # Outputs a `<small>` tag.
  408. #
  409. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/small)
  410. # [Spec](https://html.spec.whatwg.org/#the-small-element)
  411. 1 register_element def small(**attributes, &content) = nil
  412. # Outputs a `<span>` tag.
  413. #
  414. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/span)
  415. # [Spec](https://html.spec.whatwg.org/#the-span-element)
  416. 1 register_element def span(**attributes, &content) = nil
  417. # Outputs a `<strong>` tag.
  418. #
  419. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/strong)
  420. # [Spec](https://html.spec.whatwg.org/#the-strong-element)
  421. 1 register_element def strong(**attributes, &content) = nil
  422. # Outputs a `<style>` tag.
  423. #
  424. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/style)
  425. # [Spec](https://html.spec.whatwg.org/#the-style-element)
  426. 1 register_element def style(**attributes, &content) = nil
  427. # Outputs a `<sub>` tag.
  428. #
  429. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/sub)
  430. # [Spec](https://html.spec.whatwg.org/#the-sub-element)
  431. 1 register_element def sub(**attributes, &content) = nil
  432. # Outputs a `<summary>` tag.
  433. #
  434. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/summary)
  435. # [Spec](https://html.spec.whatwg.org/#the-summary-element)
  436. # [Can I Use?](https://caniuse.com/details)
  437. 1 register_element def summary(**attributes, &content) = nil
  438. # Outputs a `<sup>` tag.
  439. #
  440. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/sup)
  441. # [Spec](https://html.spec.whatwg.org/#the-sup-element)
  442. 1 register_element def sup(**attributes, &content) = nil
  443. # Outputs an `<svg>` tag.
  444. #
  445. # [MDN Docs](https://developer.mozilla.org/docs/Web/SVG/Element/svg)
  446. # [Spec](https://html.spec.whatwg.org/#the-svg-element)
  447. 1 register_element def svg(**attributes, &content) = nil
  448. # Outputs a `<table>` tag.
  449. #
  450. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/table)
  451. # [Spec](https://html.spec.whatwg.org/#the-table-element)
  452. 1 register_element def table(**attributes, &content) = nil
  453. # Outputs a `<tbody>` tag.
  454. #
  455. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/tbody)
  456. # [Spec](https://html.spec.whatwg.org/#the-tbody-element)
  457. 1 register_element def tbody(**attributes, &content) = nil
  458. # Outputs a `<td>` tag.
  459. #
  460. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/td)
  461. # [Spec](https://html.spec.whatwg.org/#the-td-element)
  462. 1 register_element def td(**attributes, &content) = nil
  463. # Outputs a `<template>` tag.
  464. #
  465. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/template)
  466. # [Spec](https://html.spec.whatwg.org/#the-template-element)
  467. 1 register_element def template(**attributes, &content) = nil
  468. # Outputs a `<textarea>` tag.
  469. #
  470. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/textarea)
  471. # [Spec](https://html.spec.whatwg.org/#the-textarea-element)
  472. 1 register_element def textarea(**attributes, &content) = nil
  473. # Outputs a `<tfoot>` tag.
  474. #
  475. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/tfoot)
  476. # [Spec](https://html.spec.whatwg.org/#the-tfoot-element)
  477. 1 register_element def tfoot(**attributes, &content) = nil
  478. # Outputs a `<th>` tag.
  479. #
  480. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/th)
  481. # [Spec](https://html.spec.whatwg.org/#the-th-element)
  482. 1 register_element def th(**attributes, &content) = nil
  483. # Outputs a `<thead>` tag.
  484. #
  485. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/thead)
  486. # [Spec](https://html.spec.whatwg.org/#the-thead-element)
  487. 1 register_element def thead(**attributes, &content) = nil
  488. # Outputs a `<time>` tag.
  489. #
  490. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/time)
  491. # [Spec](https://html.spec.whatwg.org/#the-time-element)
  492. 1 register_element def time(**attributes, &content) = nil
  493. # Outputs a `<title>` tag.
  494. #
  495. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/title)
  496. # [Spec](https://html.spec.whatwg.org/#the-title-element)
  497. 1 register_element def title(**attributes, &content) = nil
  498. # Outputs a `<tr>` tag.
  499. #
  500. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/tr)
  501. # [Spec](https://html.spec.whatwg.org/#the-tr-element)
  502. 1 register_element def tr(**attributes, &content) = nil
  503. # Outputs a `<u>` tag.
  504. #
  505. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/u)
  506. # [Spec](https://html.spec.whatwg.org/#the-u-element)
  507. 1 register_element def u(**attributes, &content) = nil
  508. # Outputs a `<ul>` tag.
  509. #
  510. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/ul)
  511. # [Spec](https://html.spec.whatwg.org/#the-ul-element)
  512. 1 register_element def ul(**attributes, &content) = nil
  513. # Outputs a `<var>` tag.
  514. #
  515. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/var)
  516. # [Spec](https://html.spec.whatwg.org/#the-var-element)
  517. 1 register_element def var(**attributes, &content) = nil
  518. # Outputs a `<video>` tag.
  519. #
  520. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/video)
  521. # [Spec](https://html.spec.whatwg.org/#the-video-element)
  522. # [Can I Use?](https://caniuse.com/video)
  523. 1 register_element def video(**attributes, &content) = nil
  524. # Outputs a `<wbr>` tag.
  525. #
  526. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/wbr)
  527. # [Spec](https://html.spec.whatwg.org/#the-wbr-element)
  528. 1 register_element def wbr(**attributes, &content) = nil
  529. end

lib/phlex/html/void_elements.rb

100.0% lines covered

100.0% branches covered

14 relevant lines. 14 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. # Void HTML elements don't accept content and never have a closing tag.
  3. 1 module Phlex::HTML::VoidElements
  4. 1 extend Phlex::SGML::Elements
  5. # Outputs an `<area>` tag.
  6. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/area)
  7. # [Spec](https://html.spec.whatwg.org/#the-area-element)
  8. 1 __register_void_element__ def area(**attributes) = nil
  9. # Outputs a `<base>` tag.
  10. # [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
  11. # [Spec](https://html.spec.whatwg.org/#the-base-element)
  12. 1 __register_void_element__ def base(**attributes) = nil
  13. # Outputs a `<br>` tag.
  14. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/br)
  15. # [Spec](https://html.spec.whatwg.org/#the-br-element)
  16. 1 __register_void_element__ def br(**attributes) = nil
  17. # Outputs a `<col>` tag.
  18. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/col)
  19. # [Spec](https://html.spec.whatwg.org/#the-col-element)
  20. 1 __register_void_element__ def col(**attributes) = nil
  21. # Outputs an `<embed>` tag.
  22. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/embed)
  23. # [Spec](https://html.spec.whatwg.org/#the-embed-element)
  24. 1 __register_void_element__ def embed(**attributes) = nil
  25. # Outputs an `<hr>` tag.
  26. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/hr)
  27. # [Spec](https://html.spec.whatwg.org/#the-hr-element)
  28. 1 __register_void_element__ def hr(**attributes) = nil
  29. # Outputs an `<img>` tag.
  30. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/img)
  31. # [Spec](https://html.spec.whatwg.org/#the-img-element)
  32. 1 __register_void_element__ def img(**attributes) = nil
  33. # Outputs an `<input>` tag.
  34. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/input)
  35. # [Spec](https://html.spec.whatwg.org/#the-input-element)
  36. 1 __register_void_element__ def input(**attributes) = nil
  37. # Outputs a `<link>` tag.
  38. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/link)
  39. # [Spec](https://html.spec.whatwg.org/#the-link-element)
  40. 1 __register_void_element__ def link(**attributes) = nil
  41. # Outputs a `<meta>` tag.
  42. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/meta)
  43. # [Spec](https://html.spec.whatwg.org/#the-meta-element)
  44. 1 __register_void_element__ def meta(**attributes) = nil
  45. # Outputs a `<source>` tag.
  46. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/source)
  47. # [Spec](https://html.spec.whatwg.org/#the-source-element)
  48. 1 __register_void_element__ def source(**attributes) = nil
  49. # Outputs a `<track>` tag.
  50. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/track)
  51. # [Spec](https://html.spec.whatwg.org/#the-track-element)
  52. 1 __register_void_element__ def track(**attributes) = nil
  53. end

lib/phlex/kit.rb

85.0% lines covered

66.67% branches covered

40 relevant lines. 34 lines covered and 6 lines missed.
15 total branches, 10 branches covered and 5 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::Kit
  3. 1 module LazyLoader
  4. 1 def method_missing(name, ...)
  5. 1 mod = self.class
  6. 1 then: 1 if name[0] == name[0].upcase && mod.constants.include?(name) && mod.const_get(name) && methods.include?(name)
  7. 1 public_send(name, ...)
  8. else: 0 else
  9. super
  10. end
  11. end
  12. 1 def respond_to_missing?(name, include_private = false)
  13. mod = self.class
  14. (name[0] == name[0].upcase && mod.constants.include?(name) && mod.const_get(name) && methods.include?(name)) || super
  15. end
  16. end
  17. 1 def self.extended(mod)
  18. 3 case mod
  19. when: 0 when Class
  20. raise Phlex::ArgumentError.new(<<~MESSAGE)
  21. `Phlex::Kit` was extended into #{mod.name}.
  22. You should only extend modules with `Phlex::Kit` as it is not compatible with classes.
  23. MESSAGE
  24. else: 3 else
  25. 3 mod.include(LazyLoader)
  26. end
  27. end
  28. 1 def method_missing(name, ...)
  29. 1 then: 1 if name[0] == name[0].upcase && constants.include?(name) && const_get(name) && methods.include?(name)
  30. 1 public_send(name, ...)
  31. else: 0 else
  32. super
  33. end
  34. end
  35. 1 def respond_to_missing?(name, include_private = false)
  36. (name[0] == name[0].upcase && constants.include?(name) && const_get(name) && methods.include?(name)) || super
  37. end
  38. 1 def const_added(name)
  39. 8 then: 2 else: 6 return if autoload?(name)
  40. 6 me = self
  41. 6 constant = const_get(name)
  42. 6 else: 0 case constant
  43. when: 5 when Class
  44. 5 then: 5 else: 0 if constant < Phlex::SGML
  45. 5 constant.include(me)
  46. 5 constant = nil
  47. 5 define_method(name) do |*args, **kwargs, &block|
  48. 5 constant = me.const_get(name)
  49. 5 render(constant.new(*args, **kwargs), &block)
  50. end
  51. 5 define_singleton_method(name) do |*args, **kwargs, &block|
  52. 3 component, fiber_id = Thread.current[:__phlex_component__]
  53. 3 then: 2 if (component && fiber_id == Fiber.current.object_id)
  54. 2 component.instance_exec do
  55. 2 constant = me.const_get(name)
  56. 2 render(constant.new(*args, **kwargs), &block)
  57. end
  58. else: 1 else
  59. 1 raise "You can't call `#{name}' outside of a Phlex rendering context."
  60. end
  61. end
  62. end
  63. when: 1 when Module
  64. 1 constant.extend(Phlex::Kit)
  65. end
  66. 6 super
  67. end
  68. end

lib/phlex/sgml.rb

84.14% lines covered

71.13% branches covered

227 relevant lines. 191 lines covered and 36 lines missed.
97 total branches, 69 branches covered and 28 branches missed.
    
  1. # frozen_string_literal: true
  2. # **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
  3. 1 class Phlex::SGML
  4. 1 ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
  5. 1 compiler.pre_cmd = [""]
  6. 1 compiler.put_cmd = "@_state.buffer.<<"
  7. 1 compiler.insert_cmd = "__implicit_output__"
  8. 1 compiler.post_cmd = ["nil"]
  9. 1 def compiler.add_insert_cmd(out, content)
  10. 4 out.push("#{@insert_cmd}((#{content}))")
  11. end
  12. end
  13. 1 include Phlex::Helpers
  14. 1 class << self
  15. # Render the view to a String. Arguments are delegated to {.new}.
  16. 1 def call(...)
  17. 1360 new(...).call
  18. end
  19. # Create a new instance of the component.
  20. # @note The block will not be delegated to {#initialize}. Instead, it will be sent to {#view_template} when rendering.
  21. 1 def new(*a, **k, &block)
  22. 1605 then: 360 if block
  23. 360 object = super(*a, **k, &nil)
  24. 720 object.instance_exec { @_content_block = block }
  25. 360 object
  26. else: 1245 else
  27. 1245 super
  28. end
  29. end
  30. 1 def erb(method_name, erb = nil, locals: nil, &block)
  31. 2 loc = caller_locations(1, 1)[0]
  32. 2 path = loc.path.delete_suffix(".rb")
  33. 2 file = loc.path
  34. 2 line = loc.lineno - 1
  35. 2 else: 2 then: 0 unless erb
  36. method_path = "#{path}/#{method_name}.html.erb"
  37. sidecar_path = "#{path}.html.erb"
  38. then: 0 if File.exist?(method_path)
  39. erb = File.read(method_path)
  40. file = method_path
  41. else: 0 line = 1
  42. then: 0 elsif method_name == :view_template && File.exist?(sidecar_path)
  43. erb = File.read(sidecar_path)
  44. file = sidecar_path
  45. line = 1
  46. else: 0 else
  47. raise Phlex::RuntimeError.new(<<~MESSAGE)
  48. No ERB template found for #{method_name}
  49. MESSAGE
  50. end
  51. end
  52. 2 code, _enc = ERBCompiler.compile(erb)
  53. 2 class_eval(<<~RUBY, file, line)
  54. def #{method_name} #{locals}
  55. #{code}
  56. end
  57. RUBY
  58. end
  59. end
  60. 1 def view_template
  61. 179 then: 176 if block_given?
  62. 176 yield
  63. else: 3 else
  64. 3 plain "Phlex Warning: Your `#{self.class.name}` class doesn't define a `view_template` method. If you are upgrading to Phlex 2.x make sure to rename your `template` method to `view_template`. See: https://beta.phlex.fun/guides/v2-upgrade.html"
  65. end
  66. end
  67. 1 def to_proc
  68. 2 proc { |c| c.render(self) }
  69. end
  70. 1 def call(buffer = +"", context: {}, fragments: nil, &)
  71. 1582 state = Phlex::SGML::State.new(
  72. user_context: context,
  73. output_buffer: buffer,
  74. then: 14 else: 1568 fragments: fragments&.to_set,
  75. )
  76. 1582 internal_call(parent: nil, state:, &)
  77. 1535 state.output_buffer << state.buffer
  78. end
  79. 1 def internal_call(parent: nil, state: nil, &block)
  80. 1603 then: 1 else: 1602 if @_state
  81. 1 raise Phlex::DoubleRenderError.new(
  82. "You can't render a #{self.class.name} more than once."
  83. )
  84. end
  85. 1602 @_state = state
  86. 1602 else: 1600 then: 2 return "" unless render?
  87. 1600 block ||= @_content_block
  88. 1600 Thread.current[:__phlex_component__] = [self, Fiber.current.object_id].freeze
  89. 1600 state.around_render(self) do
  90. 1600 before_template(&block)
  91. 1600 around_template do
  92. 1600 then: 534 if block
  93. 534 view_template do |*args|
  94. 522 then: 344 if args.length > 0
  95. 344 __yield_content_with_args__(*args, &block)
  96. else: 178 else
  97. 178 __yield_content__(&block)
  98. end
  99. end
  100. else: 1066 else
  101. 1066 view_template
  102. end
  103. end
  104. 1548 after_template(&block)
  105. end
  106. ensure
  107. 1603 Thread.current[:__phlex_component__] = [parent, Fiber.current.object_id].freeze
  108. end
  109. 1 def context
  110. 6 then: 5 if rendering?
  111. 5 @_state.user_context
  112. else: 1 else
  113. 1 raise Phlex::ArgumentError.new(<<~MESSAGE)
  114. You can’t access the context before the component has started rendering.
  115. MESSAGE
  116. end
  117. end
  118. # Returns `false` before rendering and `true` once the component has started rendering.
  119. # It will not reset back to false after rendering.
  120. 1 def rendering?
  121. 6 !!@_state
  122. end
  123. # Output plain text.
  124. 1 def plain(content)
  125. 22 else: 21 then: 1 unless __text__(content)
  126. 1 raise Phlex::ArgumentError.new("You've passed an object to plain that is not handled by format_object. See https://rubydoc.info/gems/phlex/Phlex/SGML#format_object-instance_method for more information")
  127. end
  128. 21 nil
  129. end
  130. # Output a single space character. If a block is given, a space will be output before and after the block.
  131. 1 def whitespace(&)
  132. 8 state = @_state
  133. 8 else: 4 then: 4 return unless state.should_render?
  134. 4 buffer = state.buffer
  135. 4 buffer << " "
  136. 4 then: 2 else: 2 if block_given?
  137. 2 __yield_content__(&)
  138. 2 buffer << " "
  139. end
  140. 4 nil
  141. end
  142. # Wrap the output in an HTML comment.
  143. #
  144. # [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Comments)
  145. 1 def comment(&)
  146. 12 state = @_state
  147. 12 else: 4 then: 8 return unless state.should_render?
  148. 4 buffer = state.buffer
  149. 4 buffer << "<!-- "
  150. 4 __yield_content__(&)
  151. 4 buffer << " -->"
  152. 4 nil
  153. end
  154. # Output the given safe object as-is. You may need to use `safe` to mark a string as a safe object.
  155. 1 def raw(content)
  156. 6 case content
  157. when: 3 when Phlex::SGML::SafeObject
  158. 3 state = @_state
  159. 3 else: 3 then: 0 return unless state.should_render?
  160. 3 when: 2 state.buffer << content.to_s
  161. when nil, "" # do nothing
  162. else: 1 else
  163. 1 raise Phlex::ArgumentError.new("You passed an unsafe object to `raw`.")
  164. end
  165. 5 nil
  166. end
  167. # Capture the output of the block and returns it as a string.
  168. 1 def capture(*args, &block)
  169. 5 else: 5 then: 0 return "" unless block
  170. 5 then: 1 if args.length > 0
  171. 2 @_state.capture { __yield_content_with_args__(*args, &block) }
  172. else: 4 else
  173. 8 @_state.capture { __yield_content__(&block) }
  174. end
  175. end
  176. # Define a named fragment that can be selectively rendered.
  177. 1 def fragment(name)
  178. 39 state = @_state
  179. 39 state.begin_fragment(name)
  180. 39 yield
  181. 39 state.end_fragment(name)
  182. 33 nil
  183. end
  184. # Mark the given string as safe for HTML output.
  185. 1 def safe(value)
  186. 8 case value
  187. when: 7 when String
  188. 7 Phlex::SGML::SafeValue.new(value)
  189. else: 1 else
  190. 1 raise Phlex::ArgumentError.new("Expected a String.")
  191. end
  192. end
  193. 1 alias_method :🦺, :safe
  194. # Flush the current state to the output buffer.
  195. 1 def flush
  196. 6 @_state.flush
  197. end
  198. 1 def render(renderable = nil, &)
  199. 23 case renderable
  200. when: 21 when Phlex::SGML
  201. 21 renderable.internal_call(state: @_state, parent: self, &)
  202. when: 1 when Class
  203. 1 then: 1 else: 0 if renderable < Phlex::SGML
  204. 1 render(renderable.new, &)
  205. end
  206. when: 0 when Enumerable
  207. renderable.each { |r| render(r, &) }
  208. when: 0 when Proc, Method
  209. then: 0 if renderable.arity == 0
  210. __yield_content_with_no_yield_args__(&renderable)
  211. else: 0 else
  212. __yield_content__(&renderable)
  213. end
  214. when: 0 when String
  215. plain(renderable)
  216. when: 1 when nil
  217. 1 then: 1 else: 0 __yield_content__(&) if block_given?
  218. else: 0 else
  219. raise Phlex::ArgumentError.new("You can't render a #{renderable.inspect}.")
  220. end
  221. 23 nil
  222. end
  223. # Cache a block of content.
  224. #
  225. # ```ruby
  226. # @products.each do |product|
  227. # cache product do
  228. # h1 { product.name }
  229. # end
  230. # end
  231. # ```
  232. 1 def cache(*cache_key, **, &content)
  233. 25 location = caller_locations(1, 1)[0]
  234. full_key = [
  235. 25 app_version_key, # invalidates the key when deploying new code in case of changes
  236. self.class.name, # prevents collisions between classes
  237. 25 then: 0 else: 25 (self.class.object_id if enable_cache_reloading?), # enables reloading
  238. location.base_label, # prevents collisions between different methods
  239. location.lineno, # prevents collisions between different lines
  240. cache_key, # allows for custom cache keys
  241. ].freeze
  242. 25 low_level_cache(full_key, **, &content)
  243. 25 nil
  244. end
  245. # Cache a block of content where you control the entire cache key.
  246. # If you really know what you’re doing and want to take full control
  247. # and responsibility for the cache key, use this method.
  248. #
  249. # ```ruby
  250. # low_level_cache([Commonmarker::VERSION, Digest::MD5.hexdigest(@content)]) do
  251. # markdown(@content)
  252. # end
  253. # ```
  254. #
  255. # Note: To allow you more control, this method does not take a splat of cache keys.
  256. # If you need to pass multiple cache keys, you should pass an array.
  257. 1 def low_level_cache(cache_key, **options, &content)
  258. 25 state = @_state
  259. 44 cached_buffer, fragment_map = cache_store.fetch(cache_key, **options) { state.caching(&content) }
  260. 25 then: 18 if state.should_render?
  261. 18 fragment_map.each do |fragment_name, (offset, length, nested_fragments)|
  262. 38 state.record_fragment(fragment_name, offset, length, nested_fragments)
  263. end
  264. 18 state.buffer << cached_buffer
  265. else: 7 else
  266. 7 fragment_map.each do |fragment_name, (offset, length, nested_fragments)|
  267. 21 then: 7 else: 14 if state.fragments.include?(fragment_name)
  268. 7 state.fragments.delete(fragment_name)
  269. 7 state.fragments.subtract(nested_fragments)
  270. 7 state.buffer << cached_buffer.byteslice(offset, length)
  271. end
  272. end
  273. end
  274. 25 nil
  275. end
  276. 1 def json_escape(string)
  277. ERB::Util.json_escape(string)
  278. end
  279. # Override this method to use a different deployment key.
  280. 1 private def app_version_key
  281. 25 Phlex::DEPLOYED_AT
  282. end
  283. # Override this method to use a different cache store.
  284. 1 private def cache_store
  285. raise "Cache store not implemented."
  286. end
  287. 1 private def enable_cache_reloading?
  288. 25 false
  289. end
  290. 1 private def vanish(...)
  291. capture(...)
  292. nil
  293. end
  294. 1 private def render?
  295. 1598 true
  296. end
  297. 1 private def format_object(object)
  298. 29 else: 3 case object
  299. when: 26 when Float, Integer
  300. 26 object.to_s
  301. end
  302. end
  303. 1 private def around_template
  304. 1600 yield
  305. 1548 nil
  306. end
  307. 1 private def before_template
  308. 1599 nil
  309. end
  310. 1 private def after_template
  311. 1547 nil
  312. end
  313. 1 private def __yield_content__
  314. 524 else: 524 then: 0 return unless block_given?
  315. 524 buffer = @_state.buffer
  316. 524 original_length = buffer.bytesize
  317. 524 content = yield(self)
  318. 492 then: 10 else: 482 __implicit_output__(content) if original_length == buffer.bytesize
  319. 492 nil
  320. end
  321. 1 private def __yield_content_with_no_yield_args__
  322. else: 0 then: 0 return unless block_given?
  323. buffer = @_state.buffer
  324. original_length = buffer.bytesize
  325. content = yield # <-- doesn’t yield self 😉
  326. then: 0 else: 0 __implicit_output__(content) if original_length == buffer.bytesize
  327. nil
  328. end
  329. 1 private def __yield_content_with_args__(*a)
  330. 345 else: 345 then: 0 return unless block_given?
  331. 345 buffer = @_state.buffer
  332. 345 original_length = buffer.bytesize
  333. 345 content = yield(*a)
  334. 345 then: 338 else: 7 __implicit_output__(content) if original_length == buffer.bytesize
  335. 345 nil
  336. end
  337. 1 private def __implicit_output__(content)
  338. 352 state = @_state
  339. 352 else: 349 then: 3 return true unless state.should_render?
  340. 349 case content
  341. when: 1 when Phlex::SGML::SafeObject
  342. 1 state.buffer << content.to_s
  343. when: 346 when String
  344. 346 state.buffer << Phlex::Escape.html_escape(content)
  345. when: 0 when Symbol
  346. state.buffer << Phlex::Escape.html_escape(content.name)
  347. when: 2 when nil
  348. nil
  349. else: 0 else
  350. then: 0 if (formatted_object = format_object(content))
  351. state.buffer << Phlex::Escape.html_escape(formatted_object)
  352. else: 0 else
  353. return false
  354. end
  355. end
  356. 349 true
  357. end
  358. # same as __implicit_output__ but escapes even `safe` objects
  359. 1 private def __text__(content)
  360. 22 state = @_state
  361. 22 else: 21 then: 1 return true unless state.should_render?
  362. 21 case content
  363. when: 16 when String
  364. 16 state.buffer << Phlex::Escape.html_escape(content)
  365. when: 1 when Symbol
  366. 1 state.buffer << Phlex::Escape.html_escape(content.name)
  367. when: 1 when nil
  368. nil
  369. else: 3 else
  370. 3 then: 2 if (formatted_object = format_object(content))
  371. 2 state.buffer << Phlex::Escape.html_escape(formatted_object)
  372. else: 1 else
  373. 1 return false
  374. end
  375. end
  376. 20 true
  377. end
  378. 1 private def __render_attributes__(attributes)
  379. state = @_state
  380. else: 0 then: 0 return unless state.should_render?
  381. state.buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
  382. end
  383. 1 private_class_method def self.method_added(method_name)
  384. 729 then: 695 else: 34 if method_name == :view_template
  385. 695 location = instance_method(method_name).source_location[0]
  386. 695 then: 694 else: 1 if location[0] in "/" | "."
  387. 694 Phlex.__expand_attribute_cache__(location)
  388. end
  389. end
  390. 729 super
  391. end
  392. 1 def self.__compile__(method_name)
  393. path, line = instance_method(method_name).source_location
  394. Phlex::Compiler::Method.new(self, path, line, method_name).compile
  395. end
  396. end

lib/phlex/sgml/attributes.rb

99.22% lines covered

97.17% branches covered

129 relevant lines. 128 lines covered and 1 lines missed.
106 total branches, 103 branches covered and 3 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::SGML::Attributes
  3. 1 extend self
  4. 1 UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
  5. 1 REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
  6. 1 def generate_attributes(attributes, buffer = +"")
  7. 210 attributes.each do |k, v|
  8. 280 else: 271 then: 9 next unless v
  9. 271 when: 19 name = case k
  10. 19 when: 251 when String then k
  11. 251 else: 1 when Symbol then k.name.tr("_", "-")
  12. 1 else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.")
  13. end
  14. 270 value = case v
  15. when: 18 when true
  16. 18 true
  17. when: 121 when String
  18. 121 v.gsub('"', "&quot;")
  19. when: 13 when Symbol
  20. 13 v.name.tr("_", "-").gsub('"', "&quot;")
  21. when: 14 when Integer, Float
  22. 14 v.to_s
  23. when: 1 when Date
  24. 1 v.iso8601
  25. when: 1 when Time
  26. 1 then: 1 else: 0 v.respond_to?(:iso8601) ? v.iso8601 : v.strftime("%Y-%m-%dT%H:%M:%S%:z")
  27. when: 59 when Hash
  28. 59 case k
  29. when: 10 when :style
  30. 10 generate_styles(v).gsub('"', "&quot;")
  31. else: 49 else
  32. 49 generate_nested_attributes(v, "#{name}-", buffer)
  33. end
  34. when: 25 when Array
  35. 25 case k
  36. when: 8 when :style
  37. 8 generate_styles(v).gsub('"', "&quot;")
  38. else: 17 else
  39. 17 generate_nested_tokens(v)
  40. end
  41. when: 15 when Set
  42. 15 case k
  43. when: 2 when :style
  44. 2 generate_styles(v).gsub('"', "&quot;")
  45. else: 13 else
  46. 13 generate_nested_tokens(v.to_a)
  47. end
  48. when: 2 when Phlex::SGML::SafeObject
  49. 2 v.to_s.gsub('"', "&quot;")
  50. else: 1 else
  51. 1 raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
  52. end
  53. 252 lower_name = name.downcase
  54. 252 else: 2 then: 250 unless Phlex::SGML::SafeObject === v
  55. 250 normalized_name = lower_name.delete("^a-z-")
  56. 250 then: 20 else: 230 if value != true && REF_ATTRIBUTES.include?(normalized_name)
  57. 20 case value
  58. when: 19 when String
  59. 19 else: 13 if value.downcase.delete("^a-z:").start_with?("javascript:")
  60. then: 6 # We just ignore these because they were likely not specified by the developer.
  61. 6 next
  62. end
  63. else: 1 else
  64. 1 raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
  65. end
  66. end
  67. 243 then: 1 else: 242 if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-")
  68. 1 raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  69. end
  70. 242 then: 0 else: 242 if UNSAFE_ATTRIBUTES.include?(normalized_name)
  71. raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  72. end
  73. end
  74. 244 then: 5 else: 239 if name.match?(/[<>&"']/)
  75. 5 raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  76. end
  77. 239 then: 3 else: 236 if lower_name.to_sym == :id && k != :id
  78. 3 raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.")
  79. end
  80. 236 else: 36 case value
  81. when: 12 when true
  82. 12 buffer << " " << name
  83. when: 188 when String
  84. 188 buffer << " " << name << '="' << value << '"'
  85. end
  86. end
  87. 181 buffer
  88. end
  89. # Provides the nested-attributes case for serializing out attributes.
  90. # This allows us to skip many of the checks the `__attributes__` method must perform.
  91. 1 def generate_nested_attributes(attributes, base_name, buffer = +"")
  92. 50 attributes.each do |k, v|
  93. 52 else: 50 then: 2 next unless v
  94. 50 then: 2 if (root_key = (:_ == k))
  95. 2 name = ""
  96. 2 original_base_name = base_name
  97. 2 base_name = base_name.delete_suffix("-")
  98. else: 48 else
  99. 48 when: 8 name = case k
  100. 8 when: 39 when String then k
  101. 39 else: 1 when Symbol then k.name.tr("_", "-")
  102. 1 else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
  103. end
  104. 47 then: 10 else: 37 if name.match?(/[<>&"']/)
  105. 10 raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  106. end
  107. end
  108. 39 case v
  109. when: 3 when true
  110. 3 buffer << " " << base_name << name
  111. when: 15 when String
  112. 15 buffer << " " << base_name << name << '="' << v.gsub('"', "&quot;") << '"'
  113. when: 6 when Symbol
  114. 6 buffer << " " << base_name << name << '="' << v.name.tr("_", "-").gsub('"', "&quot;") << '"'
  115. when: 10 when Integer, Float
  116. 10 buffer << " " << base_name << name << '="' << v.to_s << '"'
  117. when: 1 when Hash
  118. 1 generate_nested_attributes(v, "#{base_name}#{name}-", buffer)
  119. when: 1 when Array
  120. 1 buffer << " " << base_name << name << '="' << generate_nested_tokens(v) << '"'
  121. when: 1 when Set
  122. 1 buffer << " " << base_name << name << '="' << generate_nested_tokens(v.to_a) << '"'
  123. when: 1 when Phlex::SGML::SafeObject
  124. 1 buffer << " " << base_name << name << '="' << v.to_s.gsub('"', "&quot;") << '"'
  125. else: 1 else
  126. 1 raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
  127. end
  128. 38 then: 2 else: 36 if root_key
  129. 2 base_name = original_base_name
  130. end
  131. 38 buffer
  132. end
  133. end
  134. 1 def generate_nested_tokens(tokens, sep = " ", gsub_from = nil, gsub_to = "")
  135. 55 buffer = +""
  136. 55 i, length = 0, tokens.length
  137. 55 body: 114 while i < length
  138. 114 token = tokens[i]
  139. 114 case token
  140. when: 46 when String
  141. 46 then: 23 else: 23 token = token.gsub(gsub_from, gsub_to) if gsub_from
  142. 46 then: 24 if i > 0
  143. 24 buffer << sep << token
  144. else: 22 else
  145. 22 buffer << token
  146. end
  147. when: 20 when Symbol
  148. 20 then: 14 if i > 0
  149. 14 buffer << sep << token.name.tr("_", "-")
  150. else: 6 else
  151. 6 buffer << token.name.tr("_", "-")
  152. end
  153. when: 30 when Integer, Float, Phlex::SGML::SafeObject
  154. 30 then: 18 if i > 0
  155. 18 buffer << sep << token.to_s
  156. else: 12 else
  157. 12 buffer << token.to_s
  158. end
  159. when: 8 when Array
  160. 8 then: 7 else: 1 if token.length > 0
  161. 7 then: 6 if i > 0
  162. 6 buffer << sep << generate_nested_tokens(token, sep, gsub_from, gsub_to)
  163. else: 1 else
  164. 1 buffer << generate_nested_tokens(token, sep, gsub_from, gsub_to)
  165. end
  166. when: 8 end
  167. when nil
  168. # Do nothing
  169. else: 2 else
  170. 2 raise Phlex::ArgumentError.new("Invalid token type: #{token.class}.")
  171. end
  172. 112 i += 1
  173. end
  174. 53 buffer.gsub('"', "&quot;")
  175. end
  176. # The result is unsafe so should be escaped.
  177. 1 def generate_styles(styles)
  178. 22 else: 0 case styles
  179. when: 10 when Array, Set
  180. 10 styles.filter_map do |s|
  181. 14 case s
  182. when: 5 when String
  183. 5 then: 1 if s == "" || s.end_with?(";")
  184. 1 s
  185. else: 4 else
  186. 4 "#{s};"
  187. end
  188. when: 2 when Phlex::SGML::SafeObject
  189. 2 value = s.to_s
  190. 2 then: 1 else: 1 value.end_with?(";") ? value : "#{value};"
  191. when: 2 when Hash
  192. 2 next generate_styles(s)
  193. when: 4 when nil
  194. 4 next nil
  195. else: 1 else
  196. 1 raise Phlex::ArgumentError.new("Invalid style: #{s.inspect}.")
  197. end
  198. end.join(" ")
  199. when: 12 when Hash
  200. 12 buffer = +""
  201. 12 i = 0
  202. 12 styles.each do |k, v|
  203. 14 prop = case k
  204. when: 1 when String
  205. 1 k
  206. when: 12 when Symbol
  207. 12 k.name.tr("_", "-")
  208. else: 1 else
  209. 1 raise Phlex::ArgumentError.new("Style keys should be Strings or Symbols.")
  210. end
  211. 13 value = case v
  212. when: 5 when String
  213. 5 v
  214. when: 2 when Symbol
  215. 2 v.name.tr("_", "-")
  216. when: 4 when Integer, Float, Phlex::SGML::SafeObject
  217. 4 v.to_s
  218. when: 1 when nil
  219. 1 nil
  220. else: 1 else
  221. 1 raise Phlex::ArgumentError.new("Invalid style value: #{v.inspect}")
  222. end
  223. 12 then: 11 else: 1 if value
  224. 11 then: 9 if i == 0
  225. 9 buffer << prop << ": " << value << ";"
  226. else: 2 else
  227. 2 buffer << " " << prop << ": " << value << ";"
  228. end
  229. end
  230. 12 i += 1
  231. end
  232. 10 buffer
  233. end
  234. end
  235. end

lib/phlex/sgml/elements.rb

94.29% lines covered

27.01% branches covered

70 relevant lines. 66 lines covered and 4 lines missed.
2973 total branches, 803 branches covered and 2170 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::SGML::Elements
  3. 1 COMMA_SEPARATED_TOKENS = {
  4. img: <<~RUBY,
  5. if Array === (srcset_attribute = attributes[:srcset])
  6. attributes[:srcset] = Phlex::SGML::Attributes.generate_nested_tokens(srcset_attribute, ", ", ",", "%2C")
  7. end
  8. RUBY
  9. link: <<~RUBY,
  10. if Array === (media_attribute = attributes[:media])
  11. attributes[:media] = Phlex::SGML::Attributes.generate_nested_tokens(media_attribute, ", ", ",", "%2C")
  12. end
  13. if Array === (sizes_attribute = attributes[:sizes])
  14. attributes[:sizes] = Phlex::SGML::Attributes.generate_nested_tokens(sizes_attribute, ", ", ",", "%2C")
  15. end
  16. if Array === (imagesrcset_attribute = attributes[:imagesrcset])
  17. rel_attribute = attributes[:rel] || attributes["rel"]
  18. as_attribute = attributes[:as] || attributes["as"]
  19. if ("preload" == rel_attribute || :preload == rel_attribute) && ("image" == as_attribute || :image == as_attribute)
  20. attributes[:imagesrcset] = Phlex::SGML::Attributes.generate_nested_tokens(imagesrcset_attribute, ", ", ",", "%2C")
  21. end
  22. end
  23. RUBY
  24. input: <<~RUBY,
  25. if Array === (accept_attribute = attributes[:accept])
  26. type_attribute = attributes[:type] || attributes["type"]
  27. if "file" == type_attribute || :file == type_attribute
  28. attributes[:accept] = Phlex::SGML::Attributes.generate_nested_tokens(accept_attribute, ", ", ",", "%2C")
  29. end
  30. end
  31. RUBY
  32. }.freeze
  33. 1 def __registered_elements__
  34. 1032 @__registered_elements__ ||= {}
  35. end
  36. 1 def register_element(method_name, tag: method_name.name.tr("_", "-"))
  37. 167 class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
  38. # frozen_string_literal: true
  39. 1 def #{method_name}(**attributes)
  40. 2208 state = @_state
  41. 2208 buffer = state.buffer
  42. 2208 block_given = block_given?
  43. 2208 else: 21 then: 3 else: 7 then: 0 else: 4 then: 0 else: 10 then: 3 else: 8 then: 0 else: 4 then: 0 else: 4 then: 0 else: 9 then: 0 else: 4 then: 0 else: 7 then: 0 else: 4 then: 0 else: 4 then: 0 else: 9 then: 3 else: 7 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 3 else: 4 then: 0 else: 11 then: 0 else: 152 then: 9 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 107 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 144 then: 9 else: 6 then: 3 else: 13 then: 0 else: 4 then: 0 else: 8 then: 0 else: 4 then: 0 else: 9 then: 0 else: 13 then: 1 else: 6 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 3 else: 4 then: 0 else: 31 then: 0 else: 7 then: 1 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 5 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 1 else: 4 then: 0 else: 10 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 8 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 6 then: 0 else: 4 then: 0 else: 13 then: 0 else: 5 then: 0 else: 4 then: 0 else: 504 then: 0 else: 10 then: 2 else: 7 then: 0 else: 4 then: 0 else: 8 then: 0 else: 6 then: 0 else: 104 then: 0 else: 4 then: 0 else: 504 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 104 then: 0 else: 4 then: 0 else: 14 then: 0 else: 4 then: 0 else: 4 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 4 then: 0 unless state.should_render?
  44. 20 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 1 then: 4 else: 0 then: 5 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 9 else: 0 then: 0 else: 0 then: 0 else: 0 then: 11 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 1 else: 9 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 2 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 3 else: 6 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 yield(self) if block_given
  45. 15 return nil
  46. end
  47. 2188 then: 15 then: 10 then: 4 then: 4 then: 4 then: 4 then: 8 then: 4 then: 4 then: 5 then: 8 then: 4 then: 4 then: 4 then: 4 then: 4 then: 8 then: 4 then: 4 then: 4 then: 4 then: 141 then: 8 then: 4 then: 4 then: 4 then: 8 then: 4 then: 15 then: 4 then: 4 then: 8 then: 8 then: 4 then: 4 then: 4 then: 4 then: 4 then: 7 then: 4 then: 8 then: 10 then: 9 then: 4 then: 4 then: 4 then: 8 then: 8 then: 24 then: 4 then: 4 then: 8 then: 4 then: 4 then: 13 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 104 then: 4 then: 504 then: 4 then: 4 then: 4 then: 7 then: 4 then: 10 then: 4 then: 4 then: 4 then: 4 then: 6 then: 9 then: 6 then: 3 then: 12 then: 9 then: 3 then: 3 then: 15 then: 3 then: 3 then: 3 if attributes.length > 0 # with attributes
  48. 1369 then: 7 then: 4 then: 4 then: 11 then: 4 then: 4 then: 4 then: 4 then: 4 then: 5 then: 4 then: 4 then: 4 then: 4 then: 11 then: 16 then: 4 then: 4 then: 7 then: 4 then: 16 then: 4 then: 11 then: 4 then: 4 then: 8 then: 8 then: 4 then: 4 then: 4 then: 11 then: 4 then: 4 then: 4 then: 4 then: 4 then: 9 then: 8 then: 4 then: 4 then: 4 then: 7 then: 4 then: 4 then: 4 then: 4 then: 4 then: 8 then: 4 then: 8 then: 4 then: 4 then: 4 then: 5 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 104 then: 4 then: 504 then: 4 then: 4 then: 8 then: 4 then: 4 then: 10 then: 4 then: 4 then: 4 then: 4 then: 3 then: 6 then: 6 then: 18 then: 3 then: 6 then: 3 then: 3 then: 3 then: 3 then: 6 then: 3 if block_given # with content block
  49. 1232 buffer << "<#{tag}"
  50. begin
  51. #{COMMA_SEPARATED_TOKENS[method_name]}
  52. 1232 buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
  53. ensure
  54. 1232 buffer << ">"
  55. end
  56. begin
  57. 1232 original_length = buffer.bytesize
  58. 1232 content = yield(self)
  59. 1232 then: 6 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 7 else: 1 then: 3 else: 1 then: 6 else: 2 then: 3 else: 1 then: 8 else: 12 then: 6 else: 2 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 2 then: 9 else: 3 then: 14 else: 7 then: 7 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 7 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 4 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 2 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 6 else: 2 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 7 else: 1 then: 10 else: 6 then: 3 else: 1 then: 7 else: 1 then: 3 else: 5 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 101 then: 3 else: 1 then: 3 else: 501 then: 3 else: 1 then: 3 else: 1 then: 6 else: 1 then: 3 else: 1 then: 6 else: 1 then: 3 else: 1 then: 6 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 3 then: 6 else: 0 then: 6 else: 0 then: 6 else: 0 then: 12 else: 0 then: 3 else: 0 then: 6 else: 0 then: 3 else: 0 then: 12 else: 0 then: 6 else: 0 then: 3 else: 0 then: 3 else: 0 then: 18 else: 12 then: 15 else: 3 then: 6 else: 3 then: 3 else: 0 then: 9 else: 0 if original_length == buffer.bytesize
  60. 507 case content
  61. when: 0 when: 0 when: 6 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 6 when: 0 when: 0 when: 0 when: 3 when: 3 when: 3 when: 0 when: 0 when: 0 when: 3 when: 3 when: 3 when: 6 when: 3 when: 0 when: 0 when: 6 when: 6 when: 6 when: 0 when: 0 when ::Phlex::SGML::SafeObject
  62. buffer << content.to_s
  63. when: 6 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 6 when: 3 when: 3 when: 4 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 7 when: 3 when: 3 when: 9 when: 3 when: 3 when: 3 when: 3 when: 3 when: 6 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 0 when: 3 when: 3 when: 3 when: 3 when: 6 when: 3 when: 3 when: 3 when: 3 when: 3 when: 6 when: 3 when: 3 when: 6 when: 3 when: 3 when: 3 when: 3 when: 6 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 6 when String
  64. 506 buffer << ::Phlex::Escape.html_escape(content)
  65. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when Symbol
  66. buffer << ::Phlex::Escape.html_escape(content.name)
  67. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when nil
  68. nil
  69. else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 1 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else
  70. 1 then: 8 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 1 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 1 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 6 else: 3 else: 0 then: 3 else: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 then: 6 else: 3 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 3 else: 0 then: 0 else: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 6 else: 0 then: 3 else: 0 then: 12 else: 0 then: 0 else: 0 then: 24 else: 3 else: 0 then: 12 else: 0 then: 15 else: 6 then: 0 else: 0 then: 12 else: 3 else: 0 then: 3 else: 6 else: 0 then: 0 else: 0 then: 3 else: 0 then: 6 else: 0 if (formatted_object = format_object(content))
  71. buffer << ::Phlex::Escape.html_escape(formatted_object)
  72. end
  73. end
  74. end
  75. ensure
  76. 1232 buffer << "</#{tag}>"
  77. end
  78. else: 8 else: 0 else: 0 else: 2 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 1 else: 13 else: 0 else: 0 else: 1 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 1 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 6 else: 3 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 3 else # without content
  79. 137 buffer << "<#{tag}"
  80. begin
  81. #{COMMA_SEPARATED_TOKENS[method_name]}
  82. 137 buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
  83. ensure
  84. 137 buffer << "></#{tag}>"
  85. end
  86. end
  87. else: 0 else: 0 else: 0 else: 4 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 100 else: 0 else: 0 else: 8 else: 0 else: 1 else: 0 else: 0 else: 0 else: 10 else: 133 else: 2 else: 0 else: 2 else: 7 else: 3 else: 2 else: 0 else: 0 else: 0 else: 24 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 15 else: 0 else: 0 else: 0 else: 2 else: 0 else: 0 else: 500 else: 3 else: 0 else: 0 else: 0 else: 2 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 2 else: 100 else: 10 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 3 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else # without attributes
  88. 819 then: 0 then: 0 then: 0 then: 36 then: 8 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 4 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 3 then: 0 then: 1 then: 0 then: 0 then: 0 then: 134 then: 0 then: 1 then: 0 then: 0 then: 0 then: 2 then: 0 then: 0 then: 9 then: 0 then: 0 then: 0 then: 0 then: 24 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 6 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 2 then: 0 then: 9 then: 0 then: 0 then: 0 then: 0 then: 500 then: 0 then: 0 then: 0 then: 0 then: 1 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 6 then: 0 then: 0 then: 10 then: 0 then: 0 then: 0 then: 3 then: 3 then: 3 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 1 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 if block_given # with content block
  89. 812 buffer << "<#{tag}>"
  90. begin
  91. 812 original_length = buffer.bytesize
  92. 812 content = yield(self)
  93. 813 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 9 else: 4 then: 0 else: 0 then: 0 else: 0 then: 0 else: 3 then: 0 else: 0 then: 0 else: 3 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 2 else: 1 then: 0 else: 0 then: 0 else: 0 then: 1 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 2 else: 0 then: 4 else: 1 then: 0 else: 0 then: 4 else: 0 then: 3 else: 1 then: 0 else: 2 then: 0 else: 0 then: 0 else: 0 then: 0 else: 2 then: 10 else: 1 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 24 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 4 else: 0 then: 0 else: 0 then: 3 else: 1 then: 0 else: 0 then: 0 else: 0 then: 5 else: 1 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 1 else: 0 then: 0 else: 0 then: 0 else: 9 then: 0 else: 0 then: 500 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 1 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 20 else: 3 then: 6 else: 0 then: 0 else: 0 then: 0 else: 0 then: 6 else: 0 then: 3 else: 0 then: 3 else: 0 then: 0 else: 3 then: 3 else: 0 then: 6 else: 0 then: 0 else: 0 then: 3 else: 0 then: 6 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 1 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 if original_length == buffer.bytesize
  94. 681 case content
  95. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 132 when: 0 when: 0 when: 4 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 3 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 8 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 4 when: 0 when: 0 when: 0 when: 1 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 4 when: 6 when: 0 when: 0 when: 0 when: 15 when: 3 when: 0 when: 15 when: 6 when: 0 when: 3 when: 6 when: 6 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when ::Phlex::SGML::SafeObject
  96. 1 buffer << content.to_s
  97. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 4 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 1 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 132 when: 2 when: 0 when: 4 when: 3 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 5 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 500 when: 3 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 2 when: 0 when: 0 when: 0 when: 6 when: 0 when: 0 when: 3 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when String
  98. 653 buffer << ::Phlex::Escape.html_escape(content)
  99. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when Symbol
  100. buffer << ::Phlex::Escape.html_escape(content.name)
  101. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 2 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when nil
  102. nil
  103. else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 24 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 1 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else
  104. 25 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 3 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 24 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 9 else: 4 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 6 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 3 else: 0 else: 0 then: 6 else: 0 then: 12 else: 4 then: 0 else: 6 then: 0 else: 0 then: 3 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 if (formatted_object = format_object(content))
  105. 24 buffer << ::Phlex::Escape.html_escape(formatted_object)
  106. end
  107. end
  108. end
  109. ensure
  110. 812 buffer << "</#{tag}>"
  111. end
  112. else: 0 else: 0 else: 0 else: 125 else: 0 else: 0 else: 0 else: 3 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 5 else: 0 else: 0 else: 0 else: 0 else: 0 else: 1 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 1 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 3 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else # without content
  113. 7 buffer << "<#{tag}></#{tag}>"
  114. end
  115. end
  116. 6 then: 1 else: 166 #{'flush' if tag == 'head'}
  117. 2158 nil
  118. end
  119. RUBY
  120. 167 __registered_elements__[method_name] = tag
  121. 167 method_name
  122. end
  123. 1 def __register_void_element__(method_name, tag: method_name.name.tr("_", "-"))
  124. 12 class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
  125. # frozen_string_literal: true
  126. 1 def #{method_name}(**attributes)
  127. 68 state = @_state
  128. 68 else: 25 then: 4 else: 17 then: 0 else: 10 then: 0 else: 16 then: 0 return unless state.should_render?
  129. 64 buffer = state.buffer
  130. 64 then: 3 then: 12 then: 4 then: 3 then: 3 then: 11 then: 14 then: 13 then: 4 then: 3 if attributes.length > 0 # with attributes
  131. 61 buffer << "<#{tag}"
  132. begin
  133. 32 then: 3 else: 8 then: 7 else: 14 #{COMMA_SEPARATED_TOKENS[method_name]}
  134. 39 buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
  135. ensure
  136. 33 then: 4 else: 0 buffer << ">"
  137. 28 then: 2 else: 11 end
  138. 2 else: 0 else: 0 else: 3 else: 3 else: 0 else: 0 else: 0 else # without attributes
  139. 14 buffer << "<#{tag}>"
  140. end
  141. 21 else: 0 then: 7 else: 9
  142. 36 nil
  143. 12 end
  144. RUBY
  145. 15 else: 0 then: 4 else: 0
  146. 8 __registered_elements__[method_name] = tag
  147. 12 method_name
  148. 8 end
  149. 13 end

lib/phlex/sgml/safe_object.rb

100.0% lines covered

100.0% branches covered

1 relevant lines. 1 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. # @api private
  3. 1 module Phlex::SGML::SafeObject
  4. # This is included in objects that are safe to render in an SGML context.
  5. # They must implement a `to_s` method that returns a string.
  6. end

lib/phlex/sgml/safe_value.rb

100.0% lines covered

100.0% branches covered

5 relevant lines. 5 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::SGML::SafeValue
  3. 1 include Phlex::SGML::SafeObject
  4. 1 def initialize(to_s)
  5. 13 @to_s = to_s
  6. end
  7. 1 attr_reader :to_s
  8. end

lib/phlex/sgml/state.rb

100.0% lines covered

95.45% branches covered

74 relevant lines. 74 lines covered and 0 lines missed.
22 total branches, 21 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::SGML::State
  3. 1 def initialize(user_context: {}, output_buffer:, fragments:)
  4. 1582 @buffer = +""
  5. 1582 @capturing = false
  6. 1582 @user_context = user_context
  7. 1582 @fragments = fragments
  8. 1582 @fragment_depth = 0
  9. 1582 @cache_stack = []
  10. 1582 @halt_signal = nil
  11. 1582 @output_buffer = output_buffer
  12. end
  13. 1 attr_accessor :capturing, :user_context
  14. 1 attr_reader :fragments, :fragment_depth, :output_buffer, :buffer
  15. 1 def around_render(component)
  16. 1600 stack = @stack
  17. 1600 then: 1586 if !@fragments || @halt_signal
  18. 1586 yield
  19. else: 14 else
  20. 14 catch do |signal|
  21. 14 @halt_signal = signal
  22. 14 yield
  23. end
  24. end
  25. end
  26. 1 def should_render?
  27. 3427 !@fragments || @fragment_depth > 0
  28. end
  29. 1 def begin_fragment(id)
  30. 39 then: 10 else: 29 then: 7 else: 32 @fragment_depth += 1 if @fragments&.include?(id)
  31. 39 then: 26 else: 13 if caching?
  32. 26 current_byte_offset = 0 # Start tracking the byte offset of this fragment from the start of the cache buffer
  33. 26 @cache_stack.reverse_each do |(cache_buffer, fragment_map)| # We'll iterate deepest to shallowest
  34. 42 current_byte_offset += cache_buffer.bytesize # Add the length of the cache buffer to the current byte offset
  35. 42 fragment_map[id] = [current_byte_offset, nil, []] # Record the byte offset, length, and store a list of the nested fragments
  36. 42 fragment_map.each do |name, (_offset, length, nested_fragments)| # Iterate over the other fragments
  37. 74 then: 42 else: 32 next if name == id || length # Skip if it's the current fragment, or if the fragment has already ended
  38. 32 nested_fragments << id # Add the current fragment to the list of nested fragments
  39. end
  40. end
  41. end
  42. end
  43. 1 def end_fragment(id)
  44. 39 then: 26 else: 13 if caching?
  45. 26 byte_length = nil
  46. 26 @cache_stack.reverse_each do |(cache_buffer, fragment_map)| # We'll iterate deepest to shallowest
  47. 42 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
  48. 42 fragment_map[id][1] = byte_length # All cache contexts will use the same by
  49. end
  50. end
  51. 39 then: 10 else: 29 else: 7 then: 32 return unless @fragments&.include?(id)
  52. 7 @fragments.delete(id)
  53. 7 @fragment_depth -= 1
  54. 7 then: 6 else: 1 throw @halt_signal if @fragments.length == 0
  55. end
  56. 1 def record_fragment(id, offset, length, nested_fragments)
  57. 38 else: 20 then: 18 return unless caching?
  58. 20 @cache_stack.reverse_each do |(cache_buffer, fragment_map)|
  59. 20 offset += cache_buffer.bytesize
  60. 20 fragment_map[id] = [offset, length, nested_fragments]
  61. end
  62. end
  63. 1 def caching(&)
  64. 19 result = nil
  65. 19 capture do
  66. 19 @cache_stack.push([buffer, {}].freeze)
  67. 19 yield
  68. 19 result = @cache_stack.pop
  69. end
  70. 19 result
  71. end
  72. 1 def caching?
  73. 116 @cache_stack.length > 0
  74. end
  75. 1 def capture
  76. 24 new_buffer = +""
  77. 24 original_buffer = @buffer
  78. 24 original_capturing = @capturing
  79. 24 original_fragments = @fragments
  80. begin
  81. 24 @buffer = new_buffer
  82. 24 @capturing = true
  83. 24 @fragments = nil
  84. 24 yield
  85. ensure
  86. 24 @buffer = original_buffer
  87. 24 @capturing = original_capturing
  88. 24 @fragments = original_fragments
  89. end
  90. 24 new_buffer
  91. end
  92. 1 def flush
  93. 6 then: 0 else: 6 return if capturing
  94. 6 buffer = @buffer
  95. 6 @output_buffer << buffer.dup
  96. 6 buffer.clear
  97. 6 nil
  98. end
  99. end

lib/phlex/svg.rb

83.33% lines covered

65.0% branches covered

36 relevant lines. 30 lines covered and 6 lines missed.
20 total branches, 13 branches covered and 7 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Phlex::SVG < Phlex::SGML
  3. 1 include StandardElements
  4. # Returns the string "image/svg+xml"
  5. 1 def content_type
  6. 1 "image/svg+xml"
  7. end
  8. # Override to provide a filename for the SVG file
  9. 1 def filename
  10. nil
  11. end
  12. 1 def cdata(content = nil, &block)
  13. 2 state = @_state
  14. 2 else: 2 then: 0 return unless state.should_render?
  15. 2 then: 1 if !block && String === content
  16. 1 else: 1 state.buffer << "<![CDATA[" << content.gsub("]]>", "]]>]]<![CDATA[") << "]]>"
  17. 1 then: 1 elsif block && nil == content
  18. 1 state.buffer << "<![CDATA[" << capture(&block).gsub("]]>", "]]>]]<![CDATA[") << "]]>"
  19. else
  20. else: 0
  21. raise Phlex::ArgumentError.new("Expected a String or block.")
  22. end
  23. end
  24. 1 def tag(name, **attributes, &)
  25. 256 state = @_state
  26. 256 block_given = block_given?
  27. 256 buffer = state.buffer
  28. 256 else: 256 then: 0 unless state.should_render?
  29. then: 0 else: 0 yield(self) if block_given
  30. return nil
  31. end
  32. 256 else: 256 then: 0 unless Symbol === name
  33. raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
  34. end
  35. 256 then: 256 if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
  36. 256 then: 128 if attributes.length > 0 # with attributes
  37. 128 then: 64 if block_given # with content block
  38. 64 buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
  39. 64 __yield_content__(&)
  40. 64 buffer << "</#{tag}>"
  41. else: 64 else # without content
  42. 64 buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << "></#{tag}>"
  43. end
  44. else: 128 else # without attributes
  45. 128 then: 64 if block_given # with content block
  46. 64 buffer << ("<#{tag}>")
  47. 64 __yield_content__(&)
  48. 64 buffer << "</#{tag}>"
  49. else: 64 else # without content
  50. 64 buffer << "<#{tag}></#{tag}>"
  51. end
  52. end
  53. else: 0 else
  54. raise Phlex::ArgumentError.new("Invalid SVG tag: #{name}")
  55. end
  56. end
  57. end

lib/phlex/svg/standard_elements.rb

100.0% lines covered

100.0% branches covered

66 relevant lines. 66 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::SVG::StandardElements
  3. 1 extend Phlex::SGML::Elements
  4. # Outputs an `<a>` tag.
  5. # See https://developer.mozilla.org/docs/Web/SVG/Element/a
  6. 1 register_element def a(
  7. download: nil,
  8. href: nil,
  9. hreflang: nil,
  10. ping: nil,
  11. referrerpolicy: nil,
  12. rel: nil,
  13. target: nil,
  14. type: nil,
  15. **attributes,
  16. &content
  17. ) = nil
  18. # Outputs an `<animate>` tag.
  19. # See https://developer.mozilla.org/docs/Web/SVG/Element/animate
  20. 1 register_element def animate(
  21. attributeName: nil,
  22. values: nil,
  23. dur: nil,
  24. repeatCount: nil,
  25. **attributes,
  26. &content
  27. ) = nil
  28. # Outputs an `<animateMotion>` tag.
  29. # See https://developer.mozilla.org/docs/Web/SVG/Element/animateMotion
  30. 1 register_element def animateMotion(
  31. keyPoints: nil,
  32. path: nil,
  33. rotate: nil,
  34. begin: nil,
  35. dur: nil,
  36. end: nil,
  37. repeatCount: nil,
  38. repeatDur: nil,
  39. fill: nil,
  40. calcMode: nil,
  41. values: nil,
  42. keyTimes: nil,
  43. keySplines: nil,
  44. from: nil,
  45. to: nil,
  46. by: nil,
  47. attributeName: nil,
  48. additive: nil,
  49. accumulate: nil,
  50. **attributes,
  51. &content
  52. ) = nil
  53. # Outputs an `<animateTransform>` tag.
  54. # See https://developer.mozilla.org/docs/Web/SVG/Element/animateTransform
  55. 1 register_element def animateTransform(
  56. **attributes,
  57. &content
  58. ) = nil
  59. # Outputs a `<circle>` tag.
  60. # See https://developer.mozilla.org/docs/Web/SVG/Element/circle
  61. 1 register_element def circle(
  62. **attributes,
  63. &content
  64. ) = nil
  65. # Outputs a `<clipPath>` tag.
  66. # See https://developer.mozilla.org/docs/Web/SVG/Element/clipPath
  67. 1 register_element def clipPath(
  68. **attributes,
  69. &content
  70. ) = nil
  71. # Outputs a `<defs>` tag.
  72. # See https://developer.mozilla.org/docs/Web/SVG/Element/defs
  73. 1 register_element def defs(
  74. **attributes,
  75. &content
  76. ) = nil
  77. # Outputs a `<desc>` tag.
  78. # See https://developer.mozilla.org/docs/Web/SVG/Element/desc
  79. 1 register_element def desc(
  80. **attributes,
  81. &content
  82. ) = nil
  83. # Outputs a `<discard>` tag.
  84. # See https://developer.mozilla.org/docs/Web/SVG/Element/discard
  85. 1 register_element def discard(
  86. **attributes,
  87. &content
  88. ) = nil
  89. # Outputs an `<ellipse>` tag.
  90. # See https://developer.mozilla.org/docs/Web/SVG/Element/ellipse
  91. 1 register_element def ellipse(
  92. **attributes,
  93. &content
  94. ) = nil
  95. # Outputs an `<feBlend>` tag.
  96. # See https://developer.mozilla.org/docs/Web/SVG/Element/feBlend
  97. 1 register_element def feBlend(
  98. **attributes,
  99. &content
  100. ) = nil
  101. # Outputs an `<feColorMatrix>` tag.
  102. # See https://developer.mozilla.org/docs/Web/SVG/Element/feColorMatrix
  103. 1 register_element def feColorMatrix(
  104. **attributes,
  105. &content
  106. ) = nil
  107. # Outputs an `<feComponentTransfer>` tag.
  108. # See https://developer.mozilla.org/docs/Web/SVG/Element/feComponentTransfer
  109. 1 register_element def feComponentTransfer(
  110. **attributes,
  111. &content
  112. ) = nil
  113. # Outputs an `<feComposite>` tag.
  114. # See https://developer.mozilla.org/docs/Web/SVG/Element/feComposite
  115. 1 register_element def feComposite(
  116. **attributes,
  117. &content
  118. ) = nil
  119. # Outputs an `<feConvolveMatrix>` tag.
  120. # See https://developer.mozilla.org/docs/Web/SVG/Element/feConvolveMatrix
  121. 1 register_element def feConvolveMatrix(
  122. **attributes,
  123. &content
  124. ) = nil
  125. # Outputs an `<feDiffuseLighting>` tag.
  126. # See https://developer.mozilla.org/docs/Web/SVG/Element/feDiffuseLighting
  127. 1 register_element def feDiffuseLighting(
  128. **attributes,
  129. &content
  130. ) = nil
  131. # Outputs an `<feDisplacementMap>` tag.
  132. # See https://developer.mozilla.org/docs/Web/SVG/Element/feDisplacementMap
  133. 1 register_element def feDisplacementMap(
  134. **attributes,
  135. &content
  136. ) = nil
  137. # Outputs an `<feDistantLight>` tag.
  138. # See https://developer.mozilla.org/docs/Web/SVG/Element/feDistantLight
  139. 1 register_element def feDistantLight(
  140. **attributes,
  141. &content
  142. ) = nil
  143. # Outputs an `<feDropShadow>` tag.
  144. # See https://developer.mozilla.org/docs/Web/SVG/Element/feDropShadow
  145. 1 register_element def feDropShadow(
  146. **attributes,
  147. &content
  148. ) = nil
  149. # Outputs an `<feFlood>` tag.
  150. # See https://developer.mozilla.org/docs/Web/SVG/Element/feFlood
  151. 1 register_element def feFlood(
  152. **attributes,
  153. &content
  154. ) = nil
  155. # Outputs an `<feFuncA>` tag.
  156. # See https://developer.mozilla.org/docs/Web/SVG/Element/feFuncA
  157. 1 register_element def feFuncA(
  158. **attributes,
  159. &content
  160. ) = nil
  161. # Outputs an `<feFuncB>` tag.
  162. # See https://developer.mozilla.org/docs/Web/SVG/Element/feFuncB
  163. 1 register_element def feFuncB(
  164. **attributes,
  165. &content
  166. ) = nil
  167. # Outputs an `<feFuncG>` tag.
  168. # See https://developer.mozilla.org/docs/Web/SVG/Element/feFuncG
  169. 1 register_element def feFuncG(
  170. **attributes,
  171. &content
  172. ) = nil
  173. # Outputs an `<feFuncR>` tag.
  174. # See https://developer.mozilla.org/docs/Web/SVG/Element/feFuncR
  175. 1 register_element def feFuncR(
  176. **attributes,
  177. &content
  178. ) = nil
  179. # Outputs an `<feGaussianBlur>` tag.
  180. # See https://developer.mozilla.org/docs/Web/SVG/Element/feGaussianBlur
  181. 1 register_element def feGaussianBlur(
  182. **attributes,
  183. &content
  184. ) = nil
  185. # Outputs an `<feImage>` tag.
  186. # See https://developer.mozilla.org/docs/Web/SVG/Element/feImage
  187. 1 register_element def feImage(
  188. **attributes,
  189. &content
  190. ) = nil
  191. # Outputs an `<feMerge>` tag.
  192. # See https://developer.mozilla.org/docs/Web/SVG/Element/feMerge
  193. 1 register_element def feMerge(
  194. **attributes,
  195. &content
  196. ) = nil
  197. # Outputs an `<feMergeNode>` tag.
  198. # See https://developer.mozilla.org/docs/Web/SVG/Element/feMergeNode
  199. 1 register_element def feMergeNode(
  200. **attributes,
  201. &content
  202. ) = nil
  203. # Outputs an `<feMorphology>` tag.
  204. # See https://developer.mozilla.org/docs/Web/SVG/Element/feMorphology
  205. 1 register_element def feMorphology(
  206. **attributes,
  207. &content
  208. ) = nil
  209. # Outputs an `<feOffset>` tag.
  210. # See https://developer.mozilla.org/docs/Web/SVG/Element/feOffset
  211. 1 register_element def feOffset(
  212. **attributes,
  213. &content
  214. ) = nil
  215. # Outputs an `<fePointLight>` tag.
  216. # See https://developer.mozilla.org/docs/Web/SVG/Element/fePointLight
  217. 1 register_element def fePointLight(
  218. **attributes,
  219. &content
  220. ) = nil
  221. # Outputs an `<feSpecularLighting>` tag.
  222. # See https://developer.mozilla.org/docs/Web/SVG/Element/feSpecularLighting
  223. 1 register_element def feSpecularLighting(
  224. **attributes,
  225. &content
  226. ) = nil
  227. # Outputs an `<feSpotLight>` tag.
  228. # See https://developer.mozilla.org/docs/Web/SVG/Element/feSpotLight
  229. 1 register_element def feSpotLight(
  230. **attributes,
  231. &content
  232. ) = nil
  233. # Outputs an `<feTile>` tag.
  234. # See https://developer.mozilla.org/docs/Web/SVG/Element/feTile
  235. 1 register_element def feTile(
  236. **attributes,
  237. &content
  238. ) = nil
  239. # Outputs an `<feTurbulence>` tag.
  240. # See https://developer.mozilla.org/docs/Web/SVG/Element/feTurbulence
  241. 1 register_element def feTurbulence(
  242. **attributes,
  243. &content
  244. ) = nil
  245. # Outputs a `<filter>` tag.
  246. # See https://developer.mozilla.org/docs/Web/SVG/Element/filter
  247. 1 register_element def filter(
  248. **attributes,
  249. &content
  250. ) = nil
  251. # Outputs a `<foreignObject>` tag.
  252. # See https://developer.mozilla.org/docs/Web/SVG/Element/foreignObject
  253. 1 register_element def foreignObject(
  254. **attributes,
  255. &content
  256. ) = nil
  257. # Outputs a `<g>` tag.
  258. # See https://developer.mozilla.org/docs/Web/SVG/Element/g
  259. 1 register_element def g(
  260. **attributes,
  261. &content
  262. ) = nil
  263. # Outputs an `<image>` tag.
  264. # See https://developer.mozilla.org/docs/Web/SVG/Element/image
  265. 1 register_element def image(
  266. **attributes,
  267. &content
  268. ) = nil
  269. # Outputs a `<line>` tag.
  270. # See https://developer.mozilla.org/docs/Web/SVG/Element/line
  271. 1 register_element def line(
  272. **attributes,
  273. &content
  274. ) = nil
  275. # Outputs a `<linearGradient>` tag.
  276. # See https://developer.mozilla.org/docs/Web/SVG/Element/linearGradient
  277. 1 register_element def linearGradient(
  278. **attributes,
  279. &content
  280. ) = nil
  281. # Outputs a `<marker>` tag.
  282. # See https://developer.mozilla.org/docs/Web/SVG/Element/marker
  283. 1 register_element def marker(
  284. **attributes,
  285. &content
  286. ) = nil
  287. # Outputs a `<mask>` tag.
  288. # See https://developer.mozilla.org/docs/Web/SVG/Element/mask
  289. 1 register_element def mask(
  290. **attributes,
  291. &content
  292. ) = nil
  293. # Outputs a `<metadata>` tag.
  294. # See https://developer.mozilla.org/docs/Web/SVG/Element/metadata
  295. 1 register_element def metadata(
  296. **attributes,
  297. &content
  298. ) = nil
  299. # Outputs an `<mpath>` tag.
  300. # See https://developer.mozilla.org/docs/Web/SVG/Element/mpath
  301. 1 register_element def mpath(
  302. **attributes,
  303. &content
  304. ) = nil
  305. # Outputs a `<path>` tag.
  306. # See https://developer.mozilla.org/docs/Web/SVG/Element/path
  307. 1 register_element def path(
  308. **attributes,
  309. &content
  310. ) = nil
  311. # Outputs a `<pattern>` tag.
  312. # See https://developer.mozilla.org/docs/Web/SVG/Element/pattern
  313. 1 register_element def pattern(
  314. **attributes,
  315. &content
  316. ) = nil
  317. # Outputs a `<polygon>` tag.
  318. # See https://developer.mozilla.org/docs/Web/SVG/Element/polygon
  319. 1 register_element def polygon(
  320. **attributes,
  321. &content
  322. ) = nil
  323. # Outputs a `<polyline>` tag.
  324. # See https://developer.mozilla.org/docs/Web/SVG/Element/polyline
  325. 1 register_element def polyline(
  326. **attributes,
  327. &content
  328. ) = nil
  329. # Outputs a `<radialGradient>` tag.
  330. # See https://developer.mozilla.org/docs/Web/SVG/Element/radialGradient
  331. 1 register_element def radialGradient(
  332. **attributes,
  333. &content
  334. ) = nil
  335. # Outputs a `<rect>` tag.
  336. # See https://developer.mozilla.org/docs/Web/SVG/Element/rect
  337. 1 register_element def rect(
  338. **attributes,
  339. &content
  340. ) = nil
  341. # Outputs a `<script>` tag.
  342. # See https://developer.mozilla.org/docs/Web/SVG/Element/script
  343. 1 register_element def script(
  344. **attributes,
  345. &content
  346. ) = nil
  347. # Outputs a `<set>` tag.
  348. # See https://developer.mozilla.org/docs/Web/SVG/Element/set
  349. 1 register_element def set(
  350. **attributes,
  351. &content
  352. ) = nil
  353. # Outputs a `<stop>` tag.
  354. # See https://developer.mozilla.org/docs/Web/SVG/Element/stop
  355. 1 register_element def stop(
  356. **attributes,
  357. &content
  358. ) = nil
  359. # Outputs a `<style>` tag.
  360. # See https://developer.mozilla.org/docs/Web/SVG/Element/style
  361. 1 register_element def style(
  362. **attributes,
  363. &content
  364. ) = nil
  365. # Outputs an `<svg>` tag.
  366. # See https://developer.mozilla.org/docs/Web/SVG/Element/svg
  367. 1 register_element def svg(
  368. **attributes,
  369. &content
  370. ) = nil
  371. # Outputs a `<switch>` tag.
  372. # See https://developer.mozilla.org/docs/Web/SVG/Element/switch
  373. 1 register_element def switch(
  374. **attributes,
  375. &content
  376. ) = nil
  377. # Outputs a `<symbol>` tag.
  378. # See https://developer.mozilla.org/docs/Web/SVG/Element/symbol
  379. 1 register_element def symbol(
  380. **attributes,
  381. &content
  382. ) = nil
  383. # Outputs a `<text>` tag.
  384. # See https://developer.mozilla.org/docs/Web/SVG/Element/text
  385. 1 register_element def text(
  386. **attributes,
  387. &content
  388. ) = nil
  389. # Outputs a `<textPath>` tag.
  390. # See https://developer.mozilla.org/docs/Web/SVG/Element/textPath
  391. 1 register_element def textPath(
  392. **attributes,
  393. &content
  394. ) = nil
  395. # Outputs a `<title>` tag.
  396. # See https://developer.mozilla.org/docs/Web/SVG/Element/title
  397. 1 register_element def title(
  398. **attributes,
  399. &content
  400. ) = nil
  401. # Outputs a `<tspan>` tag.
  402. # See https://developer.mozilla.org/docs/Web/SVG/Element/tspan
  403. 1 register_element def tspan(
  404. **attributes,
  405. &content
  406. ) = nil
  407. # Outputs a `<use>` tag.
  408. # See https://developer.mozilla.org/docs/Web/SVG/Element/use
  409. 1 register_element def use(
  410. **attributes,
  411. &content
  412. ) = nil
  413. # Outputs a `<view>` tag.
  414. # See https://developer.mozilla.org/docs/Web/SVG/Element/view
  415. 1 register_element def view(
  416. **attributes,
  417. &content
  418. ) = nil
  419. end

quickdraw/compilation_equivalence_cases/attributes.rb

100.0% lines covered

50.0% branches covered

7 relevant lines. 7 lines covered and 0 lines missed.
2 total branches, 1 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Attributes < Phlex::HTML
  3. 1 def view_template
  4. 1 div(
  5. 1 a: nil,
  6. 1 b: 1,
  7. 1 c: :two,
  8. d: "three",
  9. e: 4.5,
  10. f: false,
  11. 1 then: 1 else: 0 g: true,
  12. h: [nil, 1, :two, "three", 4.5, [1]],
  13. i: Set[nil, 1, :two, "three", 4.5, [1]],
  14. j: {
  15. k: 1,
  16. "l" => 2
  17. },
  18. "m" => 3
  19. )
  20. end
  21. end

quickdraw/compilation_equivalence_cases/basic.rb

100.0% lines covered

50.0% branches covered

6 relevant lines. 6 lines covered and 0 lines missed.
2 total branches, 1 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Basic < Phlex::HTML
  3. 1 def view_template
  4. 2 h1 { "Hello" }
  5. 1 br
  6. 1 br(class: "my-class")
  7. 1 end
  8. end

quickdraw/compilation_equivalence_cases/comment.rb

100.0% lines covered

50.0% branches covered

6 relevant lines. 6 lines covered and 0 lines missed.
2 total branches, 1 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Comment < Phlex::HTML
  3. 1 def view_template
  4. 2 comment { "hello world" }
  5. 1 comment { "Begin rendering #{self.class.name}" }
  6. 1 end
  7. 1 end

quickdraw/compilation_equivalence_cases/heredoc.rb

100.0% lines covered

50.0% branches covered

12 relevant lines. 12 lines covered and 0 lines missed.
4 total branches, 2 branches covered and 2 branches missed.
    
  1. 1 class Heredoc < Phlex::HTML
  2. 1 def view_template
  3. 1 lines = 3
  4. 1 unknown {
  5. 1 p { <<~FIRST }
  6. 1 This is a heredoc.
  7. 1 It has #{lines} lines of text.
  8. And it's all going in this <p> tag.
  9. FIRST
  10. 1 }
  11. 1 end
  12. 2 then: 1 def unknown
  13. 1 then: 1 else: 0 yield
  14. 1 end
  15. end

quickdraw/compilation_equivalence_cases/interpolated_string.rb

100.0% lines covered

50.0% branches covered

8 relevant lines. 8 lines covered and 0 lines missed.
2 total branches, 1 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class InterpolatedString < Phlex::HTML
  3. 1 def view_template
  4. 1 name = "Joel"
  5. 1 @ivar = "Will"
  6. 1 h1 { "Hello, #{name}!" }
  7. 1 h2 { "Hello, #@ivar!" } # rubocop:disable Style/VariableInterpolation
  8. 2 h3 { "Hello #{"#{name}"}" }
  9. end
  10. 1 end

quickdraw/compilation_equivalence_cases/override_elements.rb

100.0% lines covered

50.0% branches covered

15 relevant lines. 15 lines covered and 0 lines missed.
6 total branches, 3 branches covered and 3 branches missed.
    
  1. 1 class OverrideElements < Phlex::HTML
  2. 1 def view_template
  3. 1 input(type: "email")
  4. 1
  5. 1 h1
  6. 1 input(type: "email")
  7. 1 div do
  8. 1 input(type: "email")
  9. 1 end
  10. end
  11. 1 then: 1 else: 0
  12. 2 def input(**)
  13. 3 plain("not-an-input")
  14. 4 then: 1 else: 0 # super(class: "form-control", **)
  15. 5 end
  16. 3 end

quickdraw/compilation_equivalence_cases/plain.rb

100.0% lines covered

50.0% branches covered

6 relevant lines. 6 lines covered and 0 lines missed.
4 total branches, 2 branches covered and 2 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Plain < Phlex::HTML
  3. 1 def view_template
  4. 1 local_variable = "good"
  5. 1 plain "Greetings "
  6. 1 plain local_variable
  7. 1 plain "sir!"
  8. end
  9. end

quickdraw/compilation_equivalence_cases/raw.rb

100.0% lines covered

50.0% branches covered

6 relevant lines. 6 lines covered and 0 lines missed.
4 total branches, 2 branches covered and 2 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Raw < Phlex::HTML
  3. 1 def view_template
  4. 2 p { "output before" }
  5. 1 raw(safe("<h1>raw output in the middle</h1>"))
  6. 1 p { "output after" }
  7. 1 end
  8. end

quickdraw/compilation_equivalence_cases/whitespace.rb

100.0% lines covered

50.0% branches covered

11 relevant lines. 11 lines covered and 0 lines missed.
2 total branches, 1 branches covered and 1 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 class Whitespace < Phlex::HTML
  3. 1 def view_template
  4. 2 h1 { "Hello" }
  5. 1 whitespace
  6. 1 h2 { "world" }
  7. 1 br
  8. 1 br
  9. 1 p do
  10. 1 plain "This sentence has"
  11. 1 then: 1 else: 0 whitespace { em { "emphasis" } }
  12. 1 plain "in it."
  13. end
  14. end
  15. end