wvanbergen-http_status_exceptions 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -4,14 +4,14 @@ This simple plugin will register exception classes for all HTTP status. These ex
4
4
  which a response will be send back to the client with the desired HTTP status, possible with some other content.
5
5
 
6
6
  You can use this plugin to access control mechanisms. You can simply raise a HTTPStatus::Forbidden if a user is not allowed to
7
- perform a certain action. A nice looking error page will be the result. See the example below
7
+ perform a certain action. A nice looking error page will be the result. See the example below:
8
8
 
9
9
  See the project wiki (http://github.com/wvanbergen/http_status_exceptions/wikis) for additional documentation.
10
10
 
11
11
  == Installation
12
12
 
13
13
  Installation is simple. Simply add the gem in your <tt>environment.rb</tt>:
14
-
14
+
15
15
  Rails::Initializer.run do |config|
16
16
  ...
17
17
  config.gem 'wvanbergen-http_status_exceptions', :lib => 'http_status_exceptions', :source => 'http://gems.github.com'
@@ -22,12 +22,12 @@ Run <tt>rake gems:install</tt> to install the gem if needed.
22
22
  == Configuration
23
23
 
24
24
  You can modify where HTTP status exception looks for its template files like so:
25
-
25
+
26
26
  class ApplicationController < ActionController::Base
27
27
  ...
28
28
  HTTPStatus::Base.template_path = 'path_to/http_status_templates'
29
29
  end
30
-
30
+
31
31
  You can also modify which layout is used when rendering a template by setting the <tt>template_layout</tt>:
32
32
 
33
33
  class ApplicationController < ActionController::Base
@@ -56,5 +56,5 @@ to the response as well, create the following view: <tt>shared/http_status/forbi
56
56
  <hr />
57
57
  <p>HTTP status code <small> <%= @exception.status_code %>: <%= @exception.status.to_s.humanize %></small></p>
58
58
 
59
- The response will only be sent if the request format is HTML because of the name of the view file. In theory you
59
+ The response will only be sent if the request format is HTML because of the name of the view file. In theory you
60
60
  could make a response for XML requests as well by using <tt>shared/http_status/forbidden.xml.builder</tt> as filename
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
1
  Dir[File.dirname(__FILE__) + "/tasks/*.rake"].each { |file| load(file) }
2
2
 
3
- GithubGem::RakeTasks.new(:gem)
3
+ GithubGem::RakeTasks.new(:gem)
4
4
 
5
5
  task :default => :spec
@@ -1,17 +1,18 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'http_status_exceptions'
3
- s.version = "0.1.6"
3
+ s.version = "0.1.7"
4
4
  s.date = "2009-09-26"
5
-
5
+
6
6
  s.summary = "A Rails plugin to use exceptions for generating HTTP status responses"
7
7
  s.description = "Clean up your controller code by raising exceptions that generate responses with different HTTP status codes."
8
-
8
+
9
9
  s.add_runtime_dependency('action_controller')
10
10
  s.add_development_dependency('rspec')
11
-
11
+
12
12
  s.authors = ['Willem van Bergen']
13
13
  s.email = ['willem@vanbergen.org']
14
14
  s.homepage = 'http://github.com/wvanbergen/http_status_exceptions/wikis'
15
-
16
- s.files = %w(spec/spec_helper.rb http_status_exceptions.gemspec .gitignore init.rb lib/http_status_exceptions.rb Rakefile MIT-LICENSE tasks/github-gem.rake README.rdoc spec/http_status_exception_spec.rb)
15
+
16
+ s.files = %w(spec/spec_helper.rb http_status_exceptions.gemspec .gitignore init.rb lib/http_status_exceptions.rb Rakefile MIT-LICENSE tasks/github-gem.rake README.rdoc spec/http_status_exception_spec.rb)
17
+ s.test_files = %w(spec/http_status_exception_spec.rb)
17
18
  end
@@ -1,5 +1,8 @@
1
1
  module HTTPStatus
2
-
2
+
3
+ # The Base HTTP status exception class is used as superclass for every
4
+ # exception class that is constructed. It implements some shared functionality
5
+ # for finding the status code and determining the template path to render.
3
6
  class Base < StandardError
4
7
 
5
8
  # The path from which the error documents are loaded.
@@ -8,38 +11,70 @@ module HTTPStatus
8
11
 
9
12
  # The layout in which the error documents are rendered
10
13
  cattr_accessor :template_layout
14
+ @@template_path = nil # Use the standard layout template setting by default.
15
+
16
+ attr_reader :details
11
17
 
12
- attr_reader :status, :details
13
-
14
18
  # Creates the exception with a message and some optional other info.
15
19
  def initialize(message = nil, details = nil)
16
- @status = self.class.to_s.split("::").last.underscore.to_sym rescue :internal_server_error
17
20
  @details = details
18
21
  super(message)
19
22
  end
20
-
21
- # The numeric status code corresponding to this exception
23
+
24
+ # Returns the HTTP status symbol (as defined by Rails) corresponding to this class.
25
+ # This method should be overridden by subclasses
26
+ def self.status
27
+ :internal_server_error
28
+ end
29
+
30
+ # Returns the HTTP status symbol (as defined by Rails) corresponding to this instance.
31
+ # By default, it calls the class method of the same name.
32
+ def status
33
+ self.class.status
34
+ end
35
+
36
+ # The numeric status code corresponding to this exception class.
37
+ # Uses the status code map provided by Rails.
38
+ def self.status_code
39
+ ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[self.status]
40
+ end
41
+
42
+ # The numeric status code corresponding to this exception.
43
+ # By default, it calls the class method of the same name.
22
44
  def status_code
23
- ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[@status]
45
+ self.class.status_code
46
+ end
47
+
48
+ # The name of the template that should be used as error page for this exception class.
49
+ def self.template
50
+ "#{template_path}/#{status}"
24
51
  end
25
-
26
- # The name of the template that should be used as error page for this exception
52
+
53
+ # The name of the template that should be used as error page for this exception.
54
+ # By default, it calls the class method of the same name.
27
55
  def template
28
- "#{@@template_path}/#{@status}"
56
+ self.class.template
29
57
  end
30
58
  end
31
59
 
32
60
  # Creates all the exception classes based on Rails's list of available status code and
33
61
  # registers the exception handler using the rescue_from method.
34
62
  def self.included(base)
35
- ActionController::StatusCodes::STATUS_CODES.each do |code, name|
36
- const_set(name.to_s.gsub(/[^A-Za-z]/, '').camelize, Class.new(HTTPStatus::Base)) if code >= 400
37
- end
38
-
39
63
  base.send(:rescue_from, HTTPStatus::Base, :with => :http_status_exception)
40
64
  end
41
65
 
42
- # The default handler for raised HTTP status exceptions.
66
+ # Generates a HTTPStatus::Base subclass for every subclass that is found
67
+ def self.const_missing(const)
68
+ status_symbol = const.to_s.underscore.to_sym
69
+ raise "Unrecognized HTTP Status name!" unless ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.has_key?(status_symbol)
70
+ klass = Class.new(HTTPStatus::Base)
71
+ klass.cattr_accessor(:status)
72
+ klass.status = status_symbol
73
+ const_set(const, klass)
74
+ return const_get(const)
75
+ end
76
+
77
+ # The default handler for raised HTTP status exceptions.
43
78
  # It will render a template if available, or respond with an empty response
44
79
  # with the HTTP status correspodning to the exception.
45
80
  def http_status_exception(exception)
@@ -52,4 +87,5 @@ module HTTPStatus
52
87
  end
53
88
  end
54
89
 
90
+ # Include the HTTPStatus module into ActionController to enable its functionality
55
91
  ActionController::Base.send(:include, HTTPStatus)
@@ -1,51 +1,93 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
- describe 'HTTPStatus#http_status_exception' do
3
+ describe HTTPStatus::Base do
4
4
  before(:each) do
5
- @controller_class = Class.new(ActionController::Base)
6
- @controller = @controller_class.new
5
+ ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.stub!(:has_key?).with(:testing_status).and_return(true)
6
+ @status_exception_class = HTTPStatus::TestingStatus
7
7
  end
8
-
9
- it "should respond to the :http_status_exception method" do
10
- @controller.should respond_to(:http_status_exception)
8
+
9
+ after(:each) do
10
+ HTTPStatus::Base.template_path = 'shared/http_status'
11
+ HTTPStatus.send :remove_const, 'TestingStatus'
12
+ end
13
+
14
+ it "should set the status symbol based on the class name" do
15
+ @status_exception_class.status.should == :testing_status
16
+ end
17
+
18
+ it "should check ActionController's status code list for the status code based on the class name" do
19
+ ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.should_receive(:[]).with(:testing_status)
20
+ @status_exception_class.status_code
21
+ end
22
+
23
+ it "should use the HTTPStatus::Base.template_path setting to determine the error template" do
24
+ HTTPStatus::Base.template_path = 'testing'
25
+ @status_exception_class.template.should == 'testing/testing_status'
11
26
  end
12
27
 
13
- ['NotFound', 'Forbidden', 'PaymentRequired'].each do |status_class|
14
- status_symbol = status_class.underscore.downcase.to_sym
15
-
16
- it "should create the HTTPStatus::#{status_class} class" do
17
- HTTPStatus.const_defined?(status_class).should be_true
28
+ it "should raise an exception when the class name does not correspond to a HTTP status code" do
29
+ lambda { HTTPStatus::Nonsense }.should raise_error
30
+ end
31
+ end
32
+
33
+ # Run some tests for different valid subclasses.
34
+ { 'NotFound' => 404, 'Forbidden' => 403, 'PaymentRequired' => 402, 'InternalServerError' => 500}.each do |status_class, status_code|
35
+
36
+ describe "HTTPStatus::#{status_class}" do
37
+ it "should generate the HTTPStatus::#{status_class} class successfully" do
38
+ lambda { HTTPStatus.const_get(status_class) }.should_not raise_error
18
39
  end
19
-
40
+
20
41
  it "should create a subclass of HTTPStatus::Base for the #{status_class.underscore.humanize.downcase} status" do
21
42
  HTTPStatus.const_get(status_class).ancestors.should include(HTTPStatus::Base)
22
43
  end
23
-
24
- it "should call render with the correct #{status_class.underscore.humanize.downcase} view and correct HTTP status" do
25
- @controller.should_receive(:render).with(hash_including(
26
- :status => status_symbol,
27
- :template => "shared/http_status/#{status_symbol}"))
28
44
 
29
- @controller.http_status_exception(HTTPStatus.const_get(status_class).new('test'))
45
+ it "should return the correct status code (#{status_code}) when using the class" do
46
+ HTTPStatus.const_get(status_class).status_code.should == status_code
30
47
  end
48
+
49
+ it "should return the correct status code (#{status_code}) when using the instance" do
50
+ HTTPStatus.const_get(status_class).new.status_code.should == status_code
51
+ end
31
52
  end
32
53
  end
33
54
 
34
- describe HTTPStatus::Base do
35
- before(:each) { @status = HTTPStatus::Base.new }
36
-
37
- it "should set the status symbol bases on the class name" do
38
- @status.status.should == :base
55
+ describe 'HTTPStatus#http_status_exception' do
56
+ before(:each) { @controller = Class.new(ActionController::Base).new }
57
+ after(:each) { HTTPStatus::Base.template_layout = nil}
58
+
59
+ it "should create the :http_status_exception method in ActionController" do
60
+ @controller.should respond_to(:http_status_exception)
39
61
  end
40
-
41
- it "should check ActionController's status code list for the status code based on the class name" do
42
- ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.should_receive(:[]).with(:base)
43
- @status.status_code
62
+
63
+ it "should call :http_status_exception when an exception is raised when handling the action" do
64
+ exception = HTTPStatus::Base.new('test')
65
+ @controller.stub!(:perform_action_without_rescue).and_raise(exception)
66
+ @controller.should_receive(:http_status_exception).with(exception)
67
+ @controller.send(:perform_action)
44
68
  end
45
-
46
- it "should use the HTTPStatus::Base.template_path setting to determine the error template" do
47
- HTTPStatus::Base.template_path = 'testing'
48
- @status.template.should == 'testing/base'
69
+
70
+ it "should call render with the correct view and correct HTTP status" do
71
+ @controller.should_receive(:render).with(hash_including(
72
+ :status => :internal_server_error, :template => "shared/http_status/internal_server_error"))
73
+
74
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
49
75
  end
50
- end
51
76
 
77
+ it "should not call render with a layout by default" do
78
+ @controller.should_not_receive(:render).with(hash_including(:layout => 'testing'))
79
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
80
+ end
81
+
82
+ it "should call render with a layout set when this property is set on the exception class" do
83
+ @controller.should_receive(:render).with(hash_including(:layout => 'testing'))
84
+ HTTPStatus::Base.template_layout = 'testing'
85
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
86
+ end
87
+
88
+ it "should call head with the correct status code if render cannot found a template" do
89
+ @controller.stub!(:render).and_raise(ActionView::MissingTemplate.new([], 'template.htm.erb'))
90
+ @controller.should_receive(:head).with(:internal_server_error)
91
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
92
+ end
93
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wvanbergen-http_status_exceptions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willem van Bergen
@@ -79,5 +79,5 @@ rubygems_version: 1.3.5
79
79
  signing_key:
80
80
  specification_version: 2
81
81
  summary: A Rails plugin to use exceptions for generating HTTP status responses
82
- test_files: []
83
-
82
+ test_files:
83
+ - spec/http_status_exception_spec.rb