loading
Generated 2025-03-18T15:45:19+00:00

All Files ( 92.69% covered at 99.12 hits/line )

21 files in total.
1054 relevant lines, 977 lines covered and 77 lines missed. ( 92.69% )
3576 total branches, 1096 branches covered and 2480 branches missed. ( 30.65% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex.rb 85.71 % 84 49 42 7 19.31 66.67 % 9 6 3
lib/phlex/csv.rb 93.50 % 257 123 115 8 22.51 90.20 % 51 46 5
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 148.53 100.00 % 7 7 0
lib/phlex/fifo_cache_store.rb 70.83 % 51 24 17 7 30.17 40.00 % 10 4 6
lib/phlex/helpers.rb 100.00 % 49 23 23 0 6.57 100.00 % 15 15 0
lib/phlex/html.rb 94.12 % 103 51 48 3 92.76 89.29 % 28 25 3
lib/phlex/html/standard_elements.rb 100.00 % 626 104 104 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.18 66.67 % 15 10 5
lib/phlex/sgml.rb 90.67 % 726 343 311 32 151.18 85.86 % 191 164 27
lib/phlex/sgml/elements.rb 91.80 % 133 61 56 5 256.08 24.47 % 3208 785 2423
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.00 100.00 % 0 0 0
lib/phlex/sgml/state.rb 100.00 % 123 74 74 0 274.91 95.45 % 22 21 1
lib/phlex/svg.rb 83.78 % 68 37 31 6 69.65 65.00 % 20 13 7
lib/phlex/svg/standard_elements.rb 100.00 % 421 66 66 0 1.00 100.00 % 0 0 0

SGML ( 92.36% covered at 181.48 hits/line )

5 files in total.
484 relevant lines, 447 lines covered and 37 lines missed. ( 92.36% )
3421 total branches, 970 branches covered and 2451 branches missed. ( 28.35% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex/sgml.rb 90.67 % 726 343 311 32 151.18 85.86 % 191 164 27
lib/phlex/sgml/elements.rb 91.80 % 133 61 56 5 256.08 24.47 % 3208 785 2423
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.00 100.00 % 0 0 0
lib/phlex/sgml/state.rb 100.00 % 123 74 74 0 274.91 95.45 % 22 21 1

HTML ( 98.22% covered at 28.69 hits/line )

3 files in total.
169 relevant lines, 166 lines covered and 3 lines missed. ( 98.22% )
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 94.12 % 103 51 48 3 92.76 89.29 % 28 25 3
lib/phlex/html/standard_elements.rb 100.00 % 626 104 104 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.17% covered at 25.66 hits/line )

2 files in total.
103 relevant lines, 97 lines covered and 6 lines missed. ( 94.17% )
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.78 % 68 37 31 6 69.65 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 ( 93.5% covered at 22.51 hits/line )

1 files in total.
123 relevant lines, 115 lines covered and 8 lines missed. ( 93.5% )
51 total branches, 46 branches covered and 5 branches missed. ( 90.2% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex/csv.rb 93.50 % 257 123 115 8 22.51 90.20 % 51 46 5

Ungrouped ( 86.86% covered at 36.42 hits/line )

10 files in total.
175 relevant lines, 152 lines covered and 23 lines missed. ( 86.86% )
56 total branches, 42 branches covered and 14 branches missed. ( 75.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/phlex.rb 85.71 % 84 49 42 7 19.31 66.67 % 9 6 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 148.53 100.00 % 7 7 0
lib/phlex/fifo_cache_store.rb 70.83 % 51 24 17 7 30.17 40.00 % 10 4 6
lib/phlex/helpers.rb 100.00 % 49 23 23 0 6.57 100.00 % 15 15 0
lib/phlex/kit.rb 85.00 % 83 40 34 6 2.18 66.67 % 15 10 5

lib/phlex.rb

85.71% lines covered

66.67% branches covered

49 relevant lines. 42 lines covered and 7 lines missed.
9 total branches, 6 branches covered and 3 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "erb"
  3. 1 require "set"
  4. 1 module Phlex
  5. 1 autoload :VERSION, "phlex/version"
  6. 1 autoload :Kit, "phlex/kit"
  7. 1 autoload :FIFO, "phlex/fifo"
  8. 1 autoload :Helpers, "phlex/helpers"
  9. 1 autoload :FIFOCacheStore, "phlex/fifo_cache_store"
  10. 1 autoload :CSV, "phlex/csv"
  11. 1 autoload :SVG, "phlex/svg"
  12. 1 autoload :HTML, "phlex/html"
  13. 1 autoload :SGML, "phlex/sgml"
  14. 1 autoload :Error, "phlex/error"
  15. 1 autoload :NameError, "phlex/errors/name_error"
  16. 1 autoload :RuntimeError, "phlex/errors/runtime_error"
  17. 1 autoload :ArgumentError, "phlex/errors/argument_error"
  18. 1 autoload :DoubleRenderError, "phlex/errors/double_render_error"
  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. 669 else: 639 then: 30 unless CACHED_FILES.include?(file_path)
  25. 30 CACHED_FILES << file_path
  26. 30 Phlex::ATTRIBUTE_CACHE.expand(File.size(file_path))
  27. end
  28. end
  29. 1 def self.eager_load
  30. 1 queue = [self]
  31. 33 body: 31 while (mod = queue.shift)
  32. 31 mod.constants.each do |const_name|
  33. 45 const = mod.const_get(const_name)
  34. 45 then: 30 else: 15 queue << const if Module === const
  35. end
  36. end
  37. end
  38. # Generate an HTML string using Phlex’ HTML DSL
  39. 1 def self.html(&block)
  40. 2 HTML.call do |component|
  41. 2 receiver = block.binding.receiver
  42. 2 receiver.instance_variables.each do |ivar|
  43. 9 then: 0 else: 9 next if component.instance_variable_defined?(ivar)
  44. 9 value = receiver.instance_variable_get(ivar)
  45. 9 component.instance_variable_set(ivar, value)
  46. end
  47. 2 component.instance_exec(receiver, &block)
  48. end
  49. end
  50. # Generate an SVG string using Phlex’ SVG DSL
  51. 1 def self.svg(&block)
  52. SVG.call do |component|
  53. receiver = block.binding.receiver
  54. receiver.instance_variables.each do |ivar|
  55. then: 0 else: 0 next if component.instance_variable_defined?(ivar)
  56. value = receiver.instance_variable_get(ivar)
  57. component.instance_variable_set(ivar, value)
  58. end
  59. component.instance_exec(receiver, &block)
  60. end
  61. end
  62. end
  63. 1 def 💪
  64. 1 Phlex
  65. end

lib/phlex/csv.rb

93.5% lines covered

90.2% branches covered

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

70.83% lines covered

40.0% branches covered

24 relevant lines. 17 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
  25. 1 def map_key(value)
  26. 192 case value
  27. when: 50 when Array
  28. 213 value.map { |it| map_key(it) }
  29. when: 0 when Hash
  30. value.to_h { |k, v| [map_key(k), map_key(v)].freeze }
  31. when: 142 when String, Symbol, Integer, Float, Time, true, false, nil
  32. 142 value
  33. else: 0 else
  34. then: 0 if value.respond_to?(:cache_key_with_version)
  35. else: 0 map_key(value.cache_key_with_version)
  36. then: 0 elsif value.respond_to?(:cache_key)
  37. map_key(value.cache_key)
  38. else: 0 else
  39. raise ArgumentError.new("Invalid cache key: #{value.class}")
  40. end
  41. end
  42. end
  43. end

lib/phlex/helpers.rb

100.0% lines covered

100.0% branches covered

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

lib/phlex/html.rb

94.12% lines covered

89.29% branches covered

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

lib/phlex/html/standard_elements.rb

100.0% lines covered

100.0% branches covered

104 relevant lines. 104 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. # Outputs a `<slot>` tag.
  397. #
  398. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/slot)
  399. # [Spec](https://html.spec.whatwg.org/#the-slot-element)
  400. 1 register_element def slot(**attributes, &content) = nil
  401. # Outputs a `<small>` tag.
  402. #
  403. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/small)
  404. # [Spec](https://html.spec.whatwg.org/#the-small-element)
  405. 1 register_element def small(**attributes, &content) = nil
  406. # Outputs a `<span>` tag.
  407. #
  408. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/span)
  409. # [Spec](https://html.spec.whatwg.org/#the-span-element)
  410. 1 register_element def span(**attributes, &content) = nil
  411. # Outputs a `<strong>` tag.
  412. #
  413. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/strong)
  414. # [Spec](https://html.spec.whatwg.org/#the-strong-element)
  415. 1 register_element def strong(**attributes, &content) = nil
  416. # Outputs a `<style>` tag.
  417. #
  418. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/style)
  419. # [Spec](https://html.spec.whatwg.org/#the-style-element)
  420. 1 register_element def style(**attributes, &content) = nil
  421. # Outputs a `<sub>` tag.
  422. #
  423. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/sub)
  424. # [Spec](https://html.spec.whatwg.org/#the-sub-element)
  425. 1 register_element def sub(**attributes, &content) = nil
  426. # Outputs a `<summary>` tag.
  427. #
  428. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/summary)
  429. # [Spec](https://html.spec.whatwg.org/#the-summary-element)
  430. # [Can I Use?](https://caniuse.com/details)
  431. 1 register_element def summary(**attributes, &content) = nil
  432. # Outputs a `<sup>` tag.
  433. #
  434. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/sup)
  435. # [Spec](https://html.spec.whatwg.org/#the-sup-element)
  436. 1 register_element def sup(**attributes, &content) = nil
  437. # Outputs an `<svg>` tag.
  438. #
  439. # [MDN Docs](https://developer.mozilla.org/docs/Web/SVG/Element/svg)
  440. # [Spec](https://html.spec.whatwg.org/#the-svg-element)
  441. 1 register_element def svg(**attributes, &content) = nil
  442. # Outputs a `<table>` tag.
  443. #
  444. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/table)
  445. # [Spec](https://html.spec.whatwg.org/#the-table-element)
  446. 1 register_element def table(**attributes, &content) = nil
  447. # Outputs a `<tbody>` tag.
  448. #
  449. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/tbody)
  450. # [Spec](https://html.spec.whatwg.org/#the-tbody-element)
  451. 1 register_element def tbody(**attributes, &content) = nil
  452. # Outputs a `<td>` tag.
  453. #
  454. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/td)
  455. # [Spec](https://html.spec.whatwg.org/#the-td-element)
  456. 1 register_element def td(**attributes, &content) = nil
  457. # Outputs a `<template>` tag.
  458. #
  459. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/template)
  460. # [Spec](https://html.spec.whatwg.org/#the-template-element)
  461. 1 register_element def template(**attributes, &content) = nil
  462. # Outputs a `<textarea>` tag.
  463. #
  464. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/textarea)
  465. # [Spec](https://html.spec.whatwg.org/#the-textarea-element)
  466. 1 register_element def textarea(**attributes, &content) = nil
  467. # Outputs a `<tfoot>` tag.
  468. #
  469. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/tfoot)
  470. # [Spec](https://html.spec.whatwg.org/#the-tfoot-element)
  471. 1 register_element def tfoot(**attributes, &content) = nil
  472. # Outputs a `<th>` tag.
  473. #
  474. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/th)
  475. # [Spec](https://html.spec.whatwg.org/#the-th-element)
  476. 1 register_element def th(**attributes, &content) = nil
  477. # Outputs a `<thead>` tag.
  478. #
  479. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/thead)
  480. # [Spec](https://html.spec.whatwg.org/#the-thead-element)
  481. 1 register_element def thead(**attributes, &content) = nil
  482. # Outputs a `<time>` tag.
  483. #
  484. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/time)
  485. # [Spec](https://html.spec.whatwg.org/#the-time-element)
  486. 1 register_element def time(**attributes, &content) = nil
  487. # Outputs a `<title>` tag.
  488. #
  489. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/title)
  490. # [Spec](https://html.spec.whatwg.org/#the-title-element)
  491. 1 register_element def title(**attributes, &content) = nil
  492. # Outputs a `<tr>` tag.
  493. #
  494. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/tr)
  495. # [Spec](https://html.spec.whatwg.org/#the-tr-element)
  496. 1 register_element def tr(**attributes, &content) = nil
  497. # Outputs a `<u>` tag.
  498. #
  499. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/u)
  500. # [Spec](https://html.spec.whatwg.org/#the-u-element)
  501. 1 register_element def u(**attributes, &content) = nil
  502. # Outputs a `<ul>` tag.
  503. #
  504. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/ul)
  505. # [Spec](https://html.spec.whatwg.org/#the-ul-element)
  506. 1 register_element def ul(**attributes, &content) = nil
  507. # Outputs a `<var>` tag.
  508. #
  509. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/var)
  510. # [Spec](https://html.spec.whatwg.org/#the-var-element)
  511. 1 register_element def var(**attributes, &content) = nil
  512. # Outputs a `<video>` tag.
  513. #
  514. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/video)
  515. # [Spec](https://html.spec.whatwg.org/#the-video-element)
  516. # [Can I Use?](https://caniuse.com/video)
  517. 1 register_element def video(**attributes, &content) = nil
  518. # Outputs a `<wbr>` tag.
  519. #
  520. # [MDN Docs](https://developer.mozilla.org/docs/Web/HTML/Element/wbr)
  521. # [Spec](https://html.spec.whatwg.org/#the-wbr-element)
  522. 1 register_element def wbr(**attributes, &content) = nil
  523. 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. 2 mod = self.class
  6. 2 then: 2 if name[0] == name[0].upcase && mod.constants.include?(name) && mod.const_get(name) && methods.include?(name)
  7. 2 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. 2 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: 2 else
  25. 2 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. 7 then: 2 else: 5 return if autoload?(name)
  40. 5 me = self
  41. 5 constant = const_get(name)
  42. 5 else: 0 case constant
  43. when: 4 when Class
  44. 4 then: 4 else: 0 if constant < Phlex::SGML
  45. 4 constant.include(me)
  46. 4 constant = nil
  47. 4 define_method(name) do |*args, **kwargs, &block|
  48. 3 constant = me.const_get(name)
  49. 3 render(constant.new(*args, **kwargs), &block)
  50. end
  51. 4 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. 5 super
  67. end
  68. end

