public
Description: A Rails plugin to use exceptions for generating HTTP status responses.
Homepage: http://wiki.github.com/wvanbergen/http_status_exceptions
Clone URL: git://github.com/wvanbergen/http_status_exceptions.git
http_status_exceptions / lib / http_status_exceptions.rb
100644 147 lines (130 sloc) 6.018 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# The HTTPStatus module is the core of the http_status_exceptions gem and
# contains all functionality.
#
# The module contains <tt>HTTPStatus::Base</tt> class, which is used as a
# superclass for every HTTPStatus exception. Subclasses, like
# <tt>HTTPStatus::Forbidden</tt> or <tt>HTTPStatus::NotFound</tt> will be
# generated on demand by the <tt>HTTPStatus.const_missing</tt> method.
#
# Moreover, it contains methods to handle these exceptions and integrate this
# functionality into <tt>ActionController::Base</tt>. When this module is in
# included in the <tt>ActionController::Base</tt> class, it will call
# <tt>rescue_from</tt> on it to handle all <tt>HTTPStatus::Base</tt>
# exceptions with the <tt>HTTPStatus#http_status_exceptions</tt> method.
#
# The exception handler will try to render a response with the correct
# HTTPStatus. When no suitable template is found to render the exception with,
# it will simply respond with an empty HTTP status code.
module HTTPStatus
 
  # The current gem release version. Do not set this value by hand, it will
  # be done automatically by them gem release script.
  VERSION = "0.2.0"
 
  # The Base HTTP status exception class is used as superclass for every
  # exception class that is constructed. It implements some shared
  # functionality for finding the status code and determining the template
  # path to render.
  #
  # Subclasses of this class will be generated on demand when a non-exisiting
  # constant of the <tt>HTTPStatus</tt> module is requested. This is
  # implemented in the <tt>HTTPStatus.const_missing</tt> method.
  class Base < StandardError
 
    # The path from which the error documents are loaded.
    cattr_accessor :template_path
    @@template_path = 'shared/http_status'
 
    # The layout in which the error documents are rendered
    cattr_accessor :template_layout
    @@template_layout = nil # Use the standard layout template setting by default.
 
    attr_reader :details
 
    # Initializes the exception instance.
    # <tt>message</tt>:: The exception message.
    # <tt>details</tt>:: An object with details about the exception.
    def initialize(message = nil, details = nil)
      @details = details
      super(message)
    end
 
    # Returns the HTTP status symbol corresponding to this class. This is one
    # of the symbols that can be found in the map that can be found in
    # <tt>ActionController::StatusCodes</tt>.
    #
    # This method should be overridden by subclasses, as it returns
    # <tt>:internal_server_error</tt> by default. This is done automatically
    # when a new exception class is being generated by
    # <tt>HTTPStatus.const_missing</tt>.
    def self.status
      :internal_server_error
    end
 
    # Returns the HTTP status symbol (as defined by Rails) corresponding to
    # this instance. By default, it calls the class method of the same name.
    def status
      self.class.status
    end
 
    # The numeric status code corresponding to this exception class. Uses the
    # status symbol to code map in <tt>ActionController::StatusCodes</tt>.
    def self.status_code
      ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[self.status]
    end
 
    # The numeric status code corresponding to this exception. By default, it
    # calls the class method of the same name.
    def status_code
      self.class.status_code
    end
 
    # The name of the template that should be used as error page for this
    # exception class.
    def self.template
      "#{template_path}/#{status}"
    end
 
    # The name of the template that should be used as error page for this
    # exception. By default, it calls the class method of the same name.
    def template
      self.class.template
    end
  end
 
  # This function will install a rescue_from handler for HTTPStatus::Base
  # exceptions in the class in which this module is included.
  #
  # <tt>base</tt>:: The class in which the module is included. Should be
  # <tt>ActionController::Base</tt> during the initialization of the gem.
  def self.included(base)
    base.send(:rescue_from, HTTPStatus::Base, :with => :http_status_exception)
  end
 
  # Generates a <tt>HTTPStatus::Base</tt> subclass on demand based on the
  # constant name. The constant name should correspond to one of the status
  # symbols defined in <tt>ActionController::StatusCodes</tt>. The function
  # will raise an exception if the constant name cannot be mapped onto one of
  # the status symbols.
  #
  # This method will create a new subclass of <tt>HTTPStatus::Base</tt> and
  # overrides the status class method of the class to return the correct
  # status symbol.
  #
  # <tt>const</tt>:: The name of the missing constant, for which an exception
  # class should be generated.
  def self.const_missing(const)
    status_symbol = const.to_s.underscore.to_sym
    raise "Unrecognized HTTP Status name!" unless ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.has_key?(status_symbol)
    klass = Class.new(HTTPStatus::Base)
    klass.cattr_accessor(:status)
    klass.status = status_symbol
    const_set(const, klass)
    return const_get(const)
  end
 
  # The default handler for raised HTTP status exceptions. It will render a
  # template if available, or respond with an empty response with the HTTP
  # status corresponding to the exception.
  #
  # You can override this method in your <tt>ApplicationController</tt> to
  # handle the exceptions yourself.
  #
  # <tt>exception</tt>:: The HTTP status exception to handle.
  def http_status_exception(exception)
    @exception = exception
    render_options = {:template => exception.template, :status => exception.status}
    render_options[:layout] = exception.template_layout if exception.template_layout
    render(render_options)
  rescue ActionView::MissingTemplate
    head(exception.status)
  end
end
 
# Include the HTTPStatus module into <tt>ActionController::Base</tt> to enable
# the <tt>http_status_exception</tt> exception handler.
ActionController::Base.send(:include, HTTPStatus)