<NME> exceptions.rb
<BEF> # encoding: utf-8

require "erb"
require "extlib"

# TODO: documentation
# TODO: specs
SettingsNotFound = Class.new(StandardError)

class TemplateNotFound < StandardError

# superclass of all the controller exceptions
module Rango
  module Exceptions
    class HttpError < StandardError
      def self.name=(name)
        @name = name
      end

      def self.name
        @name
      end
# @since 0.0.2
SkipFilter = Class.new(StandardError)

# @since 0.0.2
SkipRoute = Class.new(StandardError)

# superclass of all the controller exceptions
module Rango
  module HttpExceptions
    class HttpError < StandardError
      # @since 0.0.1
      attr_accessor :status, :params

      # @since 0.0.1
      def initialize(status, params = nil)
        self.status = status
        self.params = params
      end

      # @since 0.0.1
      def headers
        {'Content-Type' => 'text/html'}
      end

      # @since 0.0.1
      def render
        content = Rango.path.join("../../templates/errors/#{self.status}.html.erb").read
        ERB.new(content).result(binding)
      end

      # @since 0.0.1
      def call(env)
        return [self.status, self.headers, self.body]
      end
    end

    class Error404 < HttpError
      # @since 0.0.1
      attr_accessor :params

      # @since 0.0.1
      def initialize(params = nil)
        super("404")
      end

      # @since 0.0.1
      def body
        @routes = Project.router.routes
        @router = Project.settings.router
        @path   = "TODO" # or maybe rack env ... THIS SHOULD BE IMPLEMENTED AS RACK MIDDLEWARE
        self.render
      end
    end

    class Error406 < HttpError
      # @since 0.0.1
      attr_accessor :params

      # @since 0.0.1
      def initialize(params)
        super("406")
      end

      # @since 0.0.1
      def body
        self.render
      end
    end

    class Error500 < HttpError
      # @since 0.0.1
      attr_accessor :exception, :params

      # @since 0.0.1
      def initialize(exception, params)
        super("500")
        self.exception = exception
        self.params = params
      end

      # @since 0.0.1
      def body
        self.render
        # ["<h1>#{exception.message}</h2>", '@exception.bactrace.join("<br />")']
      end
    end
  end
end


# stolen from merb

