rails_dynamic_errors 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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