hanami-controller 2.0.0.beta1 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11f51222319fd677dad5bcad139e3a9fe4ec4a9099a44cab8e1809de78ca7def
4
- data.tar.gz: 38367d9e9a0831e0bae832ffc4a7528ba97ecafb399b57e085d723de522f0219
3
+ metadata.gz: a363029b7df3ddc560e2975862f72548788a2a2910c2d32519f073556b4bfbb2
4
+ data.tar.gz: e7d67505e3b0161a656d24e1ff634bd02c11d4fad1e217a1ee482271be6dbbd9
5
5
  SHA512:
6
- metadata.gz: 5522815b014bd6bf4ad6750a12c80a6db7d1aaab15562004a7898bc6cf5a5f0bbfa93d171cee31268685268ead3d37d6aa42be1bfc780c7a3bba2207814df087
7
- data.tar.gz: 379911174cd6deb4637e6dcb80731d1d221f6bb4f3c184001f6f526e8d87cdf385aa0e114a1c4fa78c2dc3361015074b5afbe428a2b630a8ff4cd17f180e5908
6
+ metadata.gz: 0a9aeb4ea44db1fe6f137dee8f3e5e05bda318a8c53f1f57c30204a0077b5dcfd82aefab569fd7169c2c728de2331fcaa52f1fcdec2e3adb6d4679c38791edf6
7
+ data.tar.gz: 726de07b87cd8055da9973ea0365c00bde1f8808a1593ef3830051e196c722f001f6dc13615182f067542c605c9437259d1d36028031867edf3abd8d9612e817
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  Complete, fast and testable actions for Rack
4
4
 