module Rango
  module HttpExceptions
    STATUS_CODES = {}
    class Base < StandardError #:doc:

      # === Returns
      # Integer:: The status-code of the error.
      #
      # @overridable
      # :api: plugin
      def status; self.class.status; end
      alias :to_i :status

      class << self

        # Get the actual status-code for an Exception class.
        #
        # As usual, this can come from a constant upwards in
        # the inheritance chain.
        #
        # ==== Returns
        # Fixnum:: The status code of this exception.
        #
        # :api: public
        def status
          const_get(:STATUS) rescue 0
        end
        alias :to_i :status

        # Set the actual status-code for an Exception class.
        #
        # If possible, set the STATUS constant, and update
        # any previously registered (inherited) status-code.
        #
        # ==== Parameters
        # num<~to_i>:: The status code
        #
        # ==== Returns
        # (Integer, nil):: The status set on this exception, or nil if a status was already set.
        #
        # :api: private
        def status=(num)
          unless self.status?
            register_status_code(self, num)
            self.const_set(:STATUS, num.to_i)
          end
        end

        # See if a status-code has been defined (on self explicitly).
        #
        # ==== Returns
        # Boolean:: Whether a status code has been set
        #
        # :api: private
        def status?
          self.const_defined?(:STATUS)
        end

        # Registers any subclasses with status codes for easy lookup by
        # set_status in Merb::Controller.
        #
        # Inheritance ensures this method gets inherited by any subclasses, so
        # it goes all the way down the chain of inheritance.
        #
        # ==== Parameters
        #
        # subclass<Merb::ControllerExceptions::Base>::
        #   The Exception class that is inheriting from Merb::ControllerExceptions::Base
        #
        # :api: public
        def inherited(subclass)
          # don't set the constant yet - any class methods will be called after self.inherited
          # unless self.status = ... is set explicitly, the status code will be inherited
          register_status_code(subclass, self.status) if self.status?
        end

        private

        # Register the status-code for an Exception class.
        #
        # ==== Parameters
        # num<~to_i>:: The status code
        #
        # :api: privaate
        def register_status_code(klass, code)
          name = self.to_s.split('::').last.snake_case
          STATUS_CODES[name.to_sym] = code.to_i
        end
      end
    end

    # informational
    class Informational               < Rango::HttpExceptions::Base; end
    class Continue                    < Rango::HttpExceptions::Informational; self.status = 100; end
    class SwitchingProtocols          < Rango::HttpExceptions::Informational; self.status = 101; end

    # successful
    class Successful                  < Rango::HttpExceptions::Base; end
    class OK                          < Rango::HttpExceptions::Successful; self.status = 200; end
    class Created                     < Rango::HttpExceptions::Successful; self.status = 201; end
    class Accepted                    < Rango::HttpExceptions::Successful; self.status = 202; end
    class NonAuthoritativeInformation < Rango::HttpExceptions::Successful; self.status = 203; end
    class NoContent                   < Rango::HttpExceptions::Successful; self.status = 204; end
    class ResetContent                < Rango::HttpExceptions::Successful; self.status = 205; end
    class PartialContent              < Rango::HttpExceptions::Successful; self.status = 206; end

    # redirection
    class Redirection                 < Rango::HttpExceptions::Base; end
    class MultipleChoices             < Rango::HttpExceptions::Redirection; self.status = 300; end
    class MovedPermanently            < Rango::HttpExceptions::Redirection; self.status = 301; end
    class MovedTemporarily            < Rango::HttpExceptions::Redirection; self.status = 302; end
    class SeeOther                    < Rango::HttpExceptions::Redirection; self.status = 303; end
    class NotModified                 < Rango::HttpExceptions::Redirection; self.status = 304; end
    class UseProxy                    < Rango::HttpExceptions::Redirection; self.status = 305; end
    class TemporaryRedirect           < Rango::HttpExceptions::Redirection; self.status = 307; end

    # client error
    class ClientError                 < Rango::HttpExceptions::Base; end
    class BadRequest                  < Rango::HttpExceptions::ClientError; self.status = 400; end
    class MultiPartParseError         < Rango::HttpExceptions::BadRequest; end
    class Unauthorized                < Rango::HttpExceptions::ClientError; self.status = 401; end
    class PaymentRequired             < Rango::HttpExceptions::ClientError; self.status = 402; end
    class Forbidden                   < Rango::HttpExceptions::ClientError; self.status = 403; end
    class NotFound                    < Rango::HttpExceptions::ClientError; self.status = 404; end
    class ActionNotFound              < Rango::HttpExceptions::NotFound; end
    class TemplateNotFound            < Rango::HttpExceptions::NotFound; end
    class LayoutNotFound              < Rango::HttpExceptions::NotFound; end
    class MethodNotAllowed            < Rango::HttpExceptions::ClientError; self.status = 405; end
    class NotAcceptable               < Rango::HttpExceptions::ClientError; self.status = 406; end
    class ProxyAuthenticationRequired < Rango::HttpExceptions::ClientError; self.status = 407; end
    class RequestTimeout              < Rango::HttpExceptions::ClientError; self.status = 408; end
    class Conflict                    < Rango::HttpExceptions::ClientError; self.status = 409; end
    class Gone                        < Rango::HttpExceptions::ClientError; self.status = 410; end
    class LengthRequired              < Rango::HttpExceptions::ClientError; self.status = 411; end
    class PreconditionFailed          < Rango::HttpExceptions::ClientError; self.status = 412; end
    class RequestEntityTooLarge       < Rango::HttpExceptions::ClientError; self.status = 413; end
    class RequestURITooLarge          < Rango::HttpExceptions::ClientError; self.status = 414; end
    class UnsupportedMediaType        < Rango::HttpExceptions::ClientError; self.status = 415; end
    class RequestRangeNotSatisfiable  < Rango::HttpExceptions::ClientError; self.status = 416; end
    class ExpectationFailed           < Rango::HttpExceptions::ClientError; self.status = 417; end

    # server error
    class ServerError                 < Rango::HttpExceptions::Base; end
    class InternalServerError         < Rango::HttpExceptions::ServerError; self.status = 500; end
    class NotImplemented              < Rango::HttpExceptions::ServerError; self.status = 501; end
    class BadGateway                  < Rango::HttpExceptions::ServerError; self.status = 502; end
    class ServiceUnavailable          < Rango::HttpExceptions::ServerError; self.status = 503; end
    class GatewayTimeout              < Rango::HttpExceptions::ServerError; self.status = 504; end
    class HTTPVersionNotSupported     < Rango::HttpExceptions::ServerError; self.status = 505; end
  end
