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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +25 -9
  5. data/hanami.gemspec +2 -2
  6. data/lib/hanami/config/actions.rb +0 -4
  7. data/lib/hanami/config/views.rb +0 -4
  8. data/lib/hanami/config.rb +54 -0
  9. data/lib/hanami/extensions/action/slice_configured_action.rb +15 -7
  10. data/lib/hanami/extensions/action.rb +4 -4
  11. data/lib/hanami/extensions/router/errors.rb +58 -0
  12. data/lib/hanami/extensions/view/context.rb +129 -60
  13. data/lib/hanami/extensions/view/part.rb +26 -0
  14. data/lib/hanami/extensions/view/scope.rb +26 -0
  15. data/lib/hanami/extensions/view/slice_configured_context.rb +0 -2
  16. data/lib/hanami/extensions/view/slice_configured_helpers.rb +44 -0
  17. data/lib/hanami/extensions/view/slice_configured_view.rb +106 -21
  18. data/lib/hanami/extensions/view/standard_helpers.rb +14 -0
  19. data/lib/hanami/extensions.rb +10 -3
  20. data/lib/hanami/helpers/form_helper/form_builder.rb +1391 -0
  21. data/lib/hanami/helpers/form_helper/values.rb +75 -0
  22. data/lib/hanami/helpers/form_helper.rb +213 -0
  23. data/lib/hanami/middleware/public_errors_app.rb +75 -0
  24. data/lib/hanami/middleware/render_errors.rb +93 -0
  25. data/lib/hanami/slice.rb +27 -2
  26. data/lib/hanami/slice_configurable.rb +3 -2
  27. data/lib/hanami/version.rb +1 -1
  28. data/lib/hanami/web/rack_logger.rb +1 -1
  29. data/lib/hanami.rb +1 -1
  30. data/spec/integration/action/view_rendering/view_context_spec.rb +221 -0
  31. data/spec/integration/action/view_rendering_spec.rb +0 -18
  32. data/spec/integration/rack_app/middleware_spec.rb +23 -23
  33. data/spec/integration/rack_app/rack_app_spec.rb +5 -1
  34. data/spec/integration/view/config/default_context_spec.rb +149 -0
  35. data/spec/integration/view/{inflector_spec.rb → config/inflector_spec.rb} +1 -1
  36. data/spec/integration/view/config/part_class_spec.rb +147 -0
  37. data/spec/integration/view/config/part_namespace_spec.rb +103 -0
  38. data/spec/integration/view/config/paths_spec.rb +119 -0
  39. data/spec/integration/view/config/scope_class_spec.rb +147 -0
  40. data/spec/integration/view/config/scope_namespace_spec.rb +103 -0
  41. data/spec/integration/view/config/template_spec.rb +38 -0
  42. data/spec/integration/view/context/request_spec.rb +3 -7
  43. data/spec/integration/view/helpers/form_helper_spec.rb +174 -0
  44. data/spec/integration/view/helpers/part_helpers_spec.rb +124 -0
  45. data/spec/integration/view/helpers/scope_helpers_spec.rb +84 -0
  46. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +162 -0
  47. data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +119 -0
  48. data/spec/integration/view/slice_configuration_spec.rb +9 -9
  49. data/spec/integration/web/render_detailed_errors_spec.rb +90 -0
  50. data/spec/integration/web/render_errors_spec.rb +240 -0
  51. data/spec/spec_helper.rb +1 -1
  52. data/spec/support/matchers.rb +32 -0
  53. data/spec/unit/hanami/config/actions/default_values_spec.rb +0 -4
  54. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +25 -0
  55. data/spec/unit/hanami/config/render_errors_spec.rb +25 -0
  56. data/spec/unit/hanami/config/views_spec.rb +0 -18
  57. data/spec/unit/hanami/extensions/view/context_spec.rb +59 -0
  58. data/spec/unit/hanami/helpers/form_helper_spec.rb +2826 -0
  59. data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +27 -0
  60. data/spec/unit/hanami/router/errors/not_found_error_spec.rb +22 -0
  61. data/spec/unit/hanami/slice_configurable_spec.rb +18 -0
  62. data/spec/unit/hanami/version_spec.rb +1 -1
  63. data/spec/unit/hanami/web/rack_logger_spec.rb +1 -1
  64. metadata +65 -33
  65. data/spec/integration/action/view_integration_spec.rb +0 -165
  66. data/spec/integration/view/part_namespace_spec.rb +0 -96
  67. data/spec/integration/view/path_spec.rb +0 -56
  68. data/spec/integration/view/template_spec.rb +0 -68
  69. data/spec/isolation/hanami/application/already_configured_spec.rb +0 -19
  70. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +0 -10
  71. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +0 -14
  72. data/spec/isolation/hanami/application/not_configured_spec.rb +0 -9
  73. data/spec/isolation/hanami/application/routes/configured_spec.rb +0 -44
  74. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +0 -16
  75. 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
- if Hanami.bundled?("hanami-controller")
962
- use(*config.actions.sessions.middleware) if config.actions.sessions.enabled?
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 # WIP
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.include?(slice.namespace.to_s) }
62
+ slices.detect { |slice| klass.name.start_with?("#{slice.namespace}#{MODULE_DELIMITER}") }
62
63
  end
63
64
  end
64
65
 
@@ -7,7 +7,7 @@ module Hanami
7
7
  # @api private
8
8
  module Version
9
9
  # @api public
10
- VERSION = "2.0.3"
10
+ VERSION = "2.1.0.beta1"
11
11
 
12
12
  # @since 0.9.0
13
13
  # @api private
@@ -29,7 +29,7 @@ module Hanami
29
29
  ROUTER_PARAMS = "router.params"
30
30
  private_constant :ROUTER_PARAMS
31
31
 
32
- CONTENT_LENGTH = "Content-Length"
32
+ CONTENT_LENGTH = "CONTENT_LENGTH"
33
33
  private_constant :CONTENT_LENGTH
34
34
 
35
35
  MILISECOND = "ms"
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