lib/phlex/sgml.rb

90.67% lines covered

85.86% branches covered

343 relevant lines. 311 lines covered and 32 lines missed.
191 total branches, 164 branches covered and 27 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 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 ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
  7. 1 compiler.pre_cmd = [""]
  8. 1 compiler.put_cmd = "@_state.buffer.<<"
  9. 1 compiler.insert_cmd = "__implicit_output__"
  10. 1 compiler.post_cmd = ["nil"]
  11. 1 def compiler.add_insert_cmd(out, content)
  12. 4 out.push("#{@insert_cmd}((#{content}))")
  13. end
  14. end
  15. 1 autoload :Elements, "phlex/sgml/elements"
  16. 1 autoload :SafeObject, "phlex/sgml/safe_object"
  17. 1 autoload :SafeValue, "phlex/sgml/safe_value"
  18. 1 autoload :State, "phlex/sgml/state"
  19. 1 include Phlex::Helpers
  20. 1 class << self
  21. # Render the view to a String. Arguments are delegated to {.new}.
  22. 1 def call(...)
  23. 1353 new(...).call
  24. end
  25. # Create a new instance of the component.
  26. # @note The block will not be delegated {#initialize}. Instead, it will be sent to {#template} when rendering.
  27. 1 def new(*a, **k, &block)
  28. 1556 then: 359 if block
  29. 359 object = super(*a, **k, &nil)
  30. 718 object.instance_exec { @_content_block = block }
  31. 359 object
  32. else: 1197 else
  33. 1197 super
  34. end
  35. end
  36. 1 def erb(method_name, erb = nil, locals: nil, &block)
  37. 2 loc = caller_locations(1, 1)[0]
  38. 2 path = loc.path.delete_suffix(".rb")
  39. 2 file = loc.path
  40. 2 line = loc.lineno - 1
  41. 2 else: 2 then: 0 unless erb
  42. method_path = "#{path}/#{method_name}.html.erb"
  43. sidecar_path = "#{path}.html.erb"
  44. then: 0 if File.exist?(method_path)
  45. erb = File.read(method_path)
  46. file = method_path
  47. else: 0 line = 1
  48. then: 0 elsif method_name == :view_template && File.exist?(sidecar_path)
  49. erb = File.read(sidecar_path)
  50. file = sidecar_path
  51. line = 1
  52. else: 0 else
  53. raise Phlex::RuntimeError.new(<<~MESSAGE)
  54. No ERB template found for #{method_name}
  55. MESSAGE
  56. end
  57. end
  58. 2 code, _enc = ERBCompiler.compile(erb)
  59. 2 class_eval(<<~RUBY, file, line)
  60. def #{method_name} #{locals}
  61. #{code}
  62. end
  63. RUBY
  64. end
  65. end
  66. 1 def view_template
  67. 160 then: 157 if block_given?
  68. 157 yield
  69. else: 3 else
  70. 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"
  71. end
  72. end
  73. 1 def to_proc
  74. 2 proc { |c| c.render(self) }
  75. end
  76. 1 def call(buffer = +"", context: {}, fragments: nil, &)
  77. 1535 state = Phlex::SGML::State.new(
  78. user_context: context,
  79. output_buffer: buffer,
  80. then: 14 else: 1521 fragments: fragments&.to_set,
  81. )
  82. 1535 internal_call(parent: nil, state:, &)
  83. 1488 state.output_buffer << state.buffer
  84. end
  85. 1 def internal_call(parent: nil, state: nil, &block)
  86. 1554 then: 1 else: 1553 if @_state
  87. 1 raise Phlex::DoubleRenderError.new(
  88. "You can't render a #{self.class.name} more than once."
  89. )
  90. end
  91. 1553 @_state = state
  92. 1553 else: 1551 then: 2 return "" unless render?
  93. 1551 block ||= @_content_block
  94. 1551 Thread.current[:__phlex_component__] = [self, Fiber.current.object_id].freeze
  95. 1551 state.around_render(self) do
  96. 1551 before_template(&block)
  97. 1551 around_template do
  98. 1551 then: 511 if block
  99. 511 view_template do |*args|
  100. 499 then: 340 if args.length > 0
  101. 340 __yield_content_with_args__(*args, &block)
  102. else: 159 else
  103. 159 __yield_content__(&block)
  104. end
  105. end
  106. else: 1040 else
  107. 1040 view_template
  108. end
  109. end
  110. 1499 after_template(&block)
  111. end
  112. ensure
  113. 1554 Thread.current[:__phlex_component__] = [parent, Fiber.current.object_id].freeze
  114. end
  115. 1 def context
  116. 6 then: 5 if rendering?
  117. 5 @_state.user_context
  118. else: 1 else
  119. 1 raise Phlex::ArgumentError.new(<<~MESSAGE)
  120. You can’t access the context before the component has started rendering.
  121. MESSAGE
  122. end
  123. end
  124. # Returns `false` before rendering and `true` once the component has started rendering.
  125. # It will not reset back to false after rendering.
  126. 1 def rendering?
  127. 6 !!@_state
  128. end
  129. # Output plain text.
  130. 1 def plain(content)
  131. 13 else: 12 then: 1 unless __text__(content)
  132. 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")
  133. end
  134. 12 nil
  135. end
  136. # Output a single space character. If a block is given, a space will be output before and after the block.
  137. 1 def whitespace(&)
  138. 6 state = @_state
  139. 6 else: 2 then: 4 return unless state.should_render?
  140. 2 buffer = state.buffer
  141. 2 buffer << " "
  142. 2 then: 1 else: 1 if block_given?
  143. 1 __yield_content__(&)
  144. 1 buffer << " "
  145. end
  146. 2 nil
  147. end
  148. # Wrap the output in an HTML comment.
  149. #
  150. # [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Comments)
  151. 1 def comment(&)
  152. 10 state = @_state
  153. 10 else: 2 then: 8 return unless state.should_render?
  154. 2 buffer = state.buffer
  155. 2 buffer << "<!-- "
  156. 2 __yield_content__(&)
  157. 2 buffer << " -->"
  158. 2 nil
  159. end
  160. # Output the given safe object as-is. You may need to use `safe` to mark a string as a safe object.
  161. 1 def raw(content)
  162. 4 case content
  163. when: 1 when Phlex::SGML::SafeObject
  164. 1 state = @_state
  165. 1 else: 1 then: 0 return unless state.should_render?
  166. 1 when: 2 state.buffer << content.to_s
  167. when nil, "" # do nothing
  168. else: 1 else
  169. 1 raise Phlex::ArgumentError.new("You passed an unsafe object to `raw`.")
  170. end
  171. 3 nil
  172. end
  173. # Capture the output of the block and returns it as a string.
  174. 1 def capture(*args, &block)
  175. 5 else: 5 then: 0 return "" unless block
  176. 5 then: 1 if args.length > 0
  177. 2 @_state.capture { __yield_content_with_args__(*args, &block) }
  178. else: 4 else
  179. 8 @_state.capture { __yield_content__(&block) }
  180. end
  181. end
  182. # Define a named fragment that can be selectively rendered.
  183. 1 def fragment(name)
  184. 39 state = @_state
  185. 39 state.begin_fragment(name)
  186. 39 yield
  187. 39 state.end_fragment(name)
  188. 33 nil
  189. end
  190. # Mark the given string as safe for HTML output.
  191. 1 def safe(value)
  192. 6 case value
  193. when: 5 when String
  194. 5 Phlex::SGML::SafeValue.new(value)
  195. else: 1 else
  196. 1 raise Phlex::ArgumentError.new("Expected a String.")
  197. end
  198. end
  199. 1 alias_method :🦺, :safe
  200. # Flush the current state to the output buffer.
  201. 1 def flush
  202. 5 @_state.flush
  203. end
  204. 1 def render(renderable = nil, &)
  205. 21 case renderable
  206. when: 19 when Phlex::SGML
  207. 19 renderable.internal_call(state: @_state, parent: self, &)
  208. when: 1 when Class
  209. 1 then: 1 else: 0 if renderable < Phlex::SGML
  210. 1 render(renderable.new, &)
  211. end
  212. when: 0 when Enumerable
  213. renderable.each { |r| render(r, &) }
  214. when: 0 when Proc, Method
  215. then: 0 if renderable.arity == 0
  216. __yield_content_with_no_yield_args__(&renderable)
  217. else: 0 else
  218. __yield_content__(&renderable)
  219. end
  220. when: 0 when String
  221. plain(renderable)
  222. when: 1 when nil
  223. 1 then: 1 else: 0 __yield_content__(&) if block_given?
  224. else: 0 else
  225. raise Phlex::ArgumentError.new("You can't render a #{renderable.inspect}.")
  226. end
  227. 21 nil
  228. end
  229. # Cache a block of content.
  230. #
  231. # ```ruby
  232. # @products.each do |product|
  233. # cache product do
  234. # h1 { product.name }
  235. # end
  236. # end
  237. # ```
  238. 1 def cache(*cache_key, **, &content)
  239. 25 location = caller_locations(1, 1)[0]
  240. full_key = [
  241. 25 app_version_key, # invalidates the key when deploying new code in case of changes
  242. self.class.name, # prevents collisions between classes
  243. 25 then: 0 else: 25 (self.class.object_id if enable_cache_reloading?), # enables reloading
  244. location.base_label, # prevents collisions between different methods
  245. location.lineno, # prevents collisions between different lines
  246. cache_key, # allows for custom cache keys
  247. ].freeze
  248. 25 low_level_cache(full_key, **, &content)
  249. 25 nil
  250. end
  251. # Cache a block of content where you control the entire cache key.
  252. # If you really know what you’re doing and want to take full control
  253. # and responsibility for the cache key, use this method.
  254. #
  255. # ```ruby
  256. # low_level_cache([Commonmarker::VERSION, Digest::MD5.hexdigest(@content)]) do
  257. # markdown(@content)
  258. # end
  259. # ```
  260. #
  261. # Note: To allow you more control, this method does not take a splat of cache keys.
  262. # If you need to pass multiple cache keys, you should pass an array.
  263. 1 def low_level_cache(cache_key, **options, &content)
  264. 25 state = @_state
  265. 44 cached_buffer, fragment_map = cache_store.fetch(cache_key, **options) { state.caching(&content) }
  266. 25 then: 18 if state.should_render?
  267. 18 fragment_map.each do |fragment_name, (offset, length, nested_fragments)|
  268. 38 state.record_fragment(fragment_name, offset, length, nested_fragments)
  269. end
  270. 18 state.buffer << cached_buffer
  271. else: 7 else
  272. 7 fragment_map.each do |fragment_name, (offset, length, nested_fragments)|
  273. 21 then: 7 else: 14 if state.fragments.include?(fragment_name)
  274. 7 state.fragments.delete(fragment_name)
  275. 7 state.fragments.subtract(nested_fragments)
  276. 7 state.buffer << cached_buffer.byteslice(offset, length)
  277. end
  278. end
  279. end
  280. 25 nil
  281. end
  282. 1 def json_escape(string)
  283. ERB::Util.json_escape(string)
  284. end
  285. 1 private
  286. # Override this method to use a different deployment key.
  287. 1 def app_version_key
  288. 25 Phlex::DEPLOYED_AT
  289. end
  290. # Override this method to use a different cache store.
  291. 1 def cache_store
  292. raise "Cache store not implemented."
  293. end
  294. 1 def enable_cache_reloading?
  295. 25 false
  296. end
  297. 1 def vanish(...)
  298. capture(...)
  299. nil
  300. end
  301. 1 def render?
  302. 1549 true
  303. end
  304. 1 def format_object(object)
  305. 29 else: 3 case object
  306. when: 26 when Float, Integer
  307. 26 object.to_s
  308. end
  309. end
  310. 1 def around_template
  311. 1551 yield
  312. 1499 nil
  313. end
  314. 1 def before_template
  315. 1550 nil
  316. end
  317. 1 def after_template
  318. 1498 nil
  319. end
  320. 1 def __yield_content__
  321. 497 else: 497 then: 0 return unless block_given?
  322. 497 buffer = @_state.buffer
  323. 497 original_length = buffer.bytesize
  324. 497 content = yield(self)
  325. 465 then: 8 else: 457 __implicit_output__(content) if original_length == buffer.bytesize
  326. 465 nil
  327. end
  328. 1 def __yield_content_with_no_yield_args__
  329. else: 0 then: 0 return unless block_given?
  330. buffer = @_state.buffer
  331. original_length = buffer.bytesize
  332. content = yield # <-- doesn’t yield self 😉
  333. then: 0 else: 0 __implicit_output__(content) if original_length == buffer.bytesize
  334. nil
  335. end
  336. 1 def __yield_content_with_args__(*a)
  337. 341 else: 341 then: 0 return unless block_given?
  338. 341 buffer = @_state.buffer
  339. 341 original_length = buffer.bytesize
  340. 341 content = yield(*a)
  341. 341 then: 336 else: 5 __implicit_output__(content) if original_length == buffer.bytesize
  342. 341 nil
  343. end
  344. 1 def __implicit_output__(content)
  345. 348 state = @_state
  346. 348 else: 345 then: 3 return true unless state.should_render?
  347. 345 case content
  348. when: 1 when Phlex::SGML::SafeObject
  349. 1 state.buffer << content.to_s
  350. when: 341 when String
  351. 341 state.buffer << Phlex::Escape.html_escape(content)
  352. when: 0 when Symbol
  353. state.buffer << Phlex::Escape.html_escape(content.name)
  354. when: 3 when nil
  355. nil
  356. else: 0 else
  357. then: 0 if (formatted_object = format_object(content))
  358. state.buffer << Phlex::Escape.html_escape(formatted_object)
  359. else: 0 else
  360. return false
  361. end
  362. end
  363. 345 true
  364. end
  365. # same as __implicit_output__ but escapes even `safe` objects
  366. 1 def __text__(content)
  367. 13 state = @_state
  368. 13 else: 12 then: 1 return true unless state.should_render?
  369. 12 case content
  370. when: 7 when String
  371. 7 state.buffer << Phlex::Escape.html_escape(content)
  372. when: 1 when Symbol
  373. 1 state.buffer << Phlex::Escape.html_escape(content.name)
  374. when: 1 when nil
  375. nil
  376. else: 3 else
  377. 3 then: 2 if (formatted_object = format_object(content))
  378. 2 state.buffer << Phlex::Escape.html_escape(formatted_object)
  379. else: 1 else
  380. 1 return false
  381. end
  382. end
  383. 11 true
  384. end
  385. 1 def __attributes__(attributes, buffer = +"")
  386. 160 attributes.each do |k, v|
  387. 185 else: 180 then: 5 next unless v
  388. 180 when: 14 name = case k
  389. 14 when: 165 when String then k
  390. 165 else: 1 when Symbol then k.name.tr("_", "-")
  391. 1 else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.")
  392. end
  393. 179 value = case v
  394. when: 16 when true
  395. 16 true
  396. when: 53 when String
  397. 53 v.gsub('"', "&quot;")
  398. when: 8 when Symbol
  399. 8 v.name.tr("_", "-").gsub('"', "&quot;")
  400. when: 8 when Integer, Float
  401. 8 v.to_s
  402. when: 55 when Hash
  403. 55 case k
  404. when: 10 when :style
  405. 10 __styles__(v).gsub('"', "&quot;")
  406. else: 45 else
  407. 45 __nested_attributes__(v, "#{name}-", buffer)
  408. end
  409. when: 23 when Array
  410. 23 case k
  411. when: 8 when :style
  412. 8 __styles__(v).gsub('"', "&quot;")
  413. else: 15 else
  414. 15 __nested_tokens__(v)
  415. end
  416. when: 13 when Set
  417. 13 case k
  418. when: 2 when :style
  419. 2 __styles__(v).gsub('"', "&quot;")
  420. else: 11 else
  421. 11 __nested_tokens__(v.to_a)
  422. end
  423. when: 2 when Phlex::SGML::SafeObject
  424. 2 v.to_s.gsub('"', "&quot;")
  425. else: 1 else
  426. 1 raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
  427. end
  428. 161 lower_name = name.downcase
  429. 161 else: 2 then: 159 unless Phlex::SGML::SafeObject === v
  430. 159 normalized_name = lower_name.delete("^a-z-")
  431. 159 then: 11 else: 148 if value != true && REF_ATTRIBUTES.include?(normalized_name)
  432. 11 case value
  433. when: 10 when String
  434. 10 else: 4 if value.downcase.delete("^a-z:").start_with?("javascript:")
  435. then: 6 # We just ignore these because they were likely not specified by the developer.
  436. 6 next
  437. end
  438. else: 1 else
  439. 1 raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
  440. end
  441. end
  442. 152 then: 1 else: 151 if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-")
  443. 1 raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  444. end
  445. 151 then: 0 else: 151 if UNSAFE_ATTRIBUTES.include?(normalized_name)
  446. raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  447. end
  448. end
  449. 153 then: 5 else: 148 if name.match?(/[<>&"']/)
  450. 5 raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  451. end
  452. 148 then: 3 else: 145 if lower_name.to_sym == :id && k != :id
  453. 3 raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.")
  454. end
  455. 145 else: 32 case value
  456. when: 10 when true
  457. 10 buffer << " " << name
  458. when: 103 when String
  459. 103 buffer << " " << name << '="' << value << '"'
  460. end
  461. end
  462. 131 buffer
  463. end
  464. # Provides the nested-attributes case for serializing out attributes.
  465. # This allows us to skip many of the checks the `__attributes__` method must perform.
  466. 1 def __nested_attributes__(attributes, base_name, buffer = +"")
  467. 46 attributes.each do |k, v|
  468. 45 else: 43 then: 2 next unless v
  469. 43 when: 6 name = case k
  470. 6 when: 36 when String then k
  471. 36 else: 1 when Symbol then k.name.tr("_", "-")
  472. 1 else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
  473. end
  474. 42 then: 10 else: 32 if name.match?(/[<>&"']/)
  475. 10 raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
  476. end
  477. 32 case v
  478. when: 3 when true
  479. 3 buffer << " " << base_name << name
  480. when: 12 when String
  481. 12 buffer << " " << base_name << name << '="' << v.gsub('"', "&quot;") << '"'
  482. when: 6 when Symbol
  483. 6 buffer << " " << base_name << name << '="' << v.name.tr("_", "-").gsub('"', "&quot;") << '"'
  484. when: 6 when Integer, Float
  485. 6 buffer << " " << base_name << name << '="' << v.to_s << '"'
  486. when: 1 when Hash
  487. 1 __nested_attributes__(v, "#{base_name}#{name}-", buffer)
  488. when: 1 when Array
  489. 1 buffer << " " << base_name << name << '="' << __nested_tokens__(v) << '"'
  490. when: 1 when Set
  491. 1 buffer << " " << base_name << name << '="' << __nested_tokens__(v.to_a) << '"'
  492. when: 1 when Phlex::SGML::SafeObject
  493. 1 buffer << " " << base_name << name << '="' << v.to_s.gsub('"', "&quot;") << '"'
  494. else: 1 else
  495. 1 raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
  496. end
  497. 31 buffer
  498. end
  499. end
  500. 1 def __nested_tokens__(tokens)
  501. 30 buffer = +""
  502. 30 i, length = 0, tokens.length
  503. 30 body: 62 while i < length
  504. 62 token = tokens[i]
  505. 62 case token
  506. when: 19 when String
  507. 19 then: 9 if i > 0
  508. 9 buffer << " " << token
  509. else: 10 else
  510. 10 buffer << token
  511. end
  512. when: 16 when Symbol
  513. 16 then: 10 if i > 0
  514. 10 buffer << " " << token.name.tr("_", "-")
  515. else: 6 else
  516. 6 buffer << token.name.tr("_", "-")
  517. end
  518. when: 18 when Integer, Float, Phlex::SGML::SafeObject
  519. 18 then: 10 if i > 0
  520. 10 buffer << " " << token.to_s
  521. else: 8 else
  522. 8 buffer << token.to_s
  523. end
  524. when: 3 when Array
  525. 3 then: 2 else: 1 if token.length > 0
  526. 2 then: 1 if i > 0
  527. 1 buffer << " " << __nested_tokens__(token)
  528. else: 1 else
  529. 1 buffer << __nested_tokens__(token)
  530. end
  531. when: 4 end
  532. when nil
  533. # Do nothing
  534. else: 2 else
  535. 2 raise Phlex::ArgumentError.new("Invalid token type: #{token.class}.")
  536. end
  537. 60 i += 1
  538. end
  539. 28 buffer.gsub('"', "&quot;")
  540. end
  541. # Result is **unsafe**, so it should be escaped!
  542. 1 def __styles__(styles)
  543. 23 else: 1 case styles
  544. when: 10 when Array, Set
  545. 10 styles.filter_map do |s|
  546. 14 case s
  547. when: 5 when String
  548. 5 then: 1 if s == "" || s.end_with?(";")
  549. 1 s
  550. else: 4 else
  551. 4 "#{s};"
  552. end
  553. when: 2 when Phlex::SGML::SafeObject
  554. 2 value = s.to_s
  555. 2 then: 1 else: 1 value.end_with?(";") ? value : "#{value};"
  556. when: 2 when Hash
  557. 2 next __styles__(s)
  558. when: 4 when nil
  559. 4 next nil
  560. else: 1 else
  561. 1 raise Phlex::ArgumentError.new("Invalid style: #{s.inspect}.")
  562. end
  563. end.join(" ")
  564. when: 12 when Hash
  565. 12 buffer = +""
  566. 12 i = 0
  567. 12 styles.each do |k, v|
  568. 14 prop = case k
  569. when: 1 when String
  570. 1 k
  571. when: 12 when Symbol
  572. 12 k.name.tr("_", "-")
  573. else: 1 else
  574. 1 raise Phlex::ArgumentError.new("Style keys should be Strings or Symbols.")
  575. end
  576. 13 value = case v
  577. when: 5 when String
  578. 5 v
  579. when: 2 when Symbol
  580. 2 v.name.tr("_", "-")
  581. when: 4 when Integer, Float, Phlex::SGML::SafeObject
  582. 4 v.to_s
  583. when: 1 when nil
  584. 1 nil
  585. else: 1 else
  586. 1 raise Phlex::ArgumentError.new("Invalid style value: #{v.inspect}")
  587. end
  588. 12 then: 11 else: 1 if value
  589. 11 then: 9 if i == 0
  590. 9 buffer << prop << ": " << value << ";"
  591. else: 2 else
  592. 2 buffer << " " << prop << ": " << value << ";"
  593. end
  594. end
  595. 12 i += 1
  596. end
  597. 10 buffer
  598. end
  599. end
  600. 1 private_class_method def self.method_added(method_name)
  601. 700 then: 670 if method_name == :view_template
  602. 670 location = instance_method(method_name).source_location[0]
  603. 670 then: 669 else: 1 if location[0] in "/" | "."
  604. 669 Phlex.__expand_attribute_cache__(location)
  605. end
  606. else: 30 else
  607. 30 super
  608. end
  609. end
  610. end

lib/phlex/sgml/elements.rb

91.8% lines covered

24.47% branches covered

61 relevant lines. 56 lines covered and 5 lines missed.
3208 total branches, 785 branches covered and 2423 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module Phlex::SGML::Elements
  3. 1 def __registered_elements__
  4. 924 @__registered_elements__ ||= {}
  5. end
  6. 1 def register_element(method_name, tag: method_name.name.tr("_", "-"))
  7. 166 class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
  8. # frozen_string_literal: true
  9. 1 def #{method_name}(**attributes)
  10. 971 state = @_state
  11. 971 buffer = state.buffer
  12. 971 block_given = block_given?
  13. 971 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: 4 then: 0 else: 142 then: 9 else: 4 then: 0 else: 138 then: 9 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: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 6 then: 0 else: 4 then: 0 else: 5 then: 0 else: 4 then: 0 else: 4 then: 0 else: 11 then: 2 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 4 then: 0 else: 6 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: 4 then: 0 else: 4 then: 0 else: 5 then: 0 else: 4 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: 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: 6 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 3 then: 0 else: 9 then: 0 else: 6 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: 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 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 unless state.should_render?
  14. 20 then: 0 else: 0 then: 4 else: 0 then: 0 else: 6 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 9 else: 0 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 17 else: 3 then: 4 else: 0 then: 32 else: 0 then: 0 else: 0 then: 4 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: 2 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 4 else: 0 then: 13 else: 0 then: 0 else: 0 then: 4 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: 3 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: 3 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: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 yield(self) if block_given
  15. 15 return nil
  16. end
  17. 951 then: 12 then: 4 then: 4 then: 4 then: 12 then: 4 then: 15 then: 11 then: 8 then: 4 then: 4 then: 4 then: 11 then: 4 then: 4 then: 4 then: 11 then: 4 then: 4 then: 4 then: 4 then: 8 then: 4 then: 4 then: 4 then: 8 then: 4 then: 8 then: 4 then: 4 then: 8 then: 4 then: 8 then: 4 then: 4 then: 7 then: 16 then: 4 then: 8 then: 4 then: 4 then: 4 then: 4 then: 5 then: 4 then: 4 then: 4 then: 4 then: 4 then: 8 then: 4 then: 4 then: 8 then: 4 then: 8 then: 4 then: 4 then: 4 then: 4 then: 9 then: 12 then: 3 then: 4 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 6 then: 9 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 6 then: 3 then: 3 then: 3 then: 6 then: 6 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 6 then: 3 then: 3 then: 3 then: 3 then: 15 then: 6 then: 3 then: 3 then: 3 then: 3 then: 6 then: 3 then: 3 if attributes.length > 0 # with attributes
  18. 751 then: 4 then: 4 then: 8 then: 4 then: 4 then: 4 then: 8 then: 8 then: 4 then: 19 then: 4 then: 8 then: 4 then: 4 then: 4 then: 4 then: 18 then: 13 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 8 then: 5 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 8 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 4 then: 6 then: 9 then: 3 then: 6 then: 9 then: 6 then: 3 then: 6 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 6 then: 6 then: 3 then: 3 then: 6 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 3 then: 6 then: 6 then: 6 then: 3 then: 9 then: 3 then: 3 then: 3 then: 3 then: 3 then: 6 then: 3 then: 3 then: 3 if block_given # with content block
  19. 619 buffer << "<#{tag}"
  20. begin
  21. 619 buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
  22. ensure
  23. 619 buffer << ">"
  24. end
  25. begin
  26. 619 original_length = buffer.bytesize
  27. 619 content = yield(self)
  28. 619 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 9 else: 3 then: 22 else: 2 then: 7 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 11 else: 1 then: 25 else: 1 then: 3 else: 1 then: 3 else: 1 then: 11 else: 4 then: 7 else: 2 then: 13 else: 3 then: 7 else: 1 then: 18 else: 5 then: 11 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: 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: 4 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: 4 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: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 1 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 6 else: 0 then: 3 else: 0 then: 6 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 6 else: 0 then: 3 else: 0 then: 3 else: 0 then: 6 else: 0 then: 6 else: 0 then: 6 else: 0 then: 3 else: 0 then: 6 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 6 else: 0 then: 9 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 6 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 then: 3 else: 0 if original_length == buffer.bytesize
  29. 501 case content
  30. when: 4 when: 0 when: 0 when: 3 when: 4 when: 4 when: 7 when: 9 when: 0 when: 4 when: 21 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 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: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 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: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when ::Phlex::SGML::SafeObject
  31. buffer << content.to_s
  32. when: 6 when: 3 when: 3 when: 4 when: 3 when: 3 when: 4 when: 7 when: 3 when: 4 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: 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: 3 when: 3 when: 3 when: 3 when: 6 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: 6 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when: 3 when String
  33. 500 buffer << ::Phlex::Escape.html_escape(content)
  34. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 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
  35. buffer << ::Phlex::Escape.html_escape(content.name)
  36. when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 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
  37. nil
  38. else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 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: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else
  39. 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: 4 else: 0 then: 0 else: 0 then: 4 else: 0 then: 0 else: 0 then: 3 else: 1 then: 0 else: 0 then: 4 else: 0 then: 7 else: 1 then: 0 else: 10 else: 0 then: 4 else: 0 then: 9 else: 9 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 4 else: 3 else: 0 then: 0 else: 0 then: 3 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: 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: 1 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: 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: 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: 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: 0 else: 0 if (formatted_object = format_object(content))
  40. buffer << ::Phlex::Escape.html_escape(formatted_object)
  41. end
  42. end
  43. end
  44. ensure
  45. 619 buffer << "</#{tag}>"
  46. end
  47. else: 8 else: 0 else: 0 else: 0 else: 0 else: 3 else: 0 else: 3 else: 0 else: 1 else: 0 else: 0 else: 0 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: 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: 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: 6 else: 0 else: 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 # without content
  48. 132 buffer << "<#{tag}"
  49. begin
  50. 132 buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
  51. ensure
  52. 132 buffer << "></#{tag}>"
  53. end
  54. end
  55. else: 0 else: 0 else: 0 else: 4 else: 3 else: 6 else: 19 else: 4 else: 0 else: 0 else: 0 else: 0 else: 13 else: 0 else: 0 else: 0 else: 127 else: 0 else: 3 else: 0 else: 24 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 2 else: 0 else: 0 else: 1 else: 0 else: 0 else: 0 else: 0 else: 0 else: 2 else: 9 else: 0 else: 0 else: 0 else: 0 else: 2 else: 3 else: 0 else: 0 else: 0 else: 1 else: 0 else: 0 else: 1 else: 0 else: 9 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 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 # without attributes
  56. 200 then: 0 then: 0 then: 0 then: 4 then: 7 then: 0 then: 0 then: 8 then: 0 then: 0 then: 0 then: 4 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 4 then: 2 then: 0 then: 0 then: 0 then: 8 then: 0 then: 0 then: 0 then: 0 then: 0 then: 127 then: 0 then: 0 then: 4 then: 12 then: 0 then: 0 then: 1 then: 7 then: 3 then: 0 then: 24 then: 0 then: 0 then: 24 then: 0 then: 4 then: 0 then: 0 then: 4 then: 0 then: 0 then: 0 then: 0 then: 0 then: 4 then: 6 then: 0 then: 4 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 2 then: 0 then: 9 then: 0 then: 4 then: 0 then: 0 then: 3 then: 0 then: 4 then: 0 then: 0 then: 1 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 0 then: 4 then: 0 then: 1 then: 0 then: 0 then: 9 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: 0 then: 0 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: 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: 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 then: 0 if block_given # with content block
  57. 194 buffer << "<#{tag}>"
  58. begin
  59. 194 original_length = buffer.bytesize
  60. 194 content = yield(self)
  61. 195 then: 0 else: 0 then: 0 else: 0 then: 0 else: 4 then: 4 else: 0 then: 10 else: 8 then: 18 else: 5 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 7 else: 18 then: 0 else: 0 then: 0 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: 127 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 1 then: 11 else: 0 then: 4 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: 0 else: 0 then: 0 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: 1 else: 0 then: 0 else: 0 then: 9 else: 2 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: 4 else: 0 then: 0 else: 0 then: 1 else: 0 then: 0 else: 1 then: 1 else: 8 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: 3 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: 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 original_length == buffer.bytesize
  62. 168 case content
  63. when: 0 when: 3 when: 0 when: 18 when: 4 when: 4 when: 0 when: 20 when: 12 when: 135 when: 0 when: 4 when: 0 when: 0 when: 0 when: 0 when: 7 when: 0 when: 0 when: 0 when: 0 when: 4 when: 0 when: 7 when: 4 when: 0 when: 4 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: 3 when: 0 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: 3 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: 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: 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: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when ::Phlex::SGML::SafeObject
  64. 1 buffer << content.to_s
  65. when: 0 when: 1 when: 0 when: 5 when: 0 when: 3 when: 0 when: 3 when: 0 when: 7 when: 0 when: 7 when: 0 when: 0 when: 0 when: 0 when: 1 when: 0 when: 0 when: 0 when: 0 when: 142 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: 1 when: 0 when: 0 when: 2 when: 0 when: 0 when: 1 when: 4 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: 1 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: 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: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 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: 0 when: 0 when String
  66. 140 buffer << ::Phlex::Escape.html_escape(content)
  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: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when Symbol
  68. buffer << ::Phlex::Escape.html_escape(content.name)
  69. when: 0 when: 0 when: 0 when: 0 when: 0 when: 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: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 0 when: 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
  70. nil
  71. else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 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: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 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: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else
  72. 25 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 else: 0 then: 0 else: 0 then: 15 else: 4 then: 10 else: 8 then: 4 else: 0 then: 0 else: 0 then: 0 else: 0 then: 5 else: 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: 4 else: 0 then: 0 else: 0 then: 8 else: 6 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 then: 0 else: 0 else: 0 then: 0 else: 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: 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 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: 0 else: 3 then: 6 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: 6 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: 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 if (formatted_object = format_object(content))
  73. 24 buffer << ::Phlex::Escape.html_escape(formatted_object)
  74. end
  75. end
  76. end
  77. ensure
  78. 194 buffer << "</#{tag}>"
  79. end
  80. 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: 5 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 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: 3 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: 9 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 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: 6 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: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 0 else: 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
  81. 6 buffer << "<#{tag}></#{tag}>"
  82. end
  83. end
  84. 5 then: 1 else: 165 #{'flush' if tag == 'head'}
  85. 921 nil
  86. end
  87. RUBY
  88. 166 __registered_elements__[method_name] = tag
  89. 166 method_name
  90. end
  91. 1 def __register_void_element__(method_name, tag: method_name.name.tr("_", "-"))
  92. 12 class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
  93. # frozen_string_literal: true
  94. 1 def #{method_name}(**attributes)
  95. 46 state = @_state
  96. 46 else: 17 then: 4 else: 13 then: 0 else: 12 then: 0 return unless state.should_render?
  97. 42 buffer = state.buffer
  98. 42 then: 3 then: 10 then: 7 then: 14 then: 18 then: 20 then: 4 then: 3 then: 3 then: 11 if attributes.length > 0 # with attributes
  99. 42 buffer << "<#{tag}"
  100. begin
  101. 42 buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes))
  102. ensure
  103. 42 buffer << ">"
  104. end
  105. else: 0 else: 0 else: 0 else: 0 else: 123 else: 0 else: 0 else: 0 else: 0 else: 0 else # without attributes
  106. buffer << "<#{tag}>"
  107. end
  108. 42 nil
  109. end
  110. RUBY
  111. 12 __registered_elements__[method_name] = tag
  112. 12 method_name
  113. end
  114. 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. 11 @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. 1535 @buffer = +""
  5. 1535 @capturing = false
  6. 1535 @user_context = user_context
  7. 1535 @fragments = fragments
  8. 1535 @fragment_depth = 0
  9. 1535 @cache_stack = []
  10. 1535 @halt_signal = nil
  11. 1535 @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. 1551 stack = @stack
  17. 1551 then: 1537 if !@fragments || @halt_signal
  18. 1537 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. 2131 !@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. 5 then: 0 else: 5 return if capturing
  94. 5 buffer = @buffer
  95. 5 @output_buffer << buffer.dup
  96. 5 buffer.clear
  97. 5 nil
  98. end
  99. end

lib/phlex/svg.rb

83.78% lines covered

65.0% branches covered

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