end
      def to_response
        super.tap do |response|
          response[2] = [self.body]
        end
      end

      # use raise MovedPermanently, "http://example.com"
      # Yes, you can use just redirect method from the controller, but
      # this will work even in filters or in environments without controllers
      # raise Redirection.new("/", 301)
      def initialize(location, status = nil)
        super(location, status)
        location = URI.escape(location)
        headers["Location"] = location
      end
    end

    Error300 = MultipleChoices = Class.new(Redirection)             { self.status ||= 300; self.name ||= "Multiple Choices" }
    Error301 = MovedPermanently = Class.new(Redirection)            { self.status ||= 301; self.name ||= "Moved Permanently" }
    Error302 = MovedTemporarily = Class.new(Redirection)            { self.status ||= 302; self.name ||= "Moved Temporarily" }
    Error303 = SeeOther = Class.new(Redirection)                    { self.status ||= 303; self.name ||= "See Other" } # this is the redirect you want to use after POST
    Error304 = NotModified = Class.new(Redirection)                 { self.status ||= 304; self.name ||= "Not Modified" }
    Error305 = UseProxy = Class.new(Redirection)                    { self.status ||= 305; self.name ||= "Use Proxy" }
    Error307 = TemporaryRedirect = Class.new(Redirection)           { self.status ||= 307; self.name ||= "Temporary Redirect" }

    # client error
    ClientError = Class.new(HttpError)
    Error400 = BadRequest = Class.new(ClientError)                  { self.status ||= 400; self.name ||= "Bad Request" }
    MultiPartParseError = Class.new(BadRequest)
    Error401 = Unauthorized = Class.new(ClientError)                { self.status ||= 401; self.name ||= "Unauthorized" }
    Error402 = PaymentRequired = Class.new(ClientError)             { self.status ||= 402; self.name ||= "Payment Required" }
    Error403 = Forbidden = Class.new(ClientError)                   { self.status ||= 403; self.name ||= "Forbidden" }
    Error404 = NotFound = Class.new(ClientError)                    { self.status ||= 404; self.name ||= "Not Found" }
    ActionNotFound = Class.new(NotFound)
    Error405 = MethodNotAllowed = Class.new(ClientError)            { self.status ||= 405; self.name ||= "Method Not Allowed" }
    Error406 = NotAcceptable = Class.new(ClientError)               { self.status ||= 406; self.name ||= "Not Acceptable" }
    Error407 = ProxyAuthenticationRequired = Class.new(ClientError) { self.status ||= 407; self.name ||= "Proxy Authentication Required" }
    Error408 = RequestTimeout = Class.new(ClientError)              { self.status ||= 408; self.name ||= "Request Timeout" }
    Error409 = Conflict = Class.new(ClientError)                    { self.status ||= 409; self.name ||= "Conflict" }
    Error410 = Gone = Class.new(ClientError)                        { self.status ||= 410; self.name ||= "Gone" }
    Error411 = LengthRequired = Class.new(ClientError)              { self.status ||= 411; self.name ||= "Length Required" }
    Error412 = PreconditionFailed = Class.new(ClientError)          { self.status ||= 412; self.name ||= "Precondition Failed" }
    Error413 = RequestEntityTooLarge = Class.new(ClientError)       { self.status ||= 413; self.name ||= "Request Entity Too Large" }
    Error414 = RequestURITooLarge = Class.new(ClientError)          { self.status ||= 414; self.name ||= "Request URI Too Large" }
    Error415 = UnsupportedMediaType = Class.new(ClientError)        { self.status ||= 415; self.name ||= "Unsupported Media Type" }
    Error416 = RequestRangeNotSatisfiable = Class.new(ClientError)  { self.status ||= 416; self.name ||= "Request Range Not Satisfiable" }
    Error417 = ExpectationFailed = Class.new(ClientError)           { self.status ||= 417; self.name ||= "Expectation Failed" }

    # server error
    ServerError = Class.new(HttpError)
    Error500 = InternalServerError = Class.new(ServerError)         { self.status ||= 500; self.name ||= "Internal Server Error" }
    Error501 = NotImplemented = Class.new(ServerError)              { self.status ||= 501; self.name ||= "Not Implemented" }
    Error502 = BadGateway = Class.new(ServerError)                  { self.status ||= 502; self.name ||= "Bad Gateway" }
    Error503 = ServiceUnavailable = Class.new(ServerError)          { self.status ||= 503; self.name ||= "Service Unavailable" }
    Error504 = GatewayTimeout = Class.new(ServerError)              { self.status ||= 504; self.name ||= "Gateway Timeout" }
    Error505 = HTTPVersionNotSupported = Class.new(ServerError)     { self.status ||= 505; self.name ||= "HTTP Version Not Supported" }
  end
