hanami 2.0.3 → 2.1.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/LICENSE.md +1 -1
- data/README.md +25 -9
- data/hanami.gemspec +2 -2
- data/lib/hanami/config/actions.rb +0 -4
- data/lib/hanami/config/views.rb +0 -4
- data/lib/hanami/config.rb +54 -0
- data/lib/hanami/extensions/action/slice_configured_action.rb +15 -7
- data/lib/hanami/extensions/action.rb +4 -4
- data/lib/hanami/extensions/router/errors.rb +58 -0
- data/lib/hanami/extensions/view/context.rb +129 -60
- data/lib/hanami/extensions/view/part.rb +26 -0
- data/lib/hanami/extensions/view/scope.rb +26 -0
- data/lib/hanami/extensions/view/slice_configured_context.rb +0 -2
- data/lib/hanami/extensions/view/slice_configured_helpers.rb +44 -0
- data/lib/hanami/extensions/view/slice_configured_view.rb +106 -21
- data/lib/hanami/extensions/view/standard_helpers.rb +14 -0
- data/lib/hanami/extensions.rb +10 -3
- data/lib/hanami/helpers/form_helper/form_builder.rb +1391 -0
- data/lib/hanami/helpers/form_helper/values.rb +75 -0
- data/lib/hanami/helpers/form_helper.rb +213 -0
- data/lib/hanami/middleware/public_errors_app.rb +75 -0
- data/lib/hanami/middleware/render_errors.rb +93 -0
- data/lib/hanami/slice.rb +27 -2
- data/lib/hanami/slice_configurable.rb +3 -2
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +1 -1
- data/lib/hanami.rb +1 -1
- data/spec/integration/action/view_rendering/view_context_spec.rb +221 -0
- data/spec/integration/action/view_rendering_spec.rb +0 -18
- data/spec/integration/rack_app/middleware_spec.rb +23 -23
- data/spec/integration/rack_app/rack_app_spec.rb +5 -1
- data/spec/integration/view/config/default_context_spec.rb +149 -0
- data/spec/integration/view/{inflector_spec.rb → config/inflector_spec.rb} +1 -1
- data/spec/integration/view/config/part_class_spec.rb +147 -0
- data/spec/integration/view/config/part_namespace_spec.rb +103 -0
- data/spec/integration/view/config/paths_spec.rb +119 -0
- data/spec/integration/view/config/scope_class_spec.rb +147 -0
- data/spec/integration/view/config/scope_namespace_spec.rb +103 -0
- data/spec/integration/view/config/template_spec.rb +38 -0
- data/spec/integration/view/context/request_spec.rb +3 -7
- data/spec/integration/view/helpers/form_helper_spec.rb +174 -0
- data/spec/integration/view/helpers/part_helpers_spec.rb +124 -0
- data/spec/integration/view/helpers/scope_helpers_spec.rb +84 -0
- data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +162 -0
- data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +119 -0
- data/spec/integration/view/slice_configuration_spec.rb +9 -9
- data/spec/integration/web/render_detailed_errors_spec.rb +90 -0
- data/spec/integration/web/render_errors_spec.rb +240 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/matchers.rb +32 -0
- data/spec/unit/hanami/config/actions/default_values_spec.rb +0 -4
- data/spec/unit/hanami/config/render_detailed_errors_spec.rb +25 -0
- data/spec/unit/hanami/config/render_errors_spec.rb +25 -0
- data/spec/unit/hanami/config/views_spec.rb +0 -18
- data/spec/unit/hanami/extensions/view/context_spec.rb +59 -0
- data/spec/unit/hanami/helpers/form_helper_spec.rb +2826 -0
- data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +27 -0
- data/spec/unit/hanami/router/errors/not_found_error_spec.rb +22 -0
- data/spec/unit/hanami/slice_configurable_spec.rb +18 -0
- data/spec/unit/hanami/version_spec.rb +1 -1
- data/spec/unit/hanami/web/rack_logger_spec.rb +1 -1
- metadata +65 -33
- data/spec/integration/action/view_integration_spec.rb +0 -165
- data/spec/integration/view/part_namespace_spec.rb +0 -96
- data/spec/integration/view/path_spec.rb +0 -56
- data/spec/integration/view/template_spec.rb +0 -68
- data/spec/isolation/hanami/application/already_configured_spec.rb +0 -19
- data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +0 -10
- data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +0 -14
- data/spec/isolation/hanami/application/not_configured_spec.rb +0 -9
- data/spec/isolation/hanami/application/routes/configured_spec.rb +0 -44
- data/spec/isolation/hanami/application/routes/not_configured_spec.rb +0 -16
- data/spec/isolation/hanami/boot/success_spec.rb +0 -50
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Helpers
|
5
|
+
module FormHelper
|
6
|
+
# Values from params and form helpers.
|
7
|
+
#
|
8
|
+
# It's responsible to populate input values with data coming from params
|
9
|
+
# and inline values specified via form helpers like `text_field`.
|
10
|
+
#
|
11
|
+
# @since 2.0.0
|
12
|
+
# @api private
|
13
|
+
class Values
|
14
|
+
# @since 2.0.0
|
15
|
+
# @api private
|
16
|
+
GET_SEPARATOR = "."
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
# @since 2.0.0
|
20
|
+
attr_reader :csrf_token
|
21
|
+
|
22
|
+
# @since 2.0.0
|
23
|
+
# @api private
|
24
|
+
def initialize(values: {}, params: {}, csrf_token: nil)
|
25
|
+
@values = values.to_h
|
26
|
+
@params = params.to_h
|
27
|
+
@csrf_token = csrf_token
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the value (if present) for the given key.
|
31
|
+
# Nested values are expressed with an array if symbols.
|
32
|
+
#
|
33
|
+
# @since 2.0.0
|
34
|
+
# @api private
|
35
|
+
def get(*keys)
|
36
|
+
get_from_params(*keys) || get_from_values(*keys)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @since 2.0.0
|
42
|
+
# @api private
|
43
|
+
def get_from_params(*keys)
|
44
|
+
keys.map! { |key| /\A\d+\z/.match?(key.to_s) ? key.to_s.to_i : key }
|
45
|
+
@params.dig(*keys)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @since 2.0.0
|
49
|
+
# @api private
|
50
|
+
def get_from_values(*keys)
|
51
|
+
head, *tail = *keys
|
52
|
+
result = @values[head]
|
53
|
+
|
54
|
+
tail.each do |k|
|
55
|
+
break if result.nil?
|
56
|
+
|
57
|
+
result = dig(result, k)
|
58
|
+
end
|
59
|
+
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
# @since 2.0.0
|
64
|
+
# @api private
|
65
|
+
def dig(base, key)
|
66
|
+
case base
|
67
|
+
when ::Hash then base[key]
|
68
|
+
when Array then base[key.to_s.to_i]
|
69
|
+
when ->(r) { r.respond_to?(key) } then base.public_send(key)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami/view"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module Helpers
|
7
|
+
# Helper methods for generating HTML forms.
|
8
|
+
#
|
9
|
+
# These helpers will be automatically available in your view templates, part classes and scope
|
10
|
+
# classes.
|
11
|
+
#
|
12
|
+
# This module provides one primary method: {#form_for}, yielding an HTML form builder. This
|
13
|
+
# integrates with request params and template locals to populate the form with appropriate
|
14
|
+
# values.
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
# @since 2.0.0
|
18
|
+
module FormHelper
|
19
|
+
require_relative "form_helper/form_builder"
|
20
|
+
|
21
|
+
# Default HTTP method for form
|
22
|
+
#
|
23
|
+
# @since 2.0.0
|
24
|
+
# @api private
|
25
|
+
DEFAULT_METHOD = "POST"
|
26
|
+
|
27
|
+
# Default charset
|
28
|
+
#
|
29
|
+
# @since 2.0.0
|
30
|
+
# @api private
|
31
|
+
DEFAULT_CHARSET = "utf-8"
|
32
|
+
|
33
|
+
# CSRF Token session key
|
34
|
+
#
|
35
|
+
# This name of this key is shared with the hanami and hanami-controller gems.
|
36
|
+
#
|
37
|
+
# @since 2.0.0
|
38
|
+
# @api private
|
39
|
+
CSRF_TOKEN = :_csrf_token
|
40
|
+
|
41
|
+
include Hanami::View::Helpers::TagHelper
|
42
|
+
|
43
|
+
# Yields a form builder for constructing an HTML form and returns the resulting form string.
|
44
|
+
#
|
45
|
+
# See {FormHelper::FormBuilder} for the methods for building the form's fields.
|
46
|
+
#
|
47
|
+
# @overload form_for(base_name, url, values: _form_for_values, params: _form_for_params, **attributes)
|
48
|
+
# Builds the form using the given base name for all fields.
|
49
|
+
#
|
50
|
+
# @param base_name [String] the base
|
51
|
+
# @param url [String] the URL for submitting the form
|
52
|
+
# @param values [Hash] values to be used for populating form field values; optional,
|
53
|
+
# defaults to the template's locals or to a part's `{name => self}`
|
54
|
+
# @param params [Hash] request param values to be used for populating form field values;
|
55
|
+
# these are used in preference over the `values`; optional, defaults to the current
|
56
|
+
# request's params
|
57
|
+
# @param attributes [Hash] the HTML attributes for the form tag
|
58
|
+
# @yieldparam [FormHelper::FormBuilder] f the form builder
|
59
|
+
#
|
60
|
+
# @overload form_for(url, values: _form_for_values, params: _form_for_params, **attributes)
|
61
|
+
# @param url [String] the URL for submitting the form
|
62
|
+
# @param values [Hash] values to be used for populating form field values; optional,
|
63
|
+
# defaults to the template's locals or to a part's `{name => self}`
|
64
|
+
# @param params [Hash] request param values to be used for populating form field values;
|
65
|
+
# these are used in preference over the `values`; optional, defaults to the current
|
66
|
+
# request's params
|
67
|
+
# @param attributes [Hash] the HTML attributes for the form tag
|
68
|
+
# @yieldparam [FormHelper::FormBuilder] f the form builder
|
69
|
+
#
|
70
|
+
# @return [String] the form HTML
|
71
|
+
#
|
72
|
+
# @see FormHelper
|
73
|
+
# @see FormHelper::FormBuilder
|
74
|
+
#
|
75
|
+
# @example Basic usage
|
76
|
+
# <%= form_for("book", "/books", class: "form-horizontal") do |f| %>
|
77
|
+
# <div>
|
78
|
+
# <%= f.label "title" %>
|
79
|
+
# <%= f.text_field "title", class: "form-control" %>
|
80
|
+
# </div>
|
81
|
+
#
|
82
|
+
# <%= f.submit "Create" %>
|
83
|
+
# <% end %>
|
84
|
+
#
|
85
|
+
# =>
|
86
|
+
# <form action="/books" method="POST" accept-charset="utf-8" class="form-horizontal">
|
87
|
+
# <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
|
88
|
+
# <div>
|
89
|
+
# <label for="book-title">Title</label>
|
90
|
+
# <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
|
91
|
+
# </div>
|
92
|
+
#
|
93
|
+
# <button type="submit">Create</button>
|
94
|
+
# </form>
|
95
|
+
#
|
96
|
+
# @example Without base name
|
97
|
+
#
|
98
|
+
# <%= form_for("/books", class: "form-horizontal") do |f| %>
|
99
|
+
# <div>
|
100
|
+
# <%= f.label "books.title" %>
|
101
|
+
# <%= f.text_field "books.title", class: "form-control" %>
|
102
|
+
# </div>
|
103
|
+
#
|
104
|
+
# <%= f.submit "Create" %>
|
105
|
+
# <% end %>
|
106
|
+
#
|
107
|
+
# =>
|
108
|
+
# <form action="/books" method="POST" accept-charset="utf-8" class="form-horizontal">
|
109
|
+
# <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
|
110
|
+
# <div>
|
111
|
+
# <label for="book-title">Title</label>
|
112
|
+
# <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
|
113
|
+
# </div>
|
114
|
+
#
|
115
|
+
# <button type="submit">Create</button>
|
116
|
+
# </form>
|
117
|
+
#
|
118
|
+
# @example Method override
|
119
|
+
# <%= form_for("/books/123", method: :put) do |f|
|
120
|
+
# <%= f.text_field "book.title" %>
|
121
|
+
# <%= f.submit "Update" %>
|
122
|
+
# <% end %>
|
123
|
+
#
|
124
|
+
# =>
|
125
|
+
# <form action="/books/123" accept-charset="utf-8" method="POST">
|
126
|
+
# <input type="hidden" name="_method" value="PUT">
|
127
|
+
# <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
|
128
|
+
# <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
|
129
|
+
#
|
130
|
+
# <button type="submit">Update</button>
|
131
|
+
# </form>
|
132
|
+
#
|
133
|
+
# @example Overriding values
|
134
|
+
# <%= form_for("/songs", values: {song: {title: "Envision"}}) do |f| %>
|
135
|
+
# <%= f.text_field "song.title" %>
|
136
|
+
# <%= f.submit "Create" %>
|
137
|
+
# <%= end %>
|
138
|
+
#
|
139
|
+
# =>
|
140
|
+
# <form action="/songs" accept-charset="utf-8" method="POST">
|
141
|
+
# <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
|
142
|
+
# <input type="text" name="song[title]" id="song-title" value="Envision">
|
143
|
+
#
|
144
|
+
# <button type="submit">Create</button>
|
145
|
+
# </form>
|
146
|
+
#
|
147
|
+
# @api public
|
148
|
+
# @since 2.0.0
|
149
|
+
def form_for(base_name, url = nil, values: _form_for_values, params: _form_for_params, **attributes)
|
150
|
+
url, base_name = base_name, nil if url.nil?
|
151
|
+
|
152
|
+
values = Values.new(values: values, params: params, csrf_token: _form_csrf_token)
|
153
|
+
|
154
|
+
builder = FormBuilder.new(
|
155
|
+
base_name: base_name,
|
156
|
+
values: values,
|
157
|
+
inflector: _context.inflector,
|
158
|
+
form_attributes: attributes
|
159
|
+
)
|
160
|
+
|
161
|
+
content = (block_given? ? yield(builder) : "").html_safe
|
162
|
+
|
163
|
+
builder.call(content, action: url, **attributes)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns CSRF meta tags for use via unobtrusive JavaScript (UJS) libraries.
|
167
|
+
#
|
168
|
+
# @return [String, nil] the tags, if a CSRF token is available, or nil
|
169
|
+
#
|
170
|
+
# @example
|
171
|
+
# csrf_meta_tags
|
172
|
+
#
|
173
|
+
# =>
|
174
|
+
# <meta name="csrf-param" content="_csrf_token">
|
175
|
+
# <meta name="csrf-token" content="4a038be85b7603c406dcbfad4b9cdf91ec6ca138ed6441163a07bb0fdfbe25b5">
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
# @since 2.0.0
|
179
|
+
def csrf_meta_tags
|
180
|
+
return unless (token = _form_csrf_token)
|
181
|
+
|
182
|
+
tag.meta(name: "csrf-param", content: CSRF_TOKEN) +
|
183
|
+
tag.meta(name: "csrf-token", content: token)
|
184
|
+
end
|
185
|
+
|
186
|
+
# @api private
|
187
|
+
# @since 2.0.0
|
188
|
+
def _form_for_values
|
189
|
+
if respond_to?(:_locals) # Scope
|
190
|
+
_locals
|
191
|
+
elsif respond_to?(:_name) # Part
|
192
|
+
{_name => self}
|
193
|
+
else
|
194
|
+
{}
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# @api private
|
199
|
+
# @since 2.0.0
|
200
|
+
def _form_for_params
|
201
|
+
_context.request.params
|
202
|
+
end
|
203
|
+
|
204
|
+
# @since 2.0.0
|
205
|
+
# @api private
|
206
|
+
def _form_csrf_token
|
207
|
+
return unless _context.request.session_enabled?
|
208
|
+
|
209
|
+
_context.csrf_token
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module Middleware
|
7
|
+
# The errors app given to {Hanami::Middleware::RenderErrors}, which renders a error responses
|
8
|
+
# from HTML pages kept in `public/` or as simple JSON structures.
|
9
|
+
#
|
10
|
+
# @see Hanami::Middleware::RenderErrors
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 2.1.0
|
14
|
+
class PublicErrorsApp
|
15
|
+
# @api private
|
16
|
+
# @since 2.1.0
|
17
|
+
attr_reader :public_path
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
# @since 2.1.0
|
21
|
+
def initialize(public_path)
|
22
|
+
@public_path = public_path
|
23
|
+
end
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
# @since 2.1.0
|
27
|
+
def call(env)
|
28
|
+
request = Rack::Request.new(env)
|
29
|
+
status = request.path_info[1..].to_i
|
30
|
+
content_type = request.get_header("HTTP_ACCEPT")
|
31
|
+
|
32
|
+
default_body = {
|
33
|
+
status: status,
|
34
|
+
error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500])
|
35
|
+
}
|
36
|
+
|
37
|
+
render(status, content_type, default_body)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def render(status, content_type, default_body)
|
43
|
+
body, rendered_content_type = render_content(status, content_type, default_body)
|
44
|
+
|
45
|
+
[
|
46
|
+
status,
|
47
|
+
{
|
48
|
+
"Content-Type" => "#{rendered_content_type}; charset=utf-8",
|
49
|
+
"Content-Length" => body.bytesize.to_s
|
50
|
+
},
|
51
|
+
[body]
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
def render_content(status, content_type, default_body)
|
56
|
+
if content_type.to_s.start_with?("application/json")
|
57
|
+
require "json"
|
58
|
+
[JSON.generate(default_body), "application/json"]
|
59
|
+
else
|
60
|
+
[render_html_content(status, default_body), "text/html"]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def render_html_content(status, default_body)
|
65
|
+
path = "#{public_path}/#{status}.html"
|
66
|
+
|
67
|
+
if File.exist?(path)
|
68
|
+
File.read(path)
|
69
|
+
else
|
70
|
+
default_body[:error]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
|
5
|
+
# rubocop:disable Lint/RescueException
|
6
|
+
|
7
|
+
module Hanami
|
8
|
+
module Middleware
|
9
|
+
# Rack middleware that rescues errors raised by the app renders friendly error responses, via a
|
10
|
+
# given "errors app".
|
11
|
+
#
|
12
|
+
# By default, this is enabled only in production mode.
|
13
|
+
#
|
14
|
+
# @see Hanami::Config#render_errors
|
15
|
+
# @see Hanani::Middleware::PublicErrorsApp
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @since 2.1.0
|
19
|
+
class RenderErrors
|
20
|
+
# @api private
|
21
|
+
# @since 2.1.0
|
22
|
+
class RenderableException
|
23
|
+
attr_reader :exception
|
24
|
+
attr_reader :responses
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
# @since 2.1.0
|
28
|
+
def initialize(exception, responses:)
|
29
|
+
@exception = exception
|
30
|
+
@responses = responses
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
# @since 2.1.0
|
35
|
+
def rescue_response?
|
36
|
+
responses.key?(exception.class.name)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
# @since 2.1.0
|
41
|
+
def status_code
|
42
|
+
Rack::Utils.status_code(responses[exception.class.name])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
# @since 2.1.0
|
48
|
+
def initialize(app, config, errors_app)
|
49
|
+
@app = app
|
50
|
+
@config = config
|
51
|
+
@errors_app = errors_app
|
52
|
+
end
|
53
|
+
|
54
|
+
# @api private
|
55
|
+
# @since 2.1.0
|
56
|
+
def call(env)
|
57
|
+
@app.call(env)
|
58
|
+
rescue Exception => exception
|
59
|
+
request = Rack::Request.new(env)
|
60
|
+
|
61
|
+
if @config.render_errors
|
62
|
+
render_exception(request, exception)
|
63
|
+
else
|
64
|
+
raise exception
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def render_exception(request, exception)
|
71
|
+
renderable = RenderableException.new(exception, responses: @config.render_error_responses)
|
72
|
+
|
73
|
+
status = renderable.status_code
|
74
|
+
request.path_info = "/#{status}"
|
75
|
+
request.set_header(Rack::REQUEST_METHOD, "GET")
|
76
|
+
|
77
|
+
@errors_app.call(request.env)
|
78
|
+
rescue Exception => failsafe_error
|
79
|
+
# rubocop:disable Style/StderrPuts
|
80
|
+
$stderr.puts "Error during exception rendering: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
|
81
|
+
# rubocop:enable Style/StderrPuts
|
82
|
+
|
83
|
+
[
|
84
|
+
500,
|
85
|
+
{"Content-Type" => "text/plain; charset=utf-8"},
|
86
|
+
["Internal Server Error"]
|
87
|
+
]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# rubocop:enable Lint/RescueException
|
data/lib/hanami/slice.rb
CHANGED
@@ -948,6 +948,7 @@ module Hanami
|
|
948
948
|
|
949
949
|
require_relative "slice/router"
|
950
950
|
|
951
|
+
slice = self
|
951
952
|
config = self.config
|
952
953
|
rack_monitor = self["rack.monitor"]
|
953
954
|
|
@@ -955,17 +956,41 @@ module Hanami
|
|
955
956
|
inspector: inspector,
|
956
957
|
routes: routes,
|
957
958
|
resolver: config.router.resolver.new(slice: self),
|
959
|
+
not_allowed: ROUTER_NOT_ALLOWED_HANDLER,
|
960
|
+
not_found: ROUTER_NOT_FOUND_HANDLER,
|
958
961
|
**config.router.options
|
959
962
|
) do
|
960
963
|
use(rack_monitor)
|
961
|
-
|
962
|
-
|
964
|
+
|
965
|
+
use(
|
966
|
+
Hanami::Middleware::RenderErrors,
|
967
|
+
config,
|
968
|
+
Hanami::Middleware::PublicErrorsApp.new(slice.root.join("public"))
|
969
|
+
)
|
970
|
+
|
971
|
+
if config.render_detailed_errors && Hanami.bundled?("hanami-webconsole")
|
972
|
+
require "hanami/webconsole"
|
973
|
+
use(Hanami::Webconsole::Middleware)
|
974
|
+
end
|
975
|
+
|
976
|
+
if Hanami.bundled?("hanami-controller") && config.actions.sessions.enabled?
|
977
|
+
use(*config.actions.sessions.middleware)
|
963
978
|
end
|
964
979
|
|
965
980
|
middleware_stack.update(config.middleware_stack)
|
966
981
|
end
|
967
982
|
end
|
968
983
|
|
984
|
+
ROUTER_NOT_ALLOWED_HANDLER = -> env, allowed_http_methods {
|
985
|
+
raise Hanami::Router::NotAllowedError.new(env, allowed_http_methods)
|
986
|
+
}.freeze
|
987
|
+
private_constant :ROUTER_NOT_ALLOWED_HANDLER
|
988
|
+
|
989
|
+
ROUTER_NOT_FOUND_HANDLER = -> env {
|
990
|
+
raise Hanami::Router::NotFoundError.new(env)
|
991
|
+
}.freeze
|
992
|
+
private_constant :ROUTER_NOT_FOUND_HANDLER
|
993
|
+
|
969
994
|
# rubocop:enable Metrics/AbcSize
|
970
995
|
end
|
971
996
|
# rubocop:enable Metrics/ModuleLength
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "constants"
|
3
4
|
require_relative "errors"
|
4
5
|
|
5
6
|
module Hanami
|
@@ -43,7 +44,7 @@ module Hanami
|
|
43
44
|
|
44
45
|
unless subclass.configured_for_slice?(slice)
|
45
46
|
subclass.configure_for_slice(slice)
|
46
|
-
subclass.configured_for_slices << slice
|
47
|
+
subclass.configured_for_slices << slice
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
@@ -58,7 +59,7 @@ module Hanami
|
|
58
59
|
|
59
60
|
slices = Hanami.app.slices.with_nested + [Hanami.app]
|
60
61
|
|
61
|
-
slices.detect { |slice| klass.name.
|
62
|
+
slices.detect { |slice| klass.name.start_with?("#{slice.namespace}#{MODULE_DELIMITER}") }
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
data/lib/hanami/version.rb
CHANGED
data/lib/hanami.rb
CHANGED
@@ -18,7 +18,7 @@ module Hanami
|
|
18
18
|
def self.loader
|
19
19
|
@loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
|
20
20
|
loader.ignore(
|
21
|
-
"#{loader.dirs.first}/hanami/{constants,boot,errors,prepare,rake_tasks,setup}.rb"
|
21
|
+
"#{loader.dirs.first}/hanami/{constants,boot,errors,extensions/router/errors,prepare,rake_tasks,setup}.rb"
|
22
22
|
)
|
23
23
|
end
|
24
24
|
end
|