rails_dynamic_errors 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +31 -0
  4. data/app/controllers/rails_dynamic_errors/errors_controller.rb +52 -0
  5. data/app/views/rails_dynamic_errors/errors/show.html.erb +4 -0
  6. data/config/routes.rb +3 -0
  7. data/lib/engine_helper.rb +15 -0
  8. data/lib/generators/rails_dynamic_errors/install_generator.rb +38 -0
  9. data/lib/rails_dynamic_errors/engine.rb +32 -0
  10. data/lib/rails_dynamic_errors/middleware/dynamic_errors.rb +98 -0
  11. data/lib/rails_dynamic_errors/version.rb +4 -0
  12. data/lib/rails_dynamic_errors.rb +11 -0
  13. data/lib/tasks/rails_dynamic_errors_tasks.rake +4 -0
  14. data/spec/controllers/errors_controller_spec.rb +128 -0
  15. data/spec/dummy/README.rdoc +28 -0
  16. data/spec/dummy/Rakefile +6 -0
  17. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  18. data/spec/dummy/app/assets/javascripts/booms.js +2 -0
  19. data/spec/dummy/app/assets/javascripts/things.js +2 -0
  20. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  21. data/spec/dummy/app/assets/stylesheets/booms.css +4 -0
  22. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  23. data/spec/dummy/app/assets/stylesheets/things.css +4 -0
  24. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  25. data/spec/dummy/app/controllers/booms_controller.rb +6 -0
  26. data/spec/dummy/app/controllers/things_controller.rb +58 -0
  27. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  28. data/spec/dummy/app/helpers/booms_helper.rb +2 -0
  29. data/spec/dummy/app/helpers/things_helper.rb +2 -0
  30. data/spec/dummy/app/models/thing.rb +2 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/app/views/rails_dynamic_errors/errors/url_helpers.html.erb +2 -0
  33. data/spec/dummy/app/views/things/_form.html.erb +21 -0
  34. data/spec/dummy/app/views/things/edit.html.erb +6 -0
  35. data/spec/dummy/app/views/things/index.html.erb +27 -0
  36. data/spec/dummy/app/views/things/new.html.erb +5 -0
  37. data/spec/dummy/app/views/things/show.html.erb +9 -0
  38. data/spec/dummy/bin/bundle +3 -0
  39. data/spec/dummy/bin/rails +4 -0
  40. data/spec/dummy/bin/rake +4 -0
  41. data/spec/dummy/config/application.rb +43 -0
  42. data/spec/dummy/config/boot.rb +5 -0
  43. data/spec/dummy/config/database.yml +25 -0
  44. data/spec/dummy/config/environment.rb +5 -0
  45. data/spec/dummy/config/environments/development.rb +29 -0
  46. data/spec/dummy/config/environments/production.rb +80 -0
  47. data/spec/dummy/config/environments/test.rb +36 -0
  48. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  50. data/spec/dummy/config/initializers/inflections.rb +16 -0
  51. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  52. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  53. data/spec/dummy/config/initializers/session_store.rb +3 -0
  54. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  55. data/spec/dummy/config/locales/en.yml +23 -0
  56. data/spec/dummy/config/routes.rb +67 -0
  57. data/spec/dummy/config.ru +4 -0
  58. data/spec/dummy/db/development.sqlite3 +0 -0
  59. data/spec/dummy/db/migrate/20140620101702_create_things.rb +9 -0
  60. data/spec/dummy/db/production.sqlite3 +0 -0
  61. data/spec/dummy/db/schema.rb +22 -0
  62. data/spec/dummy/db/test.sqlite3 +0 -0
  63. data/spec/dummy/log/development.log +3309 -0
  64. data/spec/dummy/log/production.log +35 -0
  65. data/spec/dummy/log/test.log +30008 -0
  66. data/spec/dummy/public/404.html +58 -0
  67. data/spec/dummy/public/422.html +58 -0
  68. data/spec/dummy/public/500.html +57 -0
  69. data/spec/dummy/public/favicon.ico +0 -0
  70. data/spec/dummy/tmp/config/application.rb +11 -0
  71. data/spec/dummy/tmp/config/routes.rb +10 -0
  72. data/spec/features/application_link_helpers_spec.rb +14 -0
  73. data/spec/features/error_handling_spec.rb +167 -0
  74. data/spec/lib/generators/rails_dynamic_errors/install_generator_spec.rb +32 -0
  75. data/spec/lib/rails_dynamic_errors/engine_spec.rb +13 -0
  76. data/spec/lib/rails_dynamic_errors/middleware/dynamic_errors_spec.rb +171 -0
  77. data/spec/spec_helper.rb +43 -0
  78. data/spec/support/error.rb +19 -0
  79. data/spec/views/errors/show.html.erb_spec.rb +24 -0
  80. metadata +265 -0
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/404.html -->
52
+ <div class="dialog">
53
+ <h1>The page you were looking for doesn't exist.</h1>
54
+ <p>You may have mistyped the address or the page may have moved.</p>
55
+ </div>
56
+ <p>If you are the application owner check the logs for more information.</p>
57
+ </body>
58
+ </html>
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/422.html -->
52
+ <div class="dialog">
53
+ <h1>The change you wanted was rejected.</h1>
54
+ <p>Maybe you tried to change something you didn't have access to.</p>
55
+ </div>
56
+ <p>If you are the application owner check the logs for more information.</p>
57
+ </body>
58
+ </html>
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/500.html -->
52
+ <div class="dialog">
53
+ <h1>We're sorry, but something went wrong.</h1>
54
+ </div>
55
+ <p>If you are the application owner check the logs for more information.</p>
56
+ </body>
57
+ </html>
File without changes
@@ -0,0 +1,11 @@
1
+ module Dummy
2
+ class Application < Rails::Application
3
+ # This option is used to set the HTTP error codes for which
4
+ # rails_dynamic_errors will generate dynamic error pages. A good default
5
+ # setup is [404, 422], which will catch the two main errors (excluding the
6
+ # dreaded 500 Internal Server Error) for which Rails provides static HTML
7
+ # error pages.
8
+ # config.rails_dynamic_errors.http_error_status_codes_to_handle = [404, 422]
9
+
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ Rails.application.routes.draw do
2
+
3
+ # This line mounts RailDynamicErrors' routes on the '/errors' path of your
4
+ # application. This means that any requests to URLs with this prefix in their
5
+ # path will go to RailsDynamicErrors for processing.
6
+ #
7
+ # If you would like to change where this engine is mounted, simply change the
8
+ # :at option to something different.
9
+ mount RailsDynamicErrors::Engine, at: '/errors'
10
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsDynamicErrors::Engine do
4
+ # Regression test to ensure that the application's URL helpers are available
5
+ # to the engine. This is necessary because the pages generated by the errors
6
+ # controller within the engine use the application's layouts, and these may
7
+ # contain URL helpers for the main application. By default the engine cannot
8
+ # access these. We can't just use 'main_app.<url_helper>' in the layouts,
9
+ # either, as that would break the layouts when used in the application.
10
+ it "should have access to application helpers" do
11
+ visit '/errors/url_helpers'
12
+ page.should have_link('New Thing', :href => new_thing_path)
13
+ end
14
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsDynamicErrors do
4
+ context "when the error is manually generated via the errors path" do
5
+ it "displays the error code" do
6
+ visit '/errors/invalid_ninja'
7
+ page.should have_content("invalid_ninja")
8
+ end
9
+ end
10
+
11
+ context "when the error is automatically generated via the middleware" do
12
+ before(:each) do
13
+ Rails.application.config.rails_dynamic_errors.http_error_status_codes_to_handle = []
14
+ end
15
+
16
+ it "does not interfere with valid requests" do
17
+ thing = Thing.create!(:name => "Test")
18
+ visit thing_path(thing)
19
+ page.should have_content("Name: Test")
20
+ end
21
+
22
+ context "when the application is configured to show exceptions" do
23
+ around(:each) do |example|
24
+ original = set_environment_variable('action_dispatch.show_exceptions', true)
25
+ example.run
26
+ set_environment_variable('action_dispatch.show_exceptions', original)
27
+ end
28
+
29
+ context "and not configured to show detailed exceptions" do
30
+ around(:each) do |example|
31
+ original = set_environment_variable('action_dispatch.show_detailed_exceptions', false)
32
+ example.run
33
+ set_environment_variable('action_dispatch.show_exceptions', original)
34
+ end
35
+
36
+ context "receives a 404 error" do
37
+ context "and is configured to handle it" do
38
+ before(:each) do
39
+ Rails.application.config.rails_dynamic_errors.http_error_status_codes_to_handle = [404]
40
+ end
41
+
42
+ context "and the error was generated by an invalid route" do
43
+ it "displays a 404 error page" do
44
+ visit '/bogus_route'
45
+ page.should have_content("404 Not Found")
46
+ end
47
+ end
48
+
49
+ context "and the error was generated by a valid route with a non-existent resource" do
50
+ it "displays a 404 error page" do
51
+ visit '/things/-1'
52
+ page.should have_content("404 Not Found")
53
+ end
54
+ end
55
+ end
56
+
57
+ context "and is not configured to handle it" do
58
+ context "and the error was generated by an invalid route" do
59
+ it "displays the standard Rails 404 error page" do
60
+ visit '/bogus_route'
61
+ page.should_not have_content("404 Not Found")
62
+ end
63
+ end
64
+
65
+ context "and the error was generated by a valid route with a non-existent resource" do
66
+ it "displays the standard Rails 404 error page" do
67
+ visit '/things/-1'
68
+ page.should_not have_content("404 Not Found")
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ context "receives a 500 error" do
75
+ context "and is configured to handle it" do
76
+ before(:each) do
77
+ Rails.application.config.rails_dynamic_errors.http_error_status_codes_to_handle = [500]
78
+ end
79
+
80
+ context "when an internal server error is encountered" do
81
+ it "displays a 500 error page" do
82
+ visit '/booms/1'
83
+ page.should have_content("500 Internal Server Error")
84
+ end
85
+ end
86
+ end
87
+
88
+ context "and is not configured to handle it" do
89
+ it "displays the standard Rails 500 error page" do
90
+ visit '/booms/1'
91
+ page.should_not have_content("500 Internal Server Error")
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ context "and configured to show detailed exceptions" do
98
+ around(:each) do |example|
99
+ original = set_environment_variable('action_dispatch.show_detailed_exceptions', true)
100
+ example.run
101
+ set_environment_variable('action_dispatch.show_exceptions', original)
102
+ end
103
+
104
+ context "receives a 404 error" do
105
+ context "and the error was generated by an invalid route" do
106
+ it "displays the standard Rails 404 error page" do
107
+ visit '/bogus_route'
108
+ page.should_not have_content("404 Not Found")
109
+ end
110
+ end
111
+
112
+ context "and the error was generated by a valid route with a non-existent resource" do
113
+ it "displays the standard Rails 404 error page" do
114
+ visit '/things/-1'
115
+ page.should_not have_content("404 Not Found")
116
+ end
117
+ end
118
+ end
119
+
120
+ context "receives a 500 error" do
121
+ context "when it is not configured to handle 500 errors" do
122
+ it "displays the standard Rails 500 error page" do
123
+ visit '/booms/1'
124
+ page.should_not have_content("500 Internal Server Error")
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ context "when the application is not configured to show exceptions" do
132
+ around(:each) do |example|
133
+ original = set_environment_variable('action_dispatch.show_exceptions', false)
134
+ example.run
135
+ set_environment_variable('action_dispatch.show_exceptions', original)
136
+ end
137
+
138
+ context "receives a 404 error" do
139
+ context "and the error was generated by an invalid route" do
140
+ it "displays the standard Rails 404 error page" do
141
+ expect { visit '/bogus_route' }.to raise_error
142
+ end
143
+ end
144
+
145
+ context "and the error was generated by a valid route with a non-existent resource" do
146
+ it "displays the standard Rails 404 error page" do
147
+ expect { visit '/things/-1' }.to raise_error
148
+ end
149
+ end
150
+ end
151
+
152
+ context "receives a 500 error" do
153
+ context "when it is not configured to handle 500 errors" do
154
+ it "displays the standard Rails 500 error page" do
155
+ expect { visit '/booms/1' }.to raise_error
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ def set_environment_variable(variable, value)
163
+ original_value = Rails.application.env_config[variable]
164
+ Rails.application.env_config[variable] = value
165
+ original_value
166
+ end
167
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'generator_spec'
3
+
4
+ describe RailsDynamicErrors::InstallGenerator do
5
+ destination File.expand_path("../../../../dummy/tmp/", __FILE__)
6
+
7
+ before(:each) do
8
+ prepare_destination
9
+ config_dir = File.expand_path("../../../../dummy/tmp/config/", __FILE__)
10
+ FileUtils.mkdir(config_dir)
11
+ @config_application_file = File.join(config_dir, 'application.rb')
12
+ config_routes_file = File.join(config_dir, 'routes.rb')
13
+ File.open(@config_application_file, 'w') { |f| f.write("module Dummy\n class Application < Rails::Application\n end\nend") }
14
+ File.open(config_routes_file, 'w') { |f| f.write("Rails.application.routes.draw do\nend") }
15
+ run_generator
16
+ end
17
+
18
+ it "inserts options into config/application.rb" do
19
+ text = <<-INSERTION
20
+ # config.rails_dynamic_errors.http_error_status_codes_to_handle = [404, 422]
21
+ INSERTION
22
+ File.read(@config_application_file).should include(text)
23
+ end
24
+
25
+ it "mounts the engine in config/routes.rb" do
26
+ assert_file "config/routes.rb", /mount RailsDynamicErrors::Engine/
27
+ end
28
+
29
+ it "mounts the engine in config/routes.rb at the 'errors' path" do
30
+ assert_file "config/routes.rb", /mount RailsDynamicErrors::Engine, at: '#{RailsDynamicErrors::DEFAULT_MOUNT_PATH}'/
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsDynamicErrors::Engine do
4
+ describe "::mounted_at" do
5
+ it "returns the path at which the engine is mounted within a Rails application" do
6
+ RailsDynamicErrors::Engine.mounted_at.should eq(RailsDynamicErrors::DEFAULT_MOUNT_PATH)
7
+ end
8
+
9
+ it "creates a set of namespaced configuration options in the Rails application it is mounted within" do
10
+ Rails.application.config.rails_dynamic_errors.class.should eq(ActiveSupport::OrderedOptions)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsDynamicErrors::DynamicErrors do
4
+ before(:each) do
5
+ @rails_no_route_response = [404, {'X-Cascade' => 'pass'}, ['Not Found']]
6
+ @env_options = {}
7
+
8
+ # Engine this middleware is contained within is mountable, so we have to take
9
+ # that into account
10
+ RailsDynamicErrors::Engine.stub(:mounted_at).and_return('/errors')
11
+
12
+ # Prevent the actual Rails application from processing the request
13
+ Rails.application.routes.stub(:call)
14
+ end
15
+
16
+ context "when the request is local" do
17
+ before(:each) do
18
+ @env_options['action_dispatch.show_detailed_exceptions'] = true
19
+ end
20
+
21
+ context "when an exception bubbles up from the Rails application or a middleware further down the stack" do
22
+ it "re-raises the exception" do
23
+ exception = ActionController::RoutingError.new("Not Found")
24
+ app = double("app")
25
+ app.stub(:call) { raise exception }
26
+ input_env = env_for('/valid_route/-1', @env_options)
27
+ middleware = middleware_to_handle_codes(app, [404])
28
+ expect { middleware.call(input_env) }.to raise_error(exception)
29
+ end
30
+ end
31
+
32
+ context "when the response received has a 404 status code and X-Cascade header set to 'pass'" do
33
+ it "passed on the response" do
34
+ app = double("app")
35
+ app.stub(:call).and_return(@rails_no_route_response)
36
+ input_env = env_for('/invalid_route', @env_options)
37
+ middleware = middleware_to_handle_codes(app, [404])
38
+ response = middleware.call(input_env)
39
+ response.should eq(@rails_no_route_response)
40
+ end
41
+ end
42
+ end
43
+
44
+ context "when the request is remote" do
45
+ before(:each) do
46
+ @env_options['action_dispatch.show_detailed_exceptions'] = false
47
+ end
48
+
49
+ context "when exceptions should be shown" do
50
+ before(:each) do
51
+ @env_options['action_dispatch.show_exceptions'] = true
52
+ end
53
+
54
+ context "when an exception bubbles up from the Rails application or a middleware further down the stack" do
55
+ before(:each) do
56
+ @exception = ActionController::RoutingError.new("Not Found")
57
+ @app = double("app")
58
+ @app.stub(:call) { raise @exception }
59
+ @input_env = env_for('/valid_route/-1', @env_options)
60
+ end
61
+
62
+ context "and it is configured to handle the HTTP status code that exception is associated with" do
63
+ before(:each) do
64
+ @middleware = middleware_to_handle_codes(@app, [404])
65
+ end
66
+
67
+ it "updates the environment of the request to point to a path that will generate a dynamic error page for the status code" do
68
+ (code, output_env) = @middleware.call(@input_env)
69
+ @input_env['PATH_INFO'].should eq('/errors/404')
70
+ end
71
+
72
+ it "updates the environment of the request with the exception that caused the error" do
73
+ (code, output_env) = @middleware.call(@input_env)
74
+ @input_env['action_dispatch.exception'].should eq(@exception)
75
+ end
76
+
77
+ it "calls the router of the Rails application it's embedded within to process the updated environment" do
78
+ Rails.application.routes.should_receive(:call).with(@input_env)
79
+ (code, output_env) = @middleware.call(@input_env)
80
+ end
81
+ end
82
+
83
+ context "and it is not configured to handle the HTTP status code that exception is associated with" do
84
+ it "re-raises the exception" do
85
+ middleware = middleware_to_handle_codes(@app, [])
86
+ expect { middleware.call(@input_env) }.to raise_error(@exception)
87
+ end
88
+ end
89
+ end
90
+
91
+ context "when the response received has a 404 status code and X-Cascade header set to 'pass'" do
92
+ before(:each) do
93
+ @app = double("app")
94
+ @app.stub(:call).and_return([404, {'X-Cascade' => 'pass'}, ['Not Found']])
95
+ @input_env = env_for('/invalid_route', @env_options)
96
+ end
97
+
98
+ context "and it is configured to handle 404 status codes" do
99
+ before(:each) do
100
+ @middleware = middleware_to_handle_codes(@app, [404])
101
+ end
102
+
103
+ it "updates the environment of the request to point to a path that will generate a dynamic error page for the status code" do
104
+ (code, output_env) = @middleware.call(@input_env)
105
+ @input_env['PATH_INFO'].should eq('/errors/404')
106
+ end
107
+
108
+ it "updates the environment of the request with a routing exception" do
109
+ (code, output_env) = @middleware.call(@input_env)
110
+ @input_env['action_dispatch.exception'].class.should eq(ActionController::RoutingError)
111
+ end
112
+
113
+ it "calls the router of the Rails application it's embedded within to process the updated environment" do
114
+ Rails.application.routes.should_receive(:call).with(@input_env)
115
+ (code, output_env) = @middleware.call(@input_env)
116
+ end
117
+ end
118
+
119
+ context "and it is not configured to handle 404 status codes" do
120
+ it "passes on the response" do
121
+ middleware = middleware_to_handle_codes(@app, [])
122
+ response = middleware.call(@input_env)
123
+ response.should eq([404, {'X-Cascade' => 'pass'}, ['Not Found']])
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ context "when exceptions should be raised" do
130
+ before(:each) do
131
+ @env_options['action_dispatch.show_exceptions'] = false
132
+ end
133
+
134
+ context "when an exception bubbles up from the Rails application or a middleware further down the stack" do
135
+ it "re-raises the exception" do
136
+ exception = ActionController::RoutingError.new("Not Found")
137
+ app = double("app")
138
+ app.stub(:call) { raise exception }
139
+ input_env = env_for('/valid_route/-1', @env_options)
140
+ middleware = middleware_to_handle_codes(app, [404])
141
+ expect { middleware.call(input_env) }.to raise_error(exception)
142
+ end
143
+ end
144
+
145
+ context "when the response received has a 404 status code and X-Cascade header set to 'pass'" do
146
+ it "passes on the response" do
147
+ app = double("app")
148
+ app.stub(:call).and_return([404, {'X-Cascade' => 'pass'}, ['Not Found']])
149
+ input_env = env_for('/invalid_route', @env_options)
150
+ middleware = middleware_to_handle_codes(app, [])
151
+ response = middleware.call(input_env)
152
+ response.should eq([404, {'X-Cascade' => 'pass'}, ['Not Found']])
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ def middleware_to_handle_codes(app, codes)
159
+ handle_codes(codes)
160
+ RailsDynamicErrors::DynamicErrors.new(app)
161
+ end
162
+
163
+ def handle_codes(codes)
164
+ Rails.application.config.rails_dynamic_errors.http_error_status_codes_to_handle = codes
165
+ end
166
+
167
+ def env_for(url, options = {})
168
+ env = Rack::MockRequest.env_for(url, options)
169
+ env
170
+ end
171
+ end
@@ -0,0 +1,43 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../dummy/config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+ require 'rspec/autorun'
6
+ require 'capybara/rspec'
7
+
8
+ # Requires supporting ruby files with custom matchers and macros, etc,
9
+ # in spec/support/ and its subdirectories.
10
+ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
11
+
12
+ # Checks for pending migrations before tests are run.
13
+ # If you are not using ActiveRecord, you can remove this line.
14
+ ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
15
+
16
+ RSpec.configure do |config|
17
+ # ## Mock Framework
18
+ #
19
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
20
+ #
21
+ # config.mock_with :mocha
22
+ # config.mock_with :flexmock
23
+ # config.mock_with :rr
24
+
25
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
26
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
27
+
28
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
29
+ # examples within a transaction, remove the following line or assign false
30
+ # instead of true.
31
+ config.use_transactional_fixtures = true
32
+
33
+ # If true, the base class of anonymous controllers will be inferred
34
+ # automatically. This will be the default behavior in future versions of
35
+ # rspec-rails.
36
+ config.infer_base_class_for_anonymous_controllers = false
37
+
38
+ # Run specs in random order to surface order dependencies. If you find an
39
+ # order dependency and want to debug it, you can fix the order by providing
40
+ # the seed, which is printed after each run.
41
+ # --seed 1234
42
+ config.order = "random"
43
+ end
@@ -0,0 +1,19 @@
1
+ # From ActionDispatch::ExceptionWrapper
2
+ HTTP_STATUS_CODE_RAILS_EXCEPTIONS = {
3
+ 400 => ActionController::BadRequest ,
4
+ # 400 => ActionDispatch::ParamsParser::ParseError ,
5
+ # 400 => ActionController::ParameterMissing ,
6
+ 404 => ActionController::RoutingError ,
7
+ # 404 => AbstractController::ActionNotFound ,
8
+ 405 => ActionController::MethodNotAllowed ,
9
+ # 405 => ActionController::UnknownHttpMethod ,
10
+ 406 => ActionController::UnknownFormat ,
11
+ 422 => ActionController::InvalidAuthenticityToken,
12
+ 501 => ActionController::NotImplemented ,
13
+ }
14
+
15
+ def build_environment_after_exception_for_status_code(status_code, message = nil)
16
+ @exception = HTTP_STATUS_CODE_RAILS_EXCEPTIONS.fetch(status_code, Exception).new(message)
17
+ controller.env["PATH_INFO"] = "/#{status_code}"
18
+ controller.env["action_dispatch.exception"] = @exception
19
+ end