end

<MSG> Refactored exceptions

<DFF> @@ -1,10 +1,5 @@
 # encoding: utf-8
 
-require "erb"
-require "extlib"
-
-# TODO: documentation
-# TODO: specs
 SettingsNotFound = Class.new(StandardError)
 
 class TemplateNotFound < StandardError
@@ -23,243 +18,102 @@ AnyStrategyMatched = Class.new(StandardError)
 # @since 0.0.2
 SkipFilter = Class.new(StandardError)
 
-# @since 0.0.2
-SkipRoute = Class.new(StandardError)
-
 # superclass of all the controller exceptions
 module Rango
-  module HttpExceptions
-    class HttpError < StandardError
-      # @since 0.0.1
-      attr_accessor :status, :params
+  class HttpError < StandardError
+    CONTENT_TYPE ||= "text/plain"
 
-      # @since 0.0.1
-      def initialize(status, params = nil)
-        self.status = status
-        self.params = params
-      end
-
-      # @since 0.0.1
-      def headers
-        {'Content-Type' => 'text/html'}
-      end
-
-      # @since 0.0.1
-      def render
-        content = Rango.path.join("../../templates/errors/#{self.status}.html.erb").read
-        ERB.new(content).result(binding)
-      end
-
-      # @since 0.0.1
-      def call(env)
-        return [self.status, self.headers, self.body]
-      end
+    attr_reader :content_type, :headers
+    def initialize(*args)
+      @headers = Hash.new
+      super(*args)
     end
 
-    class Error404 < HttpError
-      # @since 0.0.1
-      attr_accessor :params
-
-      # @since 0.0.1
-      def initialize(params = nil)
-        super("404")
-      end
-
-      # @since 0.0.1
-      def body
-        @routes = Project.router.routes
-        @router = Project.settings.router
-        @path   = "TODO" # or maybe rack env ... THIS SHOULD BE IMPLEMENTED AS RACK MIDDLEWARE
-        self.render
-      end
+    def inspect
+      self.to_response.inspect
     end
 
-    class Error406 < HttpError
-      # @since 0.0.1
-      attr_accessor :params
-
-      # @since 0.0.1
-      def initialize(params)
-        super("406")
-      end
-
-      # @since 0.0.1
-      def body
-        self.render
-      end
+    # @returns [String]
+    # @example NotFound.new.to_snakecase # "not_found"
+    def to_snakecase
+      self.class.name.snake_case
     end
 
