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

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

# superclass of all the controller exceptions
module Rango
  class HttpError < StandardError
    CONTENT_TYPE ||= "text/html"

    attr_accessor :content_type, :headers
    def initialize(*args)
      @headers = Hash.new
      super(*args)
    end

    def inspect
      self.to_response.inspect
    end

    # @returns [String]
    # @example NotFound.new.to_snakecase # "not_found"
    def to_snakecase
      self.class.name.split("::").last.snake_case
    end

    def status() self.class::STATUS end
    def content_type
      @content_type || self.class::CONTENT_TYPE
    end

    def to_response
      headers = {"Content-Type" => self.content_type}.merge(self.headers)
      [self.status, headers, [self.message]]
    end

    def self.const_missing(name)
      if [:STATUS, :CONTENT_TYPE].include?(name)
        raise NameError, "Every descendant of HttpError class has to have defined constant #{name}."
      else
        super(name)
      end
    end
  end

  # http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

  # informational
  Informational = Class.new(Rango::HttpError)
  Error100 = Continue = Class.new(Rango::Informational) { STATUS ||= 100 }
  Error101 = SwitchingProtocols = Class.new(Rango::Informational) { STATUS ||= 101 }

  # successful
  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
  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
  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)
  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
  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
    Error203 = NonAuthoritativeInformation = Class.new(Successful) { self.status ||= 203; self.name ||= "Non Authoritative Information" }
    Error204 = NoContent = Class.new(Successful)                   { self.status ||= 204; self.name ||= "No Content" }
    Error205 = ResetContent = Class.new(Successful)                { self.status ||= 205; self.name ||= "Reset Content" }
    Error206 = PartialContent = Class.new(Successful)              { self.status ||= 206; self.name ||= "Partial Content" }

    # redirection
    class Redirection < HttpError
      self.name ||= "Redirection"
      alias_method :location, :message

      def body
        %{Please follow <a href="#{URI.escape(self.location)}">#{self.location}</a>}
      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> All the exceptions are in Rango::Exceptions, so you can include Rango::Exceptions to your controller and use raise NotFound if post.nil? directly

<DFF> @@ -4,100 +4,102 @@
 
 # superclass of all the controller exceptions
 module Rango
-  class HttpError < StandardError
-    CONTENT_TYPE ||= "text/html"
+  module Exceptions
+    class HttpError < StandardError
+      CONTENT_TYPE ||= "text/html"
 
-    attr_accessor :content_type, :headers
-    def initialize(*args)
-      @headers = Hash.new
-      super(*args)
-    end
+      attr_accessor :content_type, :headers
+      def initialize(*args)
+        @headers = Hash.new
+        super(*args)
+      end
 
-    def inspect
-      self.to_response.inspect
-    end
+      def inspect
+        self.to_response.inspect
+      end
 
-    # @returns [String]
-    # @example NotFound.new.to_snakecase # "not_found"
-    def to_snakecase
-      self.class.name.split("::").last.snake_case
-    end
+      # @returns [String]
+      # @example NotFound.new.to_snakecase # "not_found"
+      def to_snakecase
+        self.class.name.split("::").last.snake_case
+      end
 
-    def status() self.class::STATUS end
-    def content_type
-      @content_type || self.class::CONTENT_TYPE
-    end
+      def status() self.class::STATUS end
+      def content_type
+        @content_type || self.class::CONTENT_TYPE
+      end
 
-    def to_response
-      headers = {"Content-Type" => self.content_type}.merge(self.headers)
-      [self.status, headers, [self.message]]
-    end
+      def to_response
+        headers = {"Content-Type" => self.content_type}.merge(self.headers)
+        [self.status, headers, [self.message]]
+      end
 
-    def self.const_missing(name)
-      if [:STATUS, :CONTENT_TYPE].include?(name)
-        raise NameError, "Every descendant of HttpError class has to have defined constant #{name}."
-      else
-        super(name)
+      def self.const_missing(name)
+        if [:STATUS, :CONTENT_TYPE].include?(name)
+          raise NameError, "Every descendant of HttpError class has to have defined constant #{name}."
+        else
+          super(name)
+        end
       end
     end
-  end
 
-  # http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
+    # http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
 
-  # informational
-  Informational = Class.new(Rango::HttpError)
-  Error100 = Continue = Class.new(Rango::Informational) { STATUS ||= 100 }
-  Error101 = SwitchingProtocols = Class.new(Rango::Informational) { STATUS ||= 101 }
+    # informational
+    Informational = Class.new(Rango::HttpError)
+    Error100 = Continue = Class.new(Rango::Informational) { STATUS ||= 100 }
+    Error101 = SwitchingProtocols = Class.new(Rango::Informational) { STATUS ||= 101 }
 
-  # successful
-  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 }
+    # successful
+    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
-  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 }
+    # redirection
+    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
-  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)
-  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 }
+    # client error
+    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)
+    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
-  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 }
+    # server error
+    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
 end

All the exceptions are in Rango::Exceptions, so you can include Rango::Exceptions to your controller and use raise NotFound if post.nil? directly