loading
Generated 2025-10-08T23:58:39+00:00

All Files ( 100.0% covered at 2.16 hits/line )

5 files in total.
63 relevant lines, 63 lines covered and 0 lines missed. ( 100.0% )
4 total branches, 4 branches covered and 0 branches missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/htmx.rb 100.00 % 60 21 21 0 1.33 100.00 % 0 0 0
lib/htmx/error.rb 100.00 % 7 2 2 0 1.00 100.00 % 0 0 0
lib/htmx/headers/request.rb 100.00 % 39 15 15 0 2.40 100.00 % 2 2 0
lib/htmx/headers/response.rb 100.00 % 31 10 10 0 2.10 100.00 % 0 0 0
lib/htmx/prefixer.rb 100.00 % 30 15 15 0 3.27 100.00 % 2 2 0

lib/htmx.rb

100.0% lines covered

100.0% branches covered

21 relevant lines. 21 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "zeitwerk"
  3. 1 Zeitwerk::Loader.new.then do |loader|
  4. 1 loader.inflector.inflect "htmx" => "HTMX"
  5. 1 loader.tag = File.basename __FILE__, ".rb"
  6. 1 loader.push_dir __dir__
  7. 1 loader.setup
  8. end
  9. # Main namespace.
  10. 1 module HTMX
  11. 1 REQUEST_MAP = {
  12. boosted: "HTTP_HX_BOOSTED",
  13. current_url: "HTTP_HX_CURRENT_URL",
  14. history_restore_request: "HTTP_HX_HISTORY_RESTORE_REQUEST",
  15. prompt: "HTTP_HX_PROMPT",
  16. request: "HTTP_HX_REQUEST",
  17. target: "HTTP_HX_TARGET",
  18. trigger_name: "HTTP_HX_TRIGGER_NAME",
  19. trigger: "HTTP_HX_TRIGGER"
  20. }.freeze
  21. 1 RESPONSE_MAP = {
  22. location: "HX-Location",
  23. push_url: "HX-Push-Url",
  24. redirect: "HX-Redirect",
  25. refresh: "HX-Refresh",
  26. replace_url: "HX-Replace-Url",
  27. reswap: "HX-Reswap",
  28. retarget: "HX-Retarget",
  29. trigger: "HX-Trigger",
  30. trigger_after_settle: "HX-Trigger-After-Settle",
  31. trigger_after_swap: "HX-Trigger-After-Swap"
  32. }.freeze
  33. 1 def self.loader registry = Zeitwerk::Registry
  34. 4 @loader ||= registry.loaders.each.find { |loader| loader.tag == File.basename(__FILE__, ".rb") }
  35. end
  36. 1 def self.[](...)
  37. 2 @prefixer ||= Prefixer.new
  38. 2 @prefixer.call(...)
  39. end
  40. 1 def self.request(**) = Headers::Request.for(**)
  41. 1 def self.request!(headers, **attributes) = headers.merge! attributes.transform_keys!(REQUEST_MAP)
  42. 1 def self.request?(headers, key, value) = headers[REQUEST_MAP[key]] == value
  43. 1 def self.response(**) = Headers::Response.for(**)
  44. 1 def self.response!(headers, **attributes)
  45. 3 headers.merge! attributes.transform_keys!(RESPONSE_MAP)
  46. end
  47. 1 def self.response?(headers, key, value) = headers[RESPONSE_MAP[key]] == value
  48. end

lib/htmx/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 module HTMX
  3. # The root error for all errors within this library.
  4. 1 class Error < StandardError
  5. end
  6. end

lib/htmx/headers/request.rb

100.0% lines covered

100.0% branches covered

15 relevant lines. 15 lines covered and 0 lines missed.
2 total branches, 2 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "refinements/string"
  3. 1 module HTMX
  4. 1 module Headers
  5. # Models the supported HTMX request headers.
  6. 1 Request = Data.define(*REQUEST_MAP.keys) do
  7. 1 using Refinements::String
  8. 1 def self.for(key_map: REQUEST_MAP.invert, **attributes)
  9. 3 new(**attributes.slice(*key_map.keys).transform_keys!(key_map))
  10. end
  11. 1 def self.key_for(header, key_map: REQUEST_MAP.invert) = key_map.fetch header
  12. 1 def self.header_for(key, key_map: REQUEST_MAP) = key_map.fetch key
  13. 1 def initialize boosted: nil,
  14. current_url: nil,
  15. history_restore_request: nil,
  16. prompt: nil,
  17. request: nil,
  18. target: nil,
  19. trigger_name: nil,
  20. trigger: nil
  21. 17 super
  22. end
  23. 1 def boosted? = boosted == "true"
  24. 4 then: 2 else: 1 def confirmed? = prompt ? prompt.truthy? : false
  25. 1 def history_restore_request? = history_restore_request == "true"
  26. 1 def request? = request == "true"
  27. end
  28. end
  29. end

lib/htmx/headers/response.rb

100.0% lines covered

100.0% branches covered

10 relevant lines. 10 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module HTMX
  3. 1 module Headers
  4. # Models the supported HTMX response headers.
  5. 1 Response = Data.define(*RESPONSE_MAP.keys) do
  6. 1 def self.for(key_map: RESPONSE_MAP.invert, **attributes)
  7. 3 new(**attributes.slice(*key_map.keys).transform_keys!(key_map))
  8. end
  9. 1 def self.key_for(header, key_map: RESPONSE_MAP.invert) = key_map.fetch header
  10. 1 def self.header_for(key, key_map: RESPONSE_MAP) = key_map.fetch key
  11. 1 def initialize location: nil,
  12. push_url: nil,
  13. redirect: nil,
  14. refresh: nil,
  15. replace_url: nil,
  16. reswap: nil,
  17. retarget: nil,
  18. trigger: nil,
  19. trigger_after_settle: nil,
  20. trigger_after_swap: nil
  21. 10 super
  22. end
  23. 1 def refresh? = refresh == "true"
  24. end
  25. end
  26. end

lib/htmx/prefixer.rb

100.0% lines covered

100.0% branches covered

15 relevant lines. 15 lines covered and 0 lines missed.
2 total branches, 2 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "refinements/array"
  3. 1 module HTMX
  4. # Prefixes HTML element attributes for proper consumption by the HTMX JavaScript library.
  5. 1 class Prefixer
  6. 1 using Refinements::Array
  7. 1 ALLOWED = %w[hx data-hx].freeze
  8. 1 def initialize default = "hx", allowed: ALLOWED
  9. 8 @default = default
  10. 8 @allowed = allowed
  11. 8 validate
  12. end
  13. 7 def call(**attributes) = attributes.transform_keys! { |key| "#{default}-#{key}".tr "_", "-" }
  14. 1 private
  15. 1 attr_reader :default, :allowed
  16. 1 def validate
  17. 8 then: 7 else: 1 return true if allowed.include? default
  18. 1 fail Error, %(Invalid prefix: #{default.inspect}. Use: #{allowed.to_usage "or"}.)
  19. end
  20. end
  21. end