-    class Error500 < HttpError
-      # @since 0.0.1
-      attr_accessor :exception, :params
-
-      # @since 0.0.1
-      def initialize(exception, params)
-        super("500")
-        self.exception = exception
-        self.params = params
-      end
-
-      # @since 0.0.1
-      def body
-        self.render
-        # ["<h1>#{exception.message}</h2>", '@exception.bactrace.join("<br />")']
-      end
+    def status() self.class::STATUS end
+    def content_type
+      @content_type ||= self.class::CONTENT_TYPE
     end
-  end
-end
-
-
-# stolen from merb
-
-module Rango
-  module HttpExceptions
-    STATUS_CODES = {}
-    class Base < StandardError #:doc:
-
-      # === Returns
-      # Integer:: The status-code of the error.
-      #
-      # @overridable
-      # :api: plugin
-      def status; self.class.status; end
-      alias :to_i :status
-
-      class << self
-
-        # Get the actual status-code for an Exception class.
-        #
-        # As usual, this can come from a constant upwards in
-        # the inheritance chain.
-        #
-        # ==== Returns
-        # Fixnum:: The status code of this exception.
-        #
-        # :api: public
-        def status
-          const_get(:STATUS) rescue 0
-        end
-        alias :to_i :status
 
-        # Set the actual status-code for an Exception class.
-        #
-        # If possible, set the STATUS constant, and update
-        # any previously registered (inherited) status-code.
-        #
-        # ==== Parameters
-        # num<~to_i>:: The status code
-        #
-        # ==== Returns
-        # (Integer, nil):: The status set on this exception, or nil if a status was already set.
-        #
-        # :api: private
-        def status=(num)
-          unless self.status?
-            register_status_code(self, num)
-            self.const_set(:STATUS, num.to_i)
-          end
-        end
-
-        # See if a status-code has been defined (on self explicitly).
-        #
-        # ==== Returns
-        # Boolean:: Whether a status code has been set
-        #
-        # :api: private
-        def status?
-          self.const_defined?(:STATUS)
-        end
-
-        # Registers any subclasses with status codes for easy lookup by
-        # set_status in Merb::Controller.
-        #
-        # Inheritance ensures this method gets inherited by any subclasses, so
-        # it goes all the way down the chain of inheritance.
-        #
-        # ==== Parameters
-        #
-        # subclass<Merb::ControllerExceptions::Base>::
-        #   The Exception class that is inheriting from Merb::ControllerExceptions::Base
-        #
-        # :api: public
-        def inherited(subclass)
-          # don't set the constant yet - any class methods will be called after self.inherited
-          # unless self.status = ... is set explicitly, the status code will be inherited
-          register_status_code(subclass, self.status) if self.status?
-        end
-
-        private
+    def to_response
+      headers = {"Content-Type" => self.content_type}.merge(self.headers)
+      [self.status, self.headers, [self.message]]
+    end
 
-        # Register the status-code for an Exception class.
-        #
-        # ==== Parameters
-        # num<~to_i>:: The status code
-        #
-        # :api: privaate
-        def register_status_code(klass, code)
-          name = self.to_s.split('::').last.snake_case
-          STATUS_CODES[name.to_sym] = code.to_i
-        end
+    def self.const_missing(name)
+      if [:STATUS, :CONTENT_TYPE].include?(name)
+        raise "Every descent of HTTPError class has to have defined constant #{name}."
+      else
+        super(name)
       end
     end