5
+ ## v2.0.0.beta4 - 2022-10-24
6
+
7
+ ### Added
8
+
9
+ - [Tim Riley] Add `Response#flash`, and delgate to request object for both `Response#session` and `Response#flash`, ensuring the same objects are used when accessed via either request or response (#399)
10
+
11
+ ### Fixed
12
+
13
+ - [Benjamin Klotz] When a params validation schema is provided (in a `params do` block), only return the validated params from `request.params` (#375)
14
+ - [Sean Collins] Handle dry-schema's messages hash now being frozen by default (#391)
15
+
16
+ ### Changed
17
+
18
+ - [Tim Riley] When `Action.accept` is declared (or `Action::Config.accepted_formats` configured), return a 406 error if an `Accept` request header is present but is not acceptable. In the absence of an `Accept` header, return a 415 error if a `Content-Type` header is present but not acceptable. If neither header is provided, accept the request. (#396)
19
+ - [Tim Riley] Add `Action.handle_exception` class method as a shortcut for `Hanami::Action::Config#handle_exception` (#394)
20
+ - [Tim Riley] Significantly reduce memory usage by leveraging recent dry-configurable changes, and relocating `accepted_formats`, `before_callbacks`, `after_callbacks` inheritable attributes to `config` (#392)
21
+ - [Tim Riley] Make params validation schemas (defined in `params do` block) inheritable to subclasses (#394)
22
+ - [Benhamin Klotz, Tim Riley] Raise `Hanami::Action::MissingSessionError` with a friendly message if `Request#session`, `Request#flash`, `Response#session` or `Response#flash` are called for an action that does not already include `Hanami::Action:Session` mixin (#379 via #395)
23
+
5
24
  ## v2.0.0.beta1 - 2022-07-20
6
25
 
7
26
  ### Fixed
data/README.md CHANGED
@@ -771,7 +771,7 @@ end
771
771
  # When called with "\*/\*" => 200
772
772
  # When called with "text/html" => 200
773
773
  # When called with "application/json" => 200
774
- # When called with "application/xml" => 406
774
+ # When called with "application/xml" => 415
775
775
  ```
776
776
 
777
777
  You can check if the requested MIME type is accepted by the client.
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require_relative "mime"
5
+
6
+ module Hanami
7
+ class Action
8
+ class Config < Dry::Configurable::Config
9
+ # Default MIME type to format mapping
10
+ #
11
+ # @since 0.2.0
12
+ # @api private
13
+ DEFAULT_FORMATS = {
14
+ "application/octet-stream" => :all,
15
+ "*/*" => :all,
16
+ "text/html" => :html
17
+ }.freeze
18
+
19
+ # Default public directory
20
+ #
21
+ # This serves as the root directory for file downloads
22
+ #
23
+ # @since 1.0.0
24
+ #
25
+ # @api private
26
+ DEFAULT_PUBLIC_DIRECTORY = "public"
27
+
28
+ # @!attribute [rw] handled_exceptions
29
+ #
30
+ # Specifies how to handle exceptions with an HTTP status.
31
+ #
32
+ # Raised exceptions will return the corresponding HTTP status.
33
+ #
34
+ # @return [Hash{Exception=>Integer}] exception classes as keys and HTTP statuses as values
35
+ #
36
+ # @example
37
+ # config.handled_exceptions = {ArgumentError => 400}
38
+ #
39
+ # @since 0.2.0
40
+
41
+ # Specifies how to handle exceptions with an HTTP status
42
+ #
43
+ # Raised exceptions will return the corresponding HTTP status
44
+ #
45
+ # The specified exceptions will be merged with any previously configured
46
+ # exceptions
47
+ #
48
+ # @param exceptions [Hash{Exception=>Integer}] exception classes as keys
49
+ # and HTTP statuses as values
50
+ #
51
+ # @return [void]
52
+ #
53
+ # @example
54
+ # config.handle_exceptions(ArgumentError => 400}
55
+ #
56
+ # @see handled_exceptions
57
+ #
58
+ # @since 0.2.0
59
+ def handle_exception(exceptions)
60
+ self.handled_exceptions = handled_exceptions
61
+ .merge(exceptions)
62
+ .sort { |(ex1, _), (ex2, _)| ex1.ancestors.include?(ex2) ? -1 : 1 }
63
+ .to_h
64
+ end
65
+
66
+ # @!attribute [rw] formats
67
+ #
68
+ # Specifies the MIME type to format mapping
69
+ #
70
+ # @return [Hash{String=>Symbol}] MIME type strings as keys and format symbols as values
71
+ #
72
+ # @see format
73
+ # @see Hanami::Action::Mime
74
+ #
75
+ # @example
76
+ # config.formats = {"text/html" => :html}
77
+ #
78
+ # @since 0.2.0
79
+
80
+ # Registers a MIME type to format mapping
81
+ #
82
+ # @param hash [Hash{Symbol=>String}] format symbols as keys and the MIME
83
+ # type strings must as values
84
+ #
85
+ # @return [void]
86
+ #
87
+ # @see formats
88
+ # @see Hanami::Action::Mime
89
+ #
90
+ # @example
91
+ # config.format html: "text/html"
92
+ #
93
+ # @since 0.2.0
94
+ def format(hash)
95
+ symbol, mime_type = *Utils::Kernel.Array(hash)
96
+ formats[Utils::Kernel.String(mime_type)] = Utils::Kernel.Symbol(symbol)
97
+ end
98
+
99
+ # Returns the configured format for the given MIME type
100
+ #
101
+ # @param mime_type [#to_s,#to_str] A mime type
102
+ #
103
+ # @return [Symbol,nil] the corresponding format, nil if not found
104
+ #
105
+ # @see format
106
+ #
107
+ # @since 0.2.0
108
+ # @api private
109
+ def format_for(mime_type)
110
+ formats[mime_type]
111
+ end
112
+
113
+ # Returns the configured format's MIME types
114
+ #
115
+ # @return [Array<String>] the format's MIME types
116
+ #
117
+ # @see formats=
118
+ # @see format
119
+ #
120
+ # @since 0.8.0
121
+ #
122
+ # @api private
123
+ def mime_types
124
+ # FIXME: this isn't efficient. speed it up!
125
+ ((formats.keys - DEFAULT_FORMATS.keys) +
126
+ Hanami::Action::Mime::TYPES.values).freeze
127
+ end
128
+
129
+ # Returns a MIME type for the given format
130
+ #
131
+ # @param format [#to_sym] a format
132
+ #
133
+ # @return [String,nil] the corresponding MIME type, if present
134
+ #
135
+ # @since 0.2.0
136
+ # @api private
137
+ def mime_type_for(format)
138
+ formats.key(format)
139
+ end
140
+
141
+ # @since 2.0.0
142
+ # @api private
143
+ def accepted_mime_types
144
+ accepted_formats.any? ? Mime.restrict_mime_types(self) : mime_types
145
+ end
146
+
147
+ # @!attribute [rw] default_request_format
148
+ #
149
+ # Sets a format as default fallback for all the requests without a strict
150
+ # requirement for the MIME type.
151
+ #
152
+ # The given format must be coercible to a symbol, and be a valid MIME
153
+ # type alias. If it isn't, at runtime the framework will raise an
154
+ # `Hanami::Controller::UnknownFormatError`.
155
+ #
156
+ # By default, this value is nil.
157
+ #
158
+ # @return [Symbol]
159
+ #
160
+ # @see Hanami::Action::Mime
161
+ #
162
+ # @since 0.5.0
163
+
164
+ # @!attribute [rw] default_response_format
165
+ #
166
+ # Sets a format to be used for all responses regardless of the request
167
+ # type.
168
+ #
169
+ # The given format must be coercible to a symbol, and be a valid MIME
170
+ # type alias. If it isn't, at the runtime the framework will raise an
171
+ # `Hanami::Controller::UnknownFormatError`.
172
+ #
173
+ # By default, this value is nil.
174
+ #
175
+ # @return [Symbol]
176
+ #
177
+ # @see Hanami::Action::Mime
178
+ #
179
+ # @since 0.5.0
180
+
181
+ # @!attribute [rw] default_charset
182
+ #
183
+ # Sets a charset (character set) as default fallback for all the requests
184
+ # without a strict requirement for the charset.
185
+ #
186
+ # By default, this value is nil.
187
+ #
188
+ # @return [String]
189
+ #
190
+ # @see Hanami::Action::Mime
191
+ #
192
+ # @since 0.3.0
193
+
194
+ # @!attribute [rw] default_headers
195
+ #
196
+ # Sets default headers for all responses.
197
+ #
198
+ # By default, this is an empty hash.
199
+ #
200
+ # @return [Hash{String=>String}] the headers
201
+ #
202
+ # @example
203
+ # config.default_headers = {"X-Frame-Options" => "DENY"}
204
+ #
205
+ # @see default_headers
206
+ #
207
+ # @since 0.4.0
208
+
209
+ # @!attribute [rw] cookies
210
+ #
211
+ # Sets default cookie options for all responses.
212
+ #
213
+ # By default this, is an empty hash.
214
+ #
215
+ # @return [Hash{Symbol=>String}] the cookie options
216
+ #
217
+ # @example
218
+ # config.cookies = {
219
+ # domain: "hanamirb.org",
220
+ # path: "/controller",
221
+ # secure: true,
222
+ # httponly: true
223
+ # }
224
+ #
225
+ # @since 0.4.0
226
+
227
+ # @!attribute [rw] root_directory
228
+ #
229
+ # Sets the the for the public directory, which is used for file downloads.
230
+ # This must be an existent directory.
231
+ #
232
+ # Defaults to the current working directory.
233
+ #
234
+ # @return [String] the directory path
235
+ #
236
+ # @api private
237
+ #
238
+ # @since 1.0.0
239
+
240
+ # @!attribute [rw] public_directory
241
+ #
242
+ # Sets the path to public directory. This directory is used for file downloads.
243
+ #
244
+ # This given directory will be appended onto the root directory.
245
+ #
246
+ # By default, the public directory is `"public"`.
247
+ # @return [String] the public directory path
248
+ #
249
+ # @example
250
+ # config.public_directory = "public"
251
+ # config.public_directory # => "/path/to/root/public"
252
+ #
253
+ # @see root_directory
254
+ #
255
+ # @since 2.0.0
256
+ def public_directory
257
+ # This must be a string, for Rack compatibility
258
+ root_directory.join(super).to_s
259
+ end
260
+ end
261
+ end
262
+ end
@@ -114,13 +114,13 @@ module Hanami
114
114
  #
115
115
  # @since 0.1.0
116
116
  # @api private
117
- HTTP_ACCEPT = "HTTP_ACCEPT"
117
+ HTTP_ACCEPT = "HTTP_ACCEPT"
118
118
 
119
119
  # The header key to set the mime type of the response
120
120
  #
121
121
  # @since 0.1.0
122
122
  # @api private
123
- CONTENT_TYPE = ::Rack::CONTENT_TYPE
123
+ CONTENT_TYPE = ::Rack::CONTENT_TYPE
124
124
 
125
125
  # The default mime type for an incoming HTTP request
126
126
  #
@@ -152,7 +152,7 @@ module Hanami
152
152
  #
153
153
  # @since 2.0.0
154
154
  # @api private
155
- ETAG = ::Rack::ETAG
155
+ ETAG = ::Rack::ETAG
156
156
 
157
157
  # @since 2.0.0
158
158
  # @api private
@@ -221,7 +221,7 @@ module Hanami
221
221
  #
222
222
  # @since 2.0.0
223
223
  # @api private
224
- COOKIE_HASH_KEY = ::Rack::RACK_REQUEST_COOKIE_HASH
224
+ COOKIE_HASH_KEY = ::Rack::RACK_REQUEST_COOKIE_HASH
225
225
 
226
226
  # The key used by Rack to set the cookies as a String in the env
227
227
  #
@@ -233,7 +233,7 @@ module Hanami
233
233
  #
234
234
  # @since 2.0.0
235
235
  # @api private
236
- RACK_INPUT = ::Rack::RACK_INPUT
236
+ RACK_INPUT = ::Rack::RACK_INPUT
237
237
 
238
238
  # The key that returns router params from the Rack env
239
239
  # This is a builtin integration for Hanami::Router
@@ -250,12 +250,6 @@ module Hanami
250
250
 
251
251
  # @since 2.0.0
252
252
  # @api private
253
- DEFAULT_CHARSET = "utf-8"
254
-
255
- # The key that returns content mime type from the Rack env
256
- #
257
- # @since 2.0.0
258
- # @api private
259
- HTTP_CONTENT_TYPE = "CONTENT_TYPE"
253
+ DEFAULT_CHARSET = "utf-8"
260
254
  end
261
255
  end
@@ -96,6 +96,7 @@ module Hanami
96
96
  # @api private
97
97
  def self.included(action)
98
98
  unless Hanami.respond_to?(:env?) && Hanami.env?(:test)
99
+ action.include Hanami::Action::Session
99
100
  action.class_eval do
100
101
  before :set_csrf_token, :verify_csrf_token
101
102
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ class Action
5
+ # @since 2.0.0
6
+ class Error < ::StandardError
7
+ end
8
+
9
+ # Missing session error
10
+ #
11
+ # This error is raised when `session` or `flash` is accessed/set on request/response objects
12
+ # in actions which do not include `Hanami::Action::Session`.
13
+ #
14
+ # @since 2.0.0
15
+ #
16
+ # @see Hanami::Action::Session
17
+ # @see Hanami::Action::Request#session
18
+ # @see Hanami::Action::Response#session
19
+ # @see Hanami::Action::Response#flash
20
+ class MissingSessionError < Error
21
+ def initialize(session_method)
22
+ super(<<~TEXT)
23
+ Sessions are not enabled. To use `#{session_method}`:
24
+
25
+ Configure sessions in your Hanami app, e.g.
26
+
27
+ module MyApp
28
+ class App < Hanami::App
29
+ # See Rack::Session::Cookie for options
30
+ config.sessions = :cookie, {**cookie_session_options}
31
+ end
32
+ end
33
+
34
+ Or include session support directly in your action class:
35
+
36
+ include Hanami::Action::Session
37
+ TEXT
38
+ end
39
+ end
40
+ end
41
+ end
@@ -84,13 +84,13 @@ module Hanami
84
84
  #
85
85
  # @since 2.0.0
86
86
  # @api private
87
- def self.content_type(configuration, request, accepted_mime_types)
87
+ def self.content_type(config, request, accepted_mime_types)
88
88
  if request.accept_header?
89
89
  type = best_q_match(request.accept, accepted_mime_types)
90
90
  return type if type
91
91
  end
92
92
 
93
- default_response_type(configuration) || default_content_type(configuration) || Action::DEFAULT_CONTENT_TYPE
93
+ default_response_type(config) || default_content_type(config) || Action::DEFAULT_CONTENT_TYPE
94
94
  end
95
95
 
96
96
  # @since 2.0.0
@@ -101,22 +101,22 @@ module Hanami
101
101
 
102
102
  # @since 2.0.0
103
103
  # @api private
104
- def self.default_response_type(configuration)
105
- format_to_mime_type(configuration.default_response_format, configuration)
104
+ def self.default_response_type(config)
105
+ format_to_mime_type(config.default_response_format, config)
106
106
  end
107
107
 
108
108
  # @since 2.0.0
109
109
  # @api private
110
- def self.default_content_type(configuration)
111
- format_to_mime_type(configuration.default_request_format, configuration)
110
+ def self.default_content_type(config)
111
+ format_to_mime_type(config.default_request_format, config)
112
112
  end
113
113
 
114
114
  # @since 2.0.0
115
115
  # @api private
116
- def self.format_to_mime_type(format, configuration)
116
+ def self.format_to_mime_type(format, config)
117
117
  return if format.nil?
118
118
 
119
- configuration.mime_type_for(format) ||
119
+ config.mime_type_for(format) ||
120
120
  TYPES.fetch(format) { raise Hanami::Controller::UnknownFormatError.new(format) }
121
121
  end
122
122
 
@@ -125,17 +125,17 @@ module Hanami
125
125
  #
126
126
  # @see Hanami::Action::Mime#finish
127
127
  # @example
128
- # detect_format("text/html; charset=utf-8", configuration) #=> :html
128
+ # detect_format("text/html; charset=utf-8", config) #=> :html
129
129
  #
130
130
  # @return [Symbol, nil]
131
131
  #
132
132
  # @since 2.0.0
133
133
  # @api private
134
- def self.detect_format(content_type, configuration)
134
+ def self.detect_format(content_type, config)
135
135
  return if content_type.nil?
136
136
 
137
137
  ct = content_type.split(";").first
138
- configuration.format_for(ct) || format_for(ct)
138
+ config.format_for(ct) || format_for(ct)
139
139
  end
140
140
 
141
141
  # @since 2.0.0
@@ -146,7 +146,7 @@ module Hanami
146
146
 
147
147
  # Transforms symbols to MIME Types
148
148
  # @example
149
- # restrict_mime_types(configuration, [:json]) #=> ["application/json"]
149
+ # restrict_mime_types(config, [:json]) #=> ["application/json"]
150
150
  #
151
151
  # @return [Array<String>, nil]
152
152
  #
@@ -154,35 +154,71 @@ module Hanami
154
154
  #
155
155
  # @since 2.0.0
156
156
  # @api private
157
- def self.restrict_mime_types(configuration, accepted_formats)
158
- return if accepted_formats.empty?
157
+ def self.restrict_mime_types(config)
158
+ return if config.accepted_formats.empty?
159
159
 
160
- mime_types = accepted_formats.map do |format|
161
- format_to_mime_type(format, configuration)
160
+ mime_types = config.accepted_formats.map do |format|
161
+ format_to_mime_type(format, config)
162
162
  end
163
163
 
164
- accepted_mime_types = mime_types & configuration.mime_types
164
+ accepted_mime_types = mime_types & config.mime_types
165
165
 
166
166
  return if accepted_mime_types.empty?
167
167
 
168
168
  accepted_mime_types
169
169
  end
170
170
 
171
- # Use for checking the Content-Type header to make sure is valid based
172
- # on the accepted_mime_types
171
+ # Yields if an action is configured with `accepted_formats`, the request has an `Accept`
172
+ # header, and none of the Accept types matches the accepted formats. The given block is
173
+ # expected to halt the request handling.
173
174
  #
174
- # If no Content-Type is sent in the request it will check the default_request_format
175
+ # If any of these conditions are not met, then the request is acceptable and the method
176
+ # returns without yielding.
175
177
  #
176
- # @return [TrueClass, FalseClass]
178
+ # @see Action#enforce_accepted_mime_types
179
+ # @see Action.accept
180
+ # @see Config#accepted_formats
177
181
  #
178
182
  # @since 2.0.0
179
183
  # @api private
180
- def self.accepted_mime_type?(request, accepted_mime_types, configuration)
181
- mime_type = request.env[Action::HTTP_CONTENT_TYPE] ||
182
- default_content_type(configuration) ||
183
- Action::DEFAULT_CONTENT_TYPE
184
+ def self.enforce_accept(request, config)
185
+ return unless request.accept_header?
184
186
 
185
- !accepted_mime_types.find { |mt| ::Rack::Mime.match?(mt, mime_type) }.nil?
187
+ accept_types = ::Rack::Utils.q_values(request.accept).map(&:first)
188
+ return if accept_types.any? { |mime_type| accepted_mime_type?(mime_type, config) }
189
+
190
+ yield
191
+ end
192
+
193
+ # Yields if an action is configured with `accepted_formats`, the request has a `Content-Type`
194
+ # header (or a `default_requst_format` is configured), and the content type does not match the
195
+ # accepted formats. The given block is expected to halt the request handling.
196
+ #
197
+ # If any of these conditions are not met, then the request is acceptable and the method
198
+ # returns without yielding.
199
+ #
200
+ # @see Action#enforce_accepted_mime_types
201
+ # @see Action.accept
202
+ # @see Config#accepted_formats
203
+ #
204
+ # @since 2.0.0
205
+ # @api private
206
+ def self.enforce_content_type(request, config)
207
+ content_type = request.content_type || default_content_type(config)
208
+
209
+ return if content_type.nil?
210
+
211
+ return if accepted_mime_type?(content_type, config)
212
+
213
+ yield
214
+ end
215
+
216
+ # @since 2.0.0
217
+ # @api private
218
+ def self.accepted_mime_type?(mime_type, config)
219
+ config.accepted_mime_types.any? { |accepted_mime_type|
220
+ ::Rack::Mime.match?(accepted_mime_type, mime_type)
221
+ }
186
222
  end
187
223
 
188
224
  # Use for setting the content_type and charset if the response
@@ -193,14 +229,12 @@ module Hanami
193
229
  #
194
230
  # @since 2.0.0
195
231
  # @api private
196
- def self.calculate_content_type_with_charset(configuration, request, accepted_mime_types)
197
- charset = self.charset(configuration.default_charset)
198
- content_type = self.content_type(configuration, request, accepted_mime_types)
232
+ def self.calculate_content_type_with_charset(config, request, accepted_mime_types)
233
+ charset = self.charset(config.default_charset)
234
+ content_type = self.content_type(config, request, accepted_mime_types)
199
235
  content_type_with_charset(content_type, charset)
200
236
  end
201
237
 
202
- # private
203
-
204
238
  # Patched version of <tt>Rack::Utils.best_q_match</tt>.
205
239
  #
206
240
  # @since 2.0.0
@@ -25,7 +25,7 @@ module Hanami
25
25
  # @since 1.1.0
26
26
  # @api private
27
27
  def initialize(errors = {})
28
- super(errors)
28
+ super(errors.dup)
29
29
  end
30
30
 
31
31
  # Add an error to the param validations
@@ -160,9 +160,9 @@ module Hanami
160
160
  def initialize(env)
161
161
  @env = env
162
162
  super(_extract_params)
163
- @result = validate
164
- @params = _params
165
- @errors = Errors.new(@result.messages)
163
+ validation = validate
164
+ @params = validation.to_h
165
+ @errors = Errors.new(validation.messages)
166
166
  freeze
167
167
  end
168
168
 
@@ -235,7 +235,7 @@ module Hanami
235
235
  errors.empty?
236
236
  end
237
237
 
238
- # Serialize params to Hash
238
+ # Serialize validated params to Hash
239
239
  #
240
240
  # @return [::Hash]
241
241
  #
@@ -244,13 +244,6 @@ module Hanami
244
244
  @params
245
245
  end
246
246
  alias_method :to_hash, :to_h
247
-
248
- private
249
-
250
- # @api private
251
- def _params
252
- _router_params.merge(@result.output)
253
- end
254
247
  end
255
248
  end
256
249
  end
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack/utils"
3
+ require "hanami/action/flash"
4
4
  require "rack/mime"
5
5
  require "rack/request"
6
+ require "rack/utils"
6
7
  require "securerandom"
7
8
 
8
9
  module Hanami
@@ -16,9 +17,11 @@ module Hanami
16
17
  class Request < ::Rack::Request
17
18
  attr_reader :params
18
19
 
19
- def initialize(env, params)
20
+ def initialize(env:, params:, sessions_enabled: false)
20
21
  super(env)
22
+
21
23
  @params = params
24
+ @sessions_enabled = sessions_enabled
22
25
  end
23
26
 
24
27
  def id
@@ -26,6 +29,22 @@ module Hanami
26
29
  @id ||= @env[Action::REQUEST_ID] = SecureRandom.hex(Action::DEFAULT_ID_LENGTH)
27
30
  end
28
31
 
32
+ def session
33
+ unless @sessions_enabled
34
+ raise Hanami::Action::MissingSessionError.new("Hanami::Action::Request#session")
35
+ end
36
+
37
+ super
38
+ end
39
+
40
+ def flash
41
+ unless @sessions_enabled
42
+ raise Hanami::Action::MissingSessionError.new("Hanami::Action::Request#flash")
43
+ end
44
+
45
+ @flash ||= Flash.new(session[Flash::KEY])
46
+ end
47
+
29
48
  def accept?(mime_type)
30
49
  !!::Rack::Utils.q_values(accept).find do |mime, _|
31
50
  ::Rack::Mime.match?(mime_type, mime)