# frozen_string_literal: true require "delegate" require "http/response/status/reasons" module HTTP class Response class Status < ::Delegator class << self # Coerces given value to Status. # # @example # # Status.coerce(:bad_request) # => Status.new(400) # Status.coerce("400") # => Status.new(400) # Status.coerce(true) # => raises HTTP::Error # # @raise [Error] if coercion is impossible # @param [Symbol, #to_i] object # @return [Status] def coerce(object) code = case when object.is_a?(String) then SYMBOL_CODES[symbolize object] when object.is_a?(Symbol) then SYMBOL_CODES[object] when object.is_a?(Numeric) then object.to_i end return new code if code raise Error, "Can't coerce #{object.class}(#{object}) to #{self}" end alias [] coerce private # Symbolizes given string # # @example # # symbolize "Bad Request" # => :bad_request # symbolize "Request-URI Too Long" # => :request_uri_too_long # symbolize "I'm a Teapot" # => :im_a_teapot # # @param [#to_s] str # @return [Symbol] def symbolize(str) str.to_s.downcase.tr("-", " ").gsub(/[^a-z ]/, "").gsub(/\s+/, "_").to_sym end end # Code to Symbol map # # @example Usage # # SYMBOLS[400] # => :bad_request # SYMBOLS[414] # => :request_uri_too_long # SYMBOLS[418] # => :im_a_teapot # # @return [Hash Symbol>] SYMBOLS = REASONS.transform_values { |v| symbolize(v) }.freeze # Reversed {SYMBOLS} map. # # @example Usage # # SYMBOL_CODES[:bad_request] # => 400 # SYMBOL_CODES[:request_uri_too_long] # => 414 # SYMBOL_CODES[:im_a_teapot] # => 418 # # @return [Hash Fixnum>] SYMBOL_CODES = SYMBOLS.to_h { |k, v| [v, k] }.freeze # @return [Fixnum] status code attr_reader :code # @see REASONS # @return [String, nil] status message def reason REASONS[code] end # @return [String] string representation of HTTP status def to_s "#{code} #{reason}".strip end # Check if status code is informational (1XX) # @return [Boolean] def informational? 100 <= code && code < 200 end # Check if status code is successful (2XX) # @return [Boolean] def success? 200 <= code && code < 300 end # Check if status code is redirection (3XX) # @return [Boolean] def redirect? 300 <= code && code < 400 end # Check if status code is client error (4XX) # @return [Boolean] def client_error? 400 <= code && code < 500 end # Check if status code is server error (5XX) # @return [Boolean] def server_error? 500 <= code && code < 600 end # Symbolized {#reason} # # @return [nil] unless code is well-known (see REASONS) # @return [Symbol] def to_sym SYMBOLS[code] end # Printable version of HTTP Status, surrounded by quote marks, # with special characters escaped. # # (see String#inspect) def inspect "#<#{self.class} #{self}>" end SYMBOLS.each do |code, symbol| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{symbol}? # def bad_request? #{code} == code # 400 == code end # end RUBY end def __setobj__(obj) raise TypeError, "Expected #{obj.inspect} to respond to #to_i" unless obj.respond_to? :to_i @code = obj.to_i end def __getobj__ @code end end end end