-
-    # informational
-    class Informational               < Rango::HttpExceptions::Base; end
-    class Continue                    < Rango::HttpExceptions::Informational; self.status = 100; end
-    class SwitchingProtocols          < Rango::HttpExceptions::Informational; self.status = 101; end
-
-    # successful
-    class Successful                  < Rango::HttpExceptions::Base; end
-    class OK                          < Rango::HttpExceptions::Successful; self.status = 200; end
-    class Created                     < Rango::HttpExceptions::Successful; self.status = 201; end
-    class Accepted                    < Rango::HttpExceptions::Successful; self.status = 202; end
-    class NonAuthoritativeInformation < Rango::HttpExceptions::Successful; self.status = 203; end
-    class NoContent                   < Rango::HttpExceptions::Successful; self.status = 204; end
-    class ResetContent                < Rango::HttpExceptions::Successful; self.status = 205; end
-    class PartialContent              < Rango::HttpExceptions::Successful; self.status = 206; end
-
-    # redirection
-    class Redirection                 < Rango::HttpExceptions::Base; end
-    class MultipleChoices             < Rango::HttpExceptions::Redirection; self.status = 300; end
-    class MovedPermanently            < Rango::HttpExceptions::Redirection; self.status = 301; end
-    class MovedTemporarily            < Rango::HttpExceptions::Redirection; self.status = 302; end
-    class SeeOther                    < Rango::HttpExceptions::Redirection; self.status = 303; end
-    class NotModified                 < Rango::HttpExceptions::Redirection; self.status = 304; end
-    class UseProxy                    < Rango::HttpExceptions::Redirection; self.status = 305; end
-    class TemporaryRedirect           < Rango::HttpExceptions::Redirection; self.status = 307; end
-
-    # client error
-    class ClientError                 < Rango::HttpExceptions::Base; end
-    class BadRequest                  < Rango::HttpExceptions::ClientError; self.status = 400; end
-    class MultiPartParseError         < Rango::HttpExceptions::BadRequest; end
-    class Unauthorized                < Rango::HttpExceptions::ClientError; self.status = 401; end
-    class PaymentRequired             < Rango::HttpExceptions::ClientError; self.status = 402; end
-    class Forbidden                   < Rango::HttpExceptions::ClientError; self.status = 403; end
-    class NotFound                    < Rango::HttpExceptions::ClientError; self.status = 404; end
-    class ActionNotFound              < Rango::HttpExceptions::NotFound; end
-    class TemplateNotFound            < Rango::HttpExceptions::NotFound; end
-    class LayoutNotFound              < Rango::HttpExceptions::NotFound; end
-    class MethodNotAllowed            < Rango::HttpExceptions::ClientError; self.status = 405; end
-    class NotAcceptable               < Rango::HttpExceptions::ClientError; self.status = 406; end
-    class ProxyAuthenticationRequired < Rango::HttpExceptions::ClientError; self.status = 407; end
-    class RequestTimeout              < Rango::HttpExceptions::ClientError; self.status = 408; end
-    class Conflict                    < Rango::HttpExceptions::ClientError; self.status = 409; end
-    class Gone                        < Rango::HttpExceptions::ClientError; self.status = 410; end
-    class LengthRequired              < Rango::HttpExceptions::ClientError; self.status = 411; end
-    class PreconditionFailed          < Rango::HttpExceptions::ClientError; self.status = 412; end
-    class RequestEntityTooLarge       < Rango::HttpExceptions::ClientError; self.status = 413; end
-    class RequestURITooLarge          < Rango::HttpExceptions::ClientError; self.status = 414; end
-    class UnsupportedMediaType        < Rango::HttpExceptions::ClientError; self.status = 415; end
-    class RequestRangeNotSatisfiable  < Rango::HttpExceptions::ClientError; self.status = 416; end
-    class ExpectationFailed           < Rango::HttpExceptions::ClientError; self.status = 417; end
-
-    # server error
-    class ServerError                 < Rango::HttpExceptions::Base; end
-    class InternalServerError         < Rango::HttpExceptions::ServerError; self.status = 500; end
-    class NotImplemented              < Rango::HttpExceptions::ServerError; self.status = 501; end
-    class BadGateway                  < Rango::HttpExceptions::ServerError; self.status = 502; end
-    class ServiceUnavailable          < Rango::HttpExceptions::ServerError; self.status = 503; end
-    class GatewayTimeout              < Rango::HttpExceptions::ServerError; self.status = 504; end
-    class HTTPVersionNotSupported     < Rango::HttpExceptions::ServerError; self.status = 505; end
   end
+
+  # informational
+  class Informational               < Rango::HttpError; end
+  class Continue                    < Rango::Informational; STATUS ||= 100; end
+  class SwitchingProtocols          < Rango::Informational; STATUS ||= 101; end
+
+  # successful
+  class Successful                  < Rango::HttpError; end
+  class OK                          < Rango::Successful; STATUS ||= 200; end
+  class Created                     < Rango::Successful; STATUS ||= 201; end
+  class Accepted                    < Rango::Successful; STATUS ||= 202; end
+  class NonAuthoritativeInformation < Rango::Successful; STATUS ||= 203; end
+  class NoContent                   < Rango::Successful; STATUS ||= 204; end
+  class ResetContent                < Rango::Successful; STATUS ||= 205; end
+  class PartialContent              < Rango::Successful; STATUS ||= 206; end
+
+  # redirection
+  class Redirection                 < Rango::HttpError; end
+  class MultipleChoices             < Rango::Redirection; STATUS ||= 300; end
+  class MovedPermanently            < Rango::Redirection; STATUS ||= 301; end
+  class MovedTemporarily            < Rango::Redirection; STATUS ||= 302; end
+  class SeeOther                    < Rango::Redirection; STATUS ||= 303; end
+  class NotModified                 < Rango::Redirection; STATUS ||= 304; end
+  class UseProxy                    < Rango::Redirection; STATUS ||= 305; end
+  class TemporaryRedirect           < Rango::Redirection; STATUS ||= 307; end
+
+  # client error
+  class ClientError                 < Rango::HttpError; end
+  class BadRequest                  < Rango::ClientError; STATUS ||= 400; end
+  class MultiPartParseError         < Rango::BadRequest; end
+  class Unauthorized                < Rango::ClientError; STATUS ||= 401; end
+  class PaymentRequired             < Rango::ClientError; STATUS ||= 402; end
+  class Forbidden                   < Rango::ClientError; STATUS ||= 403; end
+  class NotFound                    < Rango::ClientError; STATUS ||= 404; end
+  class ActionNotFound              < Rango::NotFound; end
+  class TemplateNotFound            < Rango::NotFound; end
+  class LayoutNotFound              < Rango::NotFound; end
+  class MethodNotAllowed            < Rango::ClientError; STATUS ||= 405; end
+  class NotAcceptable               < Rango::ClientError; STATUS ||= 406; end
+  class ProxyAuthenticationRequired < Rango::ClientError; STATUS ||= 407; end
+  class RequestTimeout              < Rango::ClientError; STATUS ||= 408; end
+  class Conflict                    < Rango::ClientError; STATUS ||= 409; end
+  class Gone                        < Rango::ClientError; STATUS ||= 410; end
+  class LengthRequired              < Rango::ClientError; STATUS ||= 411; end
+  class PreconditionFailed          < Rango::ClientError; STATUS ||= 412; end
+  class RequestEntityTooLarge       < Rango::ClientError; STATUS ||= 413; end
+  class RequestURITooLarge          < Rango::ClientError; STATUS ||= 414; end
+  class UnsupportedMediaType        < Rango::ClientError; STATUS ||= 415; end
+  class RequestRangeNotSatisfiable  < Rango::ClientError; STATUS ||= 416; end
+  class ExpectationFailed           < Rango::ClientError; STATUS ||= 417; end
+
+  # server error
+  class ServerError                 < Rango::HttpError; end
+  class InternalServerError         < Rango::ServerError; STATUS ||= 500; end
+  class NotImplemented              < Rango::ServerError; STATUS ||= 501; end
+  class BadGateway                  < Rango::ServerError; STATUS ||= 502; end
+  class ServiceUnavailable          < Rango::ServerError; STATUS ||= 503; end
+  class GatewayTimeout              < Rango::ServerError; STATUS ||= 504; end
+  class HTTPVersionNotSupported     < Rango::ServerError; STATUS ||= 505; end
 end

Refactored exceptions