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

# http://wiki.github.com/botanicus/rango/errors-handling

require "uri"

# 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

      def name
        @name ||= self.class.name
      end

      def self.message=(message)
        @message = message
      end

      # backtrace isn't writable, but we want to set it time to time, see Controller.call
      attr_writer :backtrace
      def backtrace
        super || @backtrace
      end

      def self.message
        @message ||= begin
          self.superclass.message
        rescue NoMethodError
        end
      end

      attr_writer :message
      def message
        @message ||= self.class.message
      end

      def self.status=(status)
        @status = status
      end

      def self.status
        @status ||= begin
          self.superclass.status
        rescue NoMethodError
        end
      end

      # If we have a redirection but we don't know the status yet,
      # then rather than use raise Error301 we create a new instance
  # http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

  # 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

      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> Each exceptions class with STATUS constant has its alias - for example NotFound is aliased to Error404

<DFF> @@ -58,61 +58,61 @@ module Rango
   # http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
 
   # informational
-  class Informational               < Rango::HttpError; end
-  class Continue                    < Rango::Informational; STATUS ||= 100; end
-  class SwitchingProtocols          < Rango::Informational; STATUS ||= 101; end
+  Informational = Class.new(Rango::HttpError)
+  Error100 = Continue = Class.new(Rango::Informational) { STATUS ||= 100 }
+  Error101 = SwitchingProtocols = Class.new(Rango::Informational) { STATUS ||= 101 }
 
   # 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
+  Successful = Class.new(Rango::HttpError)
+  Error200 = OK = Class.new(Rango::Successful) { STATUS ||= 200 }
+  Error201 = Created = Class.new(Rango::Successful) { STATUS ||= 201 }
+  Error202 = Accepted = Class.new(Rango::Successful) { STATUS ||= 202 }
+  Error203 = NonAuthoritativeInformation = Class.new(Rango::Successful) { STATUS ||= 203 }
+  Error204 = NoContent = Class.new(Rango::Successful) { STATUS ||= 204 }
+  Error205 = ResetContent = Class.new(Rango::Successful) { STATUS ||= 205 }
+  Error206 = PartialContent = Class.new(Rango::Successful) { STATUS ||= 206 }
 
   # 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
+  Redirection = Class.new(Rango::HttpError)
+  Error300 = MultipleChoices = Class.new(Rango::Redirection) { STATUS ||= 300 }
+  Error301 = MovedPermanently = Class.new(Rango::Redirection) { STATUS ||= 301 }
+  Error302 = MovedTemporarily = Class.new(Rango::Redirection) { STATUS ||= 302 }
+  Error303 = SeeOther = Class.new(Rango::Redirection) { STATUS ||= 303 }
+  Error304 = NotModified = Class.new(Rango::Redirection) { STATUS ||= 304 }
+  Error305 = UseProxy = Class.new(Rango::Redirection) { STATUS ||= 305 }
+  Error307 = TemporaryRedirect = Class.new(Rango::Redirection) { STATUS ||= 307 }
 
   # 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
+  ClientError = Class.new(Rango::HttpError)
+  Error400 = BadRequest = Class.new(Rango::ClientError) { STATUS ||= 400 }
+  MultiPartParseError = Class.new(Rango::BadRequest)
+  Error401 = Unauthorized = Class.new(Rango::ClientError) { STATUS ||= 401 }
+  Error402 = PaymentRequired = Class.new(Rango::ClientError) { STATUS ||= 402 }
+  Error403 = Forbidden = Class.new(Rango::ClientError) { STATUS ||= 403 }
+  Error404 = NotFound = Class.new(Rango::ClientError) { STATUS ||= 404 }
+  ActionNotFound = Class.new(Rango::NotFound)
+  TemplateNotFound = Class.new(Rango::NotFound)
+  LayoutNotFound = Class.new(Rango::NotFound)
+  Error405 = MethodNotAllowed = Class.new(Rango::ClientError) { STATUS ||= 405 }
+  Error406 = NotAcceptable = Class.new(Rango::ClientError) { STATUS ||= 406 }
+  Error407 = ProxyAuthenticationRequired = Class.new(Rango::ClientError) { STATUS ||= 407 }
+  Error408 = RequestTimeout = Class.new(Rango::ClientError) { STATUS ||= 408 }
+  Error409 = Conflict = Class.new(Rango::ClientError) { STATUS ||= 409 }
+  Error410 = Gone = Class.new(Rango::ClientError) { STATUS ||= 410 }
+  Error411 = LengthRequired = Class.new(Rango::ClientError) { STATUS ||= 411 }
+  Error412 = PreconditionFailed = Class.new(Rango::ClientError) { STATUS ||= 412 }
+  Error413 = RequestEntityTooLarge = Class.new(Rango::ClientError) { STATUS ||= 413 }
+  Error414 = RequestURITooLarge = Class.new(Rango::ClientError) { STATUS ||= 414 }
+  Error415 = UnsupportedMediaType = Class.new(Rango::ClientError) { STATUS ||= 415 }
+  Error416 = RequestRangeNotSatisfiable = Class.new(Rango::ClientError) { STATUS ||= 416 }
+  Error417 = ExpectationFailed = Class.new(Rango::ClientError) { STATUS ||= 417 }
 
   # 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
+  ServerError = Class.new(Rango::HttpError)
+  Error500 = InternalServerError = Class.new(Rango::ServerError) { STATUS ||= 500 }
+  Error501 = NotImplemented = Class.new(Rango::ServerError) { STATUS ||= 501 }
+  Error502 = BadGateway = Class.new(Rango::ServerError) { STATUS ||= 502 }
+  Error503 = ServiceUnavailable = Class.new(Rango::ServerError) { STATUS ||= 503 }
+  Error504 = GatewayTimeout = Class.new(Rango::ServerError) { STATUS ||= 504 }
+  Error505 = HTTPVersionNotSupported = Class.new(Rango::ServerError) { STATUS ||= 505 }
 end

Each exceptions class with STATUS constant has its alias - for example NotFound is aliased to Error404