|
|
Index: lib/merb/merb_view_context.rb
===================================================================
--- lib/merb/merb_view_context.rb (revision 371)
+++ lib/merb/merb_view_context.rb (working copy)
@@ -6,6 +6,7 @@
PROTECTED_IVARS = %w[@_new_cookie
@method
@env
+ @controller
@_body
@_fingerprint_before
@_session
Index: lib/merb/merb_exceptions.rb
===================================================================
--- lib/merb/merb_exceptions.rb (revision 371)
+++ lib/merb/merb_exceptions.rb (working copy)
@@ -4,6 +4,90 @@
end
module Merb
+
+ module ControllerExceptions
+ class Base < StandardError
+ include Merb::RenderMixin
+ include Merb::ControllerMixin
+
+ def _template_root
+ @controller._template_root
+ end
+
+ def set_controller(controller)
+ @controller = controller
+ @controller.status = status
+ end
+
+ def request; @controller.request; end
+ def params; @controller.params; end
+ def cookies; @controller.cookies; end
+ def headers; @controller.headers; end
+ def session; @controller.session; end
+ def response; @controller.response; end
+
+ def status
+ self.class.const_get(:STATUS)
+ end
+
+ def call_action
+ # before filters?
+ out = merb_action
+ # after filters?
+
+ # overwrite status changes during action
+ @controller.status = status
+ out
+ end
+
+ def merb_action
+ "Error #{status}! (this would be something explaining the error code)"
+ end
+ end
+
+ # created with:
+ # ruby -r rubygems -e "require 'mongrel'; puts Mongrel::HTTP_STATUS_CODES.keys.sort.map {|code| %Q{class #{Mongrel::HTTP_STATUS_CODES[code].gsub /[^\w]/,''} < Base; STATUS = #{code}; end} }.join(%Q{\n})"
+ # we could do some magic here, but i think it's more instructive to just
+ # have these values copied
+ class Continue < Base; STATUS = 100; end
+ class SwitchingProtocols < Base; STATUS = 101; end
+ class OK < Base; STATUS = 200; end
+ class Created < Base; STATUS = 201; end
+ class Accepted < Base; STATUS = 202; end
+ class NonAuthoritativeInformation < Base; STATUS = 203; end
+ class NoContent < Base; STATUS = 204; end
+ class ResetContent < Base; STATUS = 205; end
+ class PartialContent < Base; STATUS = 206; end
+ class MultipleChoices < Base; STATUS = 300; end
+ class MovedPermanently < Base; STATUS = 301; end
+ class MovedTemporarily < Base; STATUS = 302; end
+ class SeeOther < Base; STATUS = 303; end
+ class NotModified < Base; STATUS = 304; end
+ class UseProxy < Base; STATUS = 305; end
+ class BadRequest < Base; STATUS = 400; end
+ class Unauthorized < Base; STATUS = 401; end
+ class PaymentRequired < Base; STATUS = 402; end
+ class Forbidden < Base; STATUS = 403; end
+ class NotFound < Base; STATUS = 404; end
+ class MethodNotAllowed < Base; STATUS = 405; end
+ class NotAcceptable < Base; STATUS = 406; end
+ class ProxyAuthenticationRequired < Base; STATUS = 407; end
+ class RequestTimeout < Base; STATUS = 408; end
+ class Conflict < Base; STATUS = 409; end
+ class Gone < Base; STATUS = 410; end
+ class LengthRequired < Base; STATUS = 411; end
+ class PreconditionFailed < Base; STATUS = 412; end
+ class RequestEntityTooLarge < Base; STATUS = 413; end
+ class RequestURITooLarge < Base; STATUS = 414; end
+ class UnsupportedMediaType < Base; STATUS = 415; end
+ class InternalServerError < Base; STATUS = 500; end
+ class NotImplemented < Base; STATUS = 501; end
+ class BadGateway < Base; STATUS = 502; end
+ class ServiceUnavailable < Base; STATUS = 503; end
+ class GatewayTimeout < Base; STATUS = 504; end
+ class HTTPVersionnotsupported < Base; STATUS = 505; end
+ end
+
class MerbError < StandardError; end
class Noroutefound < MerbError; end
class MissingControllerFile < MerbError; end
Index: lib/merb/merb_controller.rb
===================================================================
--- lib/merb/merb_controller.rb (revision 371)
+++ lib/merb/merb_controller.rb (working copy)
@@ -96,15 +96,24 @@
def dispatch(action=:index)
start = Time.now
- if !self.class.callable_actions.include?(action.to_s)
- set_status(404)
- @_body = IO.read(DIST_ROOT / 'public/404.html') rescue "404: Not Found"
- MERB_LOGGER.info "Action: #{action} not in callable_actions: #{self.class.callable_actions}"
- else
- setup_session
- super(action)
- finalize_session
+ begin
+ if !self.class.callable_actions.include?(action.to_s)
+ # raise ControllerExceptions::NotFound. no need for public/404.html
+ set_status(404)
+ @_body = IO.read(DIST_ROOT / 'public/404.html') rescue "404: Not Found"
+ #
+ MERB_LOGGER.info "Action: #{action} not in callable_actions: #{self.class.callable_actions}"
+ else
+ setup_session
+ super(action)
+ finalize_session
+ end
+ rescue ControllerExceptions::Base => e
+ # do not pass status, the exception will set that
+ e.set_controller(self) # for access to session, params, etc
+ @_body = e.call_action
end
+
@_benchmarks[:action_time] = Time.now - start
MERB_LOGGER.info("Time spent in #{self.class}##{action} action: #{@_benchmarks[:action_time]} seconds")
end
@@ -119,6 +128,11 @@
@_status
end
+ # TODO - this is not the best stratagy
+ def status=(s)
+ @_status = s
+ end
+
# Accessor for @_request. Please use request and never @_request directly.
def request
@_request
Index: lib/merb/mixins/render_mixin.rb
===================================================================
--- lib/merb/mixins/render_mixin.rb (revision 371)
+++ lib/merb/mixins/render_mixin.rb (working copy)
@@ -91,7 +91,13 @@
# Renders the buffalo.xerb template for the current controller.
#
def render(opts={}, &blk)
- action = opts[:action] || params[:action]
+
+ action = if kind_of?(Merb::ControllerExceptions::Base)
+ self.class.name.snake_case.split('::').last
+ else
+ opts[:action] || params[:action]
+ end
+
opts[:layout] ||= _layout
case
@@ -234,6 +240,8 @@
def find_template(opts={})
if template = opts[:template]
path = _template_root / template
+ elsif opts[:action] and kind_of?(Merb::ControllerExceptions::Base)
+ path = _template_root / 'exceptions' / opts[:action]
elsif action = opts[:action]
segment = self.class.name.snake_case.split('::').join('/')
path = _template_root / segment / action
Index: specs/merb/merb_dispatch_spec.rb
===================================================================
--- specs/merb/merb_dispatch_spec.rb (revision 371)
+++ specs/merb/merb_dispatch_spec.rb (working copy)
@@ -462,4 +462,9 @@
controller.params[:post_id].should == '1'
controller.body.should == :stats
end
+
+ it "should show the error page" do
+ controller, action = request(:get, '/foo/error')
+ controller.body.should == "oh no!"
+ end
end
Index: specs/merb/merb_render_spec.rb
===================================================================
--- specs/merb/merb_render_spec.rb (revision 371)
+++ specs/merb/merb_render_spec.rb (working copy)
@@ -137,5 +137,21 @@
content = c.render(:action => "test")
content.clean.should == "Hello!"
end
+
+ it "should render exception's view without layout" do
+ c = Examples.build @in.req, @in.env, {}, @res
+ x = AdminAccessRequired.new
+ x.set_controller(c)
+ content = x.render :layout => :none
+ content.clean.should == "An Error Occurred!"
+ end
+
+ it "should render exception's view with layout" do
+ c = Examples.build @in.req, @in.env, {}, @res
+ x = AdminAccessRequired.new
+ x.set_controller(c)
+ content = x.render
+ content.clean.should == "An Error Occurred!"
+ end
end
Index: specs/fixtures/controllers/dispatch_spec_controllers.rb
===================================================================
--- specs/fixtures/controllers/dispatch_spec_controllers.rb (revision 371)
+++ specs/fixtures/controllers/dispatch_spec_controllers.rb (working copy)
@@ -7,9 +7,20 @@
def bar
"bar"
end
+
+ def error
+ raise AdminAccessRequired
+ "Hello World!"
+ end
end
+class AdminAccessRequired < Merb::ControllerExceptions::Unauthorized
+ def merb_action
+ "oh no!"
+ end
+end
+
class Posts < Merb::Controller
# GET /posts
# GET /posts.xml
Index: specs/fixtures/views/exceptions/admin_access_required.herb
===================================================================
--- specs/fixtures/views/exceptions/admin_access_required.herb (revision 0)
+++ specs/fixtures/views/exceptions/admin_access_required.herb (revision 0)
@@ -0,0 +1 @@
+<%= "An Error Occurred!" %>
\ No newline at